Monday, July 4, 2011

Version 1.3 of the firmware is up!

Trackuino logoVersion 1.3 of of the Trackuino firmware is up in the downloads area! There are lots of improvements and fixes. Here is a summary:

Improved GPS compatibility

The previous GPS code made too many risky assumptions about the NMEA format, so it failed miserably on certain versions of the Venus 634FLPx (which, ironically, was the only supported GPS). Now the parsing routines are more tolerant to variations in the numeric formats, so compatibility with other units should be greater.

Signal quality (enter real-time computing)

In Packet Radio, the maximum bandwidth used by the AFSK carrier is 2,200 Hz. Nyquist tells us that we need at least 4,400 samples per second to properly rebuild the signal at the receiver, but that requires an ideal low-pass filter with a cutoff frequency of 2,200 Hz, which is neither physically achievable nor realistic in FM radios where the cutoff is usually higher, around 3,000 Hz. For those two reasons, oversampling is usually desirable (even if not mathematically necessary) if we want to avoid annoying harmonics that distort the signal and waste our scarce power.

The Trackuino firmware uses a 62,500 KHz sample rate, which is well away from the audible spectrum. Oversampling improves the quality of the signal at the receiver, but it requires pushing samples at a rate of 1 sample every 256 clock cycles (16 MHz clock rate / 256 = 62.5 KHz), so it's really taxing on the CPU. To make things worse, the Arduino is also busy doing a lot of equally important things, such as staying up to date with the GPS, keeping track of time so that millis() and delay() keep working as expected, and handling the buzzer (more on the buzzer later). All these tasks are interrupt-driven, which means they happen asynchronously without regard of one another or the normal program flow. The interrupts can be queued, but only one of each kind at a time. If an interrupt comes without having served the previous one, the second interrupt will be lost. So, not only has our modulator a meager 256 cycles to do its job, but it also has to struggle over the CPU with other interrupts...   When some task needs to be done under a deadline constraint such as this, it's called a real-time system. Efforts to ensure real-time operation are geared towards three goals:

  1. Priorizing the real-time task over not-so-important tasks. In the AVR architecture, interrupt priorities are fixed, so we can't do that.
  2. Keeping our real-time task short, so that it can meet its deadline.
  3. Keeping non-important tasks short, so that they don't hog the CPU while the important task is waiting.

There are a number of improvements in version 1.3 on those regards:

  • Serial (GPS) interrupt: The selfish implementation of this interrupt in the Arduino library spent an abusive 330 cycles on every received byte. It means that, for every received character from the GPS, chances are that one modem interrupt is lost altogether. Arduino version 22 has an optimization that cuts this time down to 80 cycles, so the code now enforces compilation under Arduino version 22 or later.
  • Modem interrupt: Some optimizations have been done to shave off a few cycles here and there. The modem interrupt should take 150 cycles max, average is less than that.
  • Debug information: By uncommenting a flag in config.h you can get useful information on overrun conditions.

Previous versions of had interrupt overrun issues, which translated into small variations from the nominal baud rate (1200 bps). Luckily receivers have some tolerance to baud rate deviations, so it's not that big a deal. In any case, the new version should improve the rate of successfully decoded packets.

SRAM usage optimization

The Atmega328P has 2 KB of SRAM, of which about 512 bytes were taken up by a huge sine table in modem.cpp. This table has been moved to the much more spacious program memory (32 KB). There are a few other candidates that scream to be moved to PROGMEM too, such as the APRS comment, callsigns and such, but these haven't been moved (yet).

Buzzer support

This should help in acoustically locating the payload. The buzzer is handled by timer 1 using PWM. Piezoelectric buzzers demand very little current, so they can be driven directly with pins 9 or 10. The tone frequency can be configured in config.h as well as the duty cycle in seconds. It doesn't make sense to have a payload beeping at 100,000 ft. since there is no air to begin with, so the maximum operating altitude can also be programmed.

CPU usage LED indication

This might look just cosmetic or geeky, but it's actually quite useful. The idea is that the LED will glow increasingly brighter as the CPU gets busier. For example:

  • If the AVR is idle, the light will be very dim. Tthe AVR is never really idle for long periods of time, since timers 0 (millis) and 1 (buzzer) are going off periodically.
  • When receiving data on the serial port, the LED will turn slightly brighter because it takes extra CPU to process the incoming bytes. This is a way of checking for GPS activity at a glance.
  • APRS transmission takes the most CPU, so the LED will be almost fully bright.


  1. Hi Javi,

    Recently I moved to a 7.3Mhz board.
    I see that the playback rate on modem.cpp is
    F_CPU / 256 = 62.5KHz for F_CPU = 16MHz.

    My F_CPU is 7.3Mhz. Do I need to make any changes?
    However, as far as now everything works normal even with latest 1.3 code.

    Thanks, Kostas.

  2. As long as your F_CPU is set correctly, It will be okay. It's the sampling frequency that gets scaled down with the CPU clock so that the interrupt rate stays constant at 256 clock cycles per interrupt. In your case the playback rate would be ~28.5 KHz instead of 62.5 KHz.

    I assume you have your arduino "boards.txt" file modified for your particular clock rate, right?

  3. Yes, of course. Everything works just fine here with latest code on atmega644p at 7.3mhz.

  4. Hi Javi,

    some correction/fixes maybe? :)

    In ax25_send_footer()
    you declare: 'unsigned short int final_crc'
    What means the short int ? Is is correct?

    In send_byte()
    you declare: 'int i'
    and you use it: 'for (i = 0; i < 8; i++)'
    i is gonna be up to 8. Why declare it as an integer and not just as byte?

  5. Not sure if you've seen it, but there is a very tight and efficient GPS library for Ardiuno, just good TinyGPS, we use it in a multi rotor project as do most... it's non-blocking, doesn't need an interrupt and support a variety of gps (if you use the arducopter version - google that to find their code and library).

    I'll be utilizing your approach to aprs on a live multirotor project. I just finished a quick layout to support that and an external gps.

    Here's a 3D plot of a stick version with radiometrix that will go on the bottom... all built in with a 328p onboard

  6. 1. Yes, it is correct ("short int" = "short")

    2. So far I've focused in optimizing code where contention for CPU was already happening, ie. the interrupt service routines. There are lots of cases like the one you pointed out where a few byes can be saved, but I haven't tackled them because they haven't become a problem yet.

    That said, I agree with your proposal, so I submitted it to svn. I had to replace the "for" with a "while" because the compiler insisted in promoting 'i' to a 16-bits int otherwise...

  7. @aadamson: That APRS stick looks fabulous, are you making it public anywhere?

    The GPS code I wrote is somewhat APRS oriented, ie. it doesn't parse what it doesn't need, etc. But I might move to TinyGPS since the benefits of a community tested library probably outweigh any possible optimization.

  8. I think you'll find that the tinyGPS can be easily modified. It only looks at 2 string types right now with nmea, but easily good look at others, it support both ascii and binary protocols, I use both a mediaTek 3329 (10hz update rate) in binary or nmea, and I have a gs407 around there somewhere as well, which has it's own binary or nmea protocol (the binary support has been implemented in the arducopters guys version of the tinygps library).

    On the stick, I'm waiting on the prototype boards to come back and me to build one and prove it works... it's basically just a pro mini schematic glues together with the radiometrix part. I've even figured out how to add an 8W amplifier that I may do in another version. Anyway, once I have it proved, yes, it will be an open hardware design with eagle files etc.

    It's just *too* easy to build these 328ps in SMD... toaster oven from walmart, kester solder paste and 0603 parts and you are good to go... Takes about 10 mins or so to do the solder paste and pop them in the oven... :)

    1. hey! i want to use the mt3329 in NMEA mode; could you send me the syntax for making it run in the NMEA mode.

  9. Hi,

    Any hints on using a UniTraQ GT-320? It is the GPS I have and I'm getting no position information in the APRS packet. Any help appreciated...launch in less than 3 weeks.

    Mike KD0MEQ
    ROBOMO-2 launch team

  10. @WinfieldMike: Try setting GPS_BADURATE to 4800 in "config.h". If that still doesn't work, send me a sample of the GPS output after it's gotten a valid fix.

  11. GPS baudrate is set to 4800.

    Here is output of the GPS:


  12. Javi, I have some kind of GPS error....looking at the sentences it is putting me in the southern hemisphere...I am in Missouri. So I obviously have some kind of GPS error...I'll check it out.


  13. Javi, some more comments for your code.

    An 'unsigned char' is a 'byte' according to arduino. Maybe you want to replace that in your code to make it look much simpler.

    Also, on arduino, 'short' is the same as 'int'
    It's a signed 16 bit quantity, so capable of representing the range -32 768 to +32 767.

    Moreover, talking about the 'for' to 'while' change. Your parameter 'unsigned char byte' uses the 'byte' which is a deserved word for arduino. Change this to something like that and it will compile:

    send_byte(byte abyte)
    for (byte i = 0; i < 8; i++)
    update_crc((abyte >> i) & 1);
    if ((abyte >> i) & 1)

    That's all :)

  14. @WinfieldMike: Yea... that doesn't look like a valid fix. If you look at the GPRMC sentence, the 'V' after the second comma, that's the fix status: (A)ctive or (V)oid. The '0' after the longitude in GPGGA also means an invalid position. Your lat/long are probably random until you get a fix. On my GPS, for example, they point in the middle of Taiwan :)

    @m1x10: The "elegant" solution is to use stdint.h types, that way type sizes stay portable across platforms (there are plans for a Chipkit UNO32 version). Agreed on the reserved words, I'm going to fix that. Thank you.

  15. @m1x10 (continued): type optimization is not always that simple, because what works on one platform may be sub-optimal on others. Here is an interesting example: 'int' is encouraged by the C standard to be the size of a machine word, so that operations with ints can be done with 1 single register/instruction. However, the C standard also says that an int can never be smaller than 16 bits, so the first rule doesn't hold in 8-bit AVRs. Because of that, you end up with twice as much assembler lines for every operation with an int. If you hand-optimize that by downgrading ints to chars, then you make 32 and 16-bit platforms unhappy because it takes them extra work to handle sub-word sizes.

  16. Hey Javi,

    Can you recommend a GPS for the Trackuino that I can get online? Sparkfun or similar store. Preferably all-in-one ready to go with built in antenna.

    I am going to abandon the GPS I have and might as well get one that works with the Trackuino 100%.

    It will be for high altitude ballooning and needs to read above 65,000'.

    We are going to show your Trackuino at this year's Great Plains Super Launch.


  17. Javi,

    Payload is built and sending packets via my APRS-IS iGate to Track callsign KD0MEQ-11. Used the GPS you recommended, works like a champ. The flight is a go for July 30th, Great Plains Super Launch (approx 15 balloons). How can I send you a picture of our payload?


  18. Mike: upload it to and post the link here.

  19. Javi,

    Launching Saturday morning. Go to and look for the link to the map page. Launches start at 8:10am Saturday, Mountain time. Our balloon is flying the Trackuino and the Venus GPS you suggested. Callsign KD0MEQ-11.


  20. Good luck!! We just had a failed attempt, the balloon neck broke just as we were ready to let go :(

  21. Good news/bad news....

    Our flight made it to 88,095', but unfortunately our RadioMetrix quit just after launch. We had a backup tracker running callsign KD4STH-7. The backup tracker had problems, but worked well enough. The Trackuino fimware worked flawlessly until the RadioMetrix quit. Oh well.

    One suggestion for an additional feature: the hosts for the launch like to timeslot the APRS packets. We were assigned the 52 second timeslot. Could you add timeslotting to a future update? With mass launches it is important. Thanks for making the Trackuino firmware!

    Sorry to hear about the neck breaking. That is a real bummer.


  22. Mike: I'm sorry to hear that the main tracker didn't work as expected. Do you have any ideas what went wrong? I find it more likely to be a firmware or electric flaw rather than the radiometrix quitting spontaneously. And if there is a chance of firmware failure, I'd like to fix it.

  23. You can see some pictures and a video from the flight at


  24. trackuino mini stick is up and functional.

    Note this is just a tracker in this version, no support for sensors, etc.

    I used an LM1117 VR that regulates 5v (max input is 15v), this provides 800ma of current. Design is basically an arduino pro mini, with HX1, and a gps interface using the standard GS406 connector.

    You'll see the stick format in the pictures below. The power comes in, is diode protected and in my test case comes from a 7.4v lipo battery that I just happened to have laying around. GPS is a 10hz MediaTek in NMEA mode at 38400 default.

    I'd like to propose that the code be made such that the sensor packages can be defined out. I did this and it was rather easy but would make for an easy build option.

    SMA connector for antenna, and in my testing, I just used a HT SMA antenna, approx 6" long but tuned for 2mtrs.

    This design could be enhanced and I may do that to include a breakout header for additional pins and access to 5V and ground. Would be rather easy to also add power to that header such that it could be daughter board connected to a mother board if so desired.

    Here are the pictures... I plan to use this on a UAV and so I wanted something really small. I also changed the software to support the second path, and changed the balloon identifier to a helicopter identifier :)..

    I think that all total, without the gps or radio module, these would cost approx $7 +/-

    Anyway, just wanted to share, it's alive... I'll put the eagle files up on my github sometime this week as I still want to make a couple of quick changes first.

  25. @aadamson: amazing job! thanks for sharing! I'll be happy to post the link to your github as soon as you have it online.

  26. Javi,

    here is my git with all the files...


  27. Hi Javi,

    It's me m1x10. Just using another nick :)
    I got some questions considering SSTV.
    There is a new camera/sstv module arround:

    Is it possible to use it with your AFSK modulator over NBFM at 433MHz? ( Propably using a radiometrix 433MHz transmitter )

    If it's possible then we need a AFSK demodulator on the receiver side ! Is it possible to provide us your magic AFSK demodulator code?

  28. I found a guy who made an avr afsk demodulator kit. He provides source assembly code.
    On paragraph:
    TriTone Modem - a digital solution (AT90S2313)

  29. Hi Javi, I’m trying to get a trackuino working for a balloon project and I’m having some issues that I hope you can help with. The radio is an FRS converted for ham use (440MHz). I am using the trackuino to send temperature data, but not position (the package will be lost). Everything seems to be working fine, but my data transmission is unreliable. Even with a very clean signal, I get about 10-20% packet loss. My ground station is a Yaesu VX-3R and AGWPE sound card modem. I am able to reliably decode local APRS packets with this setup. I have also tried a TinyTrak with the same FRS radio, and it decodes reliably. So I think it’s something to do with the trackuino, but I’m not sure what. I’ve looked at the signals using AGWPE’s soundcard setup utilities, and it looks very clean. So I think that maybe the timing could be slightly off or something. Do you ever experience this sort of problem, where everything looks fine, but you don’t get 100% data transmission? Could it be an overrun problem? Thanks for sharing this excellent code! Saludos desde Nuevo Mexico, USA!

  30. The trackuino outputs 5v peak-to-peak. Make sure the FRS takes an input within that range or you'll have to downshift it through a divider. Try to capture the received signal with Audacity (or a scope, even better) and look for clues like distortion or clipping.

    You can check for overruns by uncommenting the DEBUG_MODEM define in config.h and looking at the serial console for a "MODEM OVERRUNS" message. But as far as my tests go, the last version (1.31) is free of timing issues.

    I also use AGWPE and given the right conditions (good LOS, etc.) I get 100% decode rate with the HX1.

  31. @NATO: oh, I forgot another option: crank TXDELAY up in config.h. It might just be taking to long to go full RF.

  32. Sounds good. I started messing around with audacity last night. I thought it might be interesting to look at a pure sine wave. In your modem.cpp file, I found the following lines:

    if ((current_byte & 1) == 0) {
    // Toggle tone (1200 <> 2200)
    phase_delta ^= (PHASE_DELTA_1200 ^ PHASE_DELTA_2200);

    If I commented those out, would I get just a pure sine wave? I'll start by looking at the output of the trackuino directly (audacity, and scope), then work from there. I also want to look at the low-pass filter and see if it needs to be optimized for my specific radio. Thanks.

  33. So, I think I got it worked out. My packets seem to decode much better now, thanks to the info on this page:

    I changed the cutoff for the low pass filter to a much higher frequency, and added a tuning pot in almost the exact same configuration as the tinytrak. This allows tuning of the TX level, so that clipping of the high frequencies and subsequent emphasis of the low frequencies can be nearly eliminated. Before, my 1200 Hz amplitude was almost double the 2200 Hz. According to the referenced page, that's really bad news.

    Anyway, it seems to help tremendously in my case. I hope it helps someone else out as well. Here's what I used:

    330 220k
    pin o----/\/\---o----/\/\--o
    | \ 10uF
    --- 10k /<--||--o MIC
    0.1u --- \
    | |
    V V

    (Sorry, it looks good when I write it, but may get jumbled-up in the final posting)

    Monitor the audio of the receiving radio either on a scope or in AGWPE. Adjust the pot so that that low frequencies are about the same as the high. This usually results in a weaker audio signal, so you may have to crank up the volume a bit. In my case I couldn't get it exact, but I did get it within about 10%.

  34. Olá, posso usar a Trackuino escudo em caros e monitorar sua localização em tempo real em um mapa?
    Os dados do gps são enviados por radio frequencia?
    Que equipamento eu preciso ter na central?
    Já temos uma frequencia licenciada pela anatel.

    Att.: Almir de Souza
    Cidade: Maceió-Al - Brasil

  35. Almir: you can use You need a radio amateur license. As long as you transmit in the APRS frequency, your local digipeaters will pick up the signal and your position will show up there.

    However, beware that lucrative operation of a radio amateur license is usually prohibited...

  36. Javi:
    Do you still support the firmware? I'd like to transmit the raw NMEA sentence from the GPS instead of converting it to APRS "/" format. Is there a string or char array that contains the entire sentence when it is received, or do I have to reconstruct it from the tokens in gps.cpp?

  37. MatthewBruehl: tokens are discarded after decoding them to save memory, so you'd need to store the sentences in memory yourself. You can't rebuild the same exact NMEA sentence from the tokens because some of them are irrelevant to APRS and they don't even get decoded.