Last winter (2010/2011) I finally got around to writing code that drives the Microchip MRF24J40MB transceiver module on the PCB for my GPS-guided rocket recovery project.
The transceiver module is a very nice little part – it’s an IEEE 802.15.4 (ZigBee) radio that uses the 2.4 GHz ISM band – the same band that WiFi and microwave ovens use. The “-MB” is Microchip’s long-range module – it has the radio, antenna, power amplifier (PA), and low noise amplifier (LNA) all on a surface-mountable board a bit bigger than a postage stamp. It comes pre-approved by the FCC for unlicensed use. And the price is reasonable.
Here’s a pic showing the size:
It outputs 100 mW (20 dBm) and has a receive sensitivity of -102 dBm, so the range is quite good; well over 2500 meters outdoors (line of sight).
Microchip also offers a “-MA” module that’s both smaller and cheaper, but it only outputs 1 mW and doesn’t have the LNA, so it has less range.
It talks to the MCU with a simple 4-wire serial SPI interface; internally it has a lot of registers – you send it SPI commands to read and write the registers.
I’d already included an interface to the module on my PCB layout (posted elsewhere on this site – see here), so now I had to write some code that would talk to the module.
My goal was to get real-time telemetry from the rocket in flight, as well as to be able to send the rocket commands from the ground. I was already logging flight data to the flash memory in the PIC32, but if the rocket isn’t found I can’t get that data out of the PIC. So at a minimum, I wanted to get GPS fixes in flight, so if the rocket is lost I know where to go looking for it.
As well, the ability to send commands to the rocket is useful. With the telemetry system I can arm/disarm the ejection charges from a safe distance, as well as manually force ejection, switch modes, and power down the PCB (to save battery power after a scrub).
Software
IEEE 802.15.4 is the basis of ZigBee, a rather complicated protocol for smart gadgets and appliances to talk to each other wirelessly. Microchip offers source code for the entire ZigBee stack, but I didn’t need anything like that kind of complexity for this application.
Microchip also offers source code for something they call “MiWi P2P”, which is a vastly simplified protocol that implements a small subset of the ZigBee functions (it’s not compatible with ZigBee).
I had a look at the datasheet for the MRF24J40 transceiver chip – it’s 156 intimidating pages long. So I decided to use Microchip’s MiWi P2P source code, thinking that would be an easier solution than figuring out the datasheet and writing my own code.
That was a big mistake, at least for me. The MiWi P2P code has lots of functionality that I didn’t need – it will route messages around a network of peer devices, do store-and-forward for sleeping radios, etc. All I needed was simple point-to-point transmit and receive. So I started to modify the code to simplify things – all that functionality was making the interface to the rest of the system needlessly complicated. In the process of doing that, I found lots and lots of problems and bugs in the MiWi code. I ended up spending several weeks porting and debugging it; by the end I’d rewritten 98% of it (and thrown away a good 85% as useless overhead for my application). I used version 3.1.3, which is no longer the latest, but I don’t think the newer versions are much better.
The MiWi P2P code does work if you treat it as a “black box” and don’t attempt to do anything other than the few example things documented by Microchip. The bugs either aren’t exercised or are worked around for those cases. But if you want to customize or optimize anything at all you quickly run into trouble. It’s a bloated, buggy, poorly written and poorly documented piece of code. (It is well-architected. And it is free.)
In the process of rewriting the MiWi code, I learned how the MRF24J40 hardware works, and how to code to it myself. It turns out that “intimidating” 156 page datasheet isn’t nearly as bad as it first seems – from a software point of view, you only need to read sections 3.2 (Initialization), 3.11 (Reception), and 3.12 (Transmission) – that tells you everything you need to know in less than 10 pages! (It helps to skim over the IEEE 802.15.4 document first to know what to expect – but you don’t have to implement all of it to get simple data transfer working.)
If you’re going to do this yourself my advice is to ignore Microchip’s source code and write directly to the MRF24J40 hardware per the datasheet, unless you really need the whole ZigBee stack. Follow the datasheet’s initialization instructions exactly. Do not even look at Microchip’s application notes – they’ll only add confusion.
If you do that, you can have it running with 2 or 3 days of work (instead of the 2 or 3 weeks I spent). The hardware is excellent and it works like the datasheet says.
[Update January 2012: I’ve posted the source code; see: https://nerdfever.com/?p=1797]
Once I had basic radio I/O working, I implemented a set of commands the rocket would respond to – they’re all simple ASCII strings (think a command-line interface). Here is the list as of this writing:
STATE NAMES (forces rocket software into commanded state)
NONe
SAFe
ARMed
TLAunch (timed launch – used for ground testing)
ASCent
DEScent
GROund
TESt
NOUN ALONE
DUMp (dumps log as hex)
RESET-LOG *
RESET-NAV *
RESET-ALL *
POWER-DOWN *
* must type whole command for these
PARAMETER COMMANDS (noun verb)
(no parameter = report current status)
CHAnnel n (set radio channel)
BUZzer on/off
BEEp on/off
LOWbuz on/off
GPS_enable on/off (GPS power)
DROgue on/off (drogue output)
MAIn on/off (main parachute deployment output)
ST2 on/off (“stage 2” output)
ST3 on/off (“stage 3″output)
RED on/off (LED)
YELLow on/off (LED)
GREen on/off (LED)
VSWbatt_pwr on/off (switched battery power circuit)
VSErvo_pwr on/off (servo power)
STEer / S 0 to 255 (both servos same)
S1 0 to 255 (servo #1)
S2 0 to 255 (servo #2)
UPDates on/off (transmit updates every 40 mS)
LTHreshold meters (launch detect threshold)
PARAMETERS
<integer> value
ON 1
OFF 0
TOGgle !<oldValue>
I only have to send the capitalized characters (usually the first 3); the rest are ‘syntactic sugar’ to make it easier for me to remember them.
In addition to responding to the commands, the rocket will also send status information each time the software changes state (for example, when launch is detected and the rocket goes from ARMED to ASCENT) and on each GPS fix and each cycle of the navigation algorithm (for diagnostics/debugging).
Ground station
Now that I had telemetry software in the rocket, I needed a ground station to talk with the rocket.
The ground station consists of a 802.15.4 radio that talks to the rocket, plugged into a netbook computer that acts as a terminal. Here’s a pic of the ground station radio:
Look familiar? It’s pretty much the same PCB that goes in the rocket – this was an earlier spin of the PCB that didn’t work out too well – I’d used a ADP3335 LDO as a voltage regulator, but forgot to put in any heatsink for it. The LDO got too hot for comfort when running of the 7.4v flight battery, but it wasn’t bad when running off the 5v USB from the netbook, so I used the board as the ground station radio. (As you can see, I was experimenting with making the radio module a separate board at the time.)
There are two USB connectors that go to the netbook – one powers the board, the other has a USB-to-serial converter on it, because I still haven’t gotten around to writing a driver for the USB port on the microcontroller side. On the netbook side I’m just running a terminal emulator (PuTTY if you must know) for now – in the future I’m planning to write something that will process the data in real-time to produce plots, etc.
Here’s the netbook, with the radio attached by velcro to the back:
It’s a Samsung N130 running Ubuntu. There’s no particular reason to run Ubuntu instead of Windows (my usual OS) for now, except that Ubuntu seems to be a better-supported Python development environment, and eventually I plan to write some Python code to talk to the rocket in real-time. And it was a good excuse to play around with Ubuntu a little.
One major change I made to the netbook was to replace the standard LCD screen with a Pixel Qi display. Normal (backlit) LCDs are almost impossible to read in direct sunlight – and rocket launches are very often outdoors. I’ve had terrible problems trying to read laptop screens at launches. So I bought the ridiculously overpriced display from Maker SHED ($275 plus shipping for a display to go in a $300 netbook!).
Although I still grumble about the price, the Pixel Qi display is great. It’s a normal color LCD except that, in addition to the backlit mode where it acts like any other LCD display, it has a reflective layer that makes it readable in full sunlight – the more light the better. Some people call this a separate “mode” from when it’s backlit, but there really isn’t a mode – the display is reflective all the time, but you can’t tell unless there is a lot of light. If there is so much ambient light that it washes out the backlighting (outdoors), you can just turn off the backlight altogether and save the battery power it consumes. The reflective mode is black-and-white, but that’s fine with me – it’s readable and I love it.
While I had the netbook apart, I swapped out the HDD for an 80 GB SSD – so it runs a little faster now, with no moving parts except the CPU fan.
Universal controller
Lastly, after a few test flights (which I’ll describe in an upcoming post), I decided that I wanted a way to manually steer the parachute, mainly so I could try to analyse what the navigation system was doing with known steering inputs (which didn’t work for reasons I’ll explain later too).
So I built what I call the “universal controller” to steer the parachute:
It’s nothing but a potentiometer and switch. But, hey, on/off and less/more – what else could you possibly need? 🙂
The pot is connected to an analog input of the microcontroller on the ground station PCB. When the switch is set to ON (switch is attached to a digital input), the position of the pot is read and transmitted as a steering command to the rocket; the steering servo just tracks the position of the knob. When the switch is OFF, the rocket steers itself with the on-board navigation algorithm.
The telemetry system has worked very well, at ranges of well over a mile.