This is post #2 in a series covering the hardware and software design for my work-in-progress GPS-guided rocket recovery project. The main index to the series of posts is here, and an introduction to the project (a PowerPoint presentation) is here.

This post will introduce the software at a high level.

I’ll start with a rough outline of the software architecture and functional spec for the Rev3 hardware/software system.

I say rough because, like most one-man projects, things evolve without necessarily being thoroughly documented. That said, the code is heavily commented (and the comments are up-to-date), so it shouldn’t be too difficult to follow what is going on.

This post will give you an idea of what to expect, but you really need to read the source code to fully understand what is going on.

Regarding rights: My attitude toward the software is a little different than for the hardware. I’m posting all the source code and these comments to help people understand it, and I want to encourage people to use and modify it. However, much hard-won experience is represented in the software and it has a lot more general utility, even outside of rocketry.

So here’s the deal – I hereby grant everyone and everything in the universe permission to use and modify this software for any NON-COMMERCIAL purpose whatsoever, PROVIDED that you (a) agree not to sue me about it, (b) credit as the original source of the software in any publications, and (c) agree that I make no promises and that if you’re unhappy the most I owe you is what you paid me (zip, zero, nada, nothing). Oh, and you agree to USE THIS AT YOUR OWN RISK, that you’re a responsible adult and know that rockets can be dangerous and hurt people if you’re not careful (regardless of whether or not software is involved) so you’ll be careful and will not blame anyone else if you screw up (especially me).

I’ll be very pleased if you leave a comment or drop me an email if you find it useful, but you don’t have to.

For COMMERCIAL use, ask my permission first.  If you’re going to make lots of money off my work, I’d like a (oh-so small and reasonable) cut.  But I’ve no intention of giving anybody heartache over small amounts – just ask, I think you’ll find me surprisingly easy to deal with.

That said, here is the the code, including the Microchip MPLAB IDE project files, just as it appears in the project folder:

You may find it handy to look at the code when reading this introduction, although I’ll save the gory details for later posts.


There is no real-time operating system (RTOS) – no operating system of any kind.

All the code is in C running directly on the “naked” hardware. In my experience RTOSes are very rarely useful in simple-to-moderate complexity embedded systems. Instead, they tend to add a needless layer of complexity and overhead that serves to obscure what is really going on. (RTOSes do have a proper place, I just think they’re over-used.)

I also don’t use any of Microchip’s hardware interface libraries. Instead, I control the hardware directly according to the PIC datasheet. I prefer to know and control what is really going on, and learning how to control the hardware doesn’t take much longer than learning how to use the library functions.

Some will say this makes the code less portable to other MCUs, but I prefer to have a solid understanding of what the code is doing so that I can intelligently port it when needed. In my view this is less risky than relying on the compatibility of “black-box” libraries which, in my experience, often disappoint. I do isolate all the hardware interface functions in a single module (see hardware.h and hardware.c).

As you read the code, you’ll see that I compute many constant values in the C pre-processor. (For example, see #define FLASH_BUFFER_ROWS and #define MS_TO_UTICKS  in hardware.h.)  I find this helps clarify how the constants are calculated, as well as reducing code size.


I make some use of conditional compilation in the code. The major compilation switches are all set (or not) in file tweaks.h. Most of them relate to the presence or absence of various bits of hardware (magnetic compass, GPS models, particular servos, etc.)

The compilation flags __DEBUG and ICD2 disable the “red” button input (symbol SW_RED) for debug builds. This button shares a pin with the ICD2 reset line, so including __DEBUG will include the compilation switch ICD2, which prevents the “red” button from being used (and therefore resetting the CPU). When these switches are defined, the compiler will generate a couple of warnings about code that doesn’t do anything – this is intentional.

Also in tweaks.h are gathered all the critical parameters that you might want to “tweak” (delay periods, thresholds, buffer sizes, etc.).


I’ve left in a fair amount of commented-out code. This was all code that I decided to rewrite for some reason or other (often noted in comments). I left it in on the chance that someday I’ll want to go back to something similar.

Although the code all worked at the time it was commented out, it may not work any more (if un-commented) because of other changes in the code.


The software uses a simple, not terribly strict, finite state machine (FSM) design. This means that at any given time the software is in a given “state” (and maybe also a “sub-state”), in which it has a certain behavior. Various events can cause a change from one “state” into another. When the state changes, some set of actions occur to set up the new state. (The current state and sub-state are in global variables “State” and “Substate”, defined in globals.c.)

There are 6 states (defined in stateMachine.h):

  • IDLE – This is the state upon power-up or after reset.
  • ARMED – Ready for flight, this is used on the launch pad.
  • TIMEDLAUNCH – Used only for ground testing.   The software waits 60 seconds then goes into ARMED SAFE if the ARM/SAFE switch is set to SAFE, or goes to FLIGHT ASCENT if the switch is set to ARM. This is useful for testing things like ejection charges, when you want a chance to get to a safe distance before the thing triggers.
  • FLIGHT – Actually in flight, after launch has been detected.
  • FULL – This is similar to FLIGHT, but the flight log (stored in Flash memory) is full.
  • TEST – Used only for ground testing of systems.

ARMED has 2 sub-states:

  • SAFE – Ready for launch detection, just waiting for ARM/SAFE to be set to ARM.
  • DANGER – Detecting launch, running camera and logging data prior to flight.

Finally, FLIGHT has 3 sub-states:

  • ASCENT – From launch detect to apogee.
  • APOGEE – The short period at apogee when current is supplied to the parachute ejection system.
  • DESCENT – Everything post-apogee, until the system is turned off or the flight log becomes full.

File stateMachine.c has all the functions that implement the state machine (details in a later post).


There are 3 LEDs – red, yellow and green.

Each LED can either be in a steady state (on or off), or it can “invert” (flicker OFF when ON, or vice-versa) to indicate some instantaneous event.

Steady ON or OFF:

  • All OFF – Power is off
  • All ON – CPU is resetting (it will do this for 1 or 2 seconds on power-up)
  • Red ON only – Indicates IDLE state
  • Yellow ON only – Indicates ARMED state
  • Green ON only – Indicates FLIGHT or TIMEDLAUNCH state
  • Red and Green ON (both) – Indicates FULL state

Inversions (flickering):

  • Red flicker – Indicates PIC is writing to Flash memory (the PIC stalls instruction execution during this process)
  • Yellow flicker – Indicates PIC has just received a fix from the GPS


There are 3 buttons, one each associated with the red, yellow, and green LEDs.

The buttons can be “pressed”, “held” or “held long” as follows:

  • Red press (< 1 second) – Enter IDLE state.
  • Red hold (> 1 second) – Check ejection circuit continuity for duration of hold. Green LED ON = good continuity. Yellow LED ON = failed continuity.
  • Yellow press (< 1 second) – Enter ARMED state. The sub-state is controlled by the SAFE/ARM switch. If set to ARM (ARMED DANGER state), the piezo buzzer will sound continuously to indicate this.
  • Yellow long hold (> 4 seconds) – Enter TEST state.
  • Green press (< 1 second) – Enter TIMEDLAUNCH state.
  • Green hold (> 1 second) – Enter FLIGHT ASCENT state. (For ground testing use only.)


The preflight setup related to the software (assuming the rocket is all ready to go) is:

  1. Set ARM/SAFE switch to SAFE (always, for safety).
  2. Power up the board, wait for the power-up sequence to finish (all LEDs ON).
  3. Hold the Red button to check for good ejection circuit continuity.
  4. Button up the electronics bay.
  5. Set ARM/SAFE switch to ARM. 60 seconds later, the system will automatically enter the ARMED state.

That’s if you’re using electronic parachute ejection. If you’re using motor ejection,  it’s just #2 and #4 (power up and prep the rocket).

The reason you want to be so sure the ARM/SAFE switch is in SAFE is that once the rocket is armed, the parachute ejection charge will fire as soon as the rocket detects launch (goes into FLIGHT ASCENT) and then apogee (goes into FLIGHT APOGEE).  Since the software considers ‘apogee’ to mean ‘not going up’, you don’t want to be working on the rocket when that happens.

When you’re ready to finally arm the rocket for flight (before launch), set the SAFE/ARM switch to ARM (step 5). 60 seconds later, when the system enters the ARMED state, the piezo buzzer will sound to indicate that it’s ready. (You can also press the yellow button to force it into ARMED immediately.)

Now the rocket will watch the pressure altimeter to detect launch.

If for any reason you need to take the rocket off the pad or work on it, start by setting SAFE/ARM to SAFE. You’ll know it’s safe if the pizeo buzzer stops sounding.  In ARMED SAFE, the software will not attempt to detect launch.

Rev3 hardware (on the right) installed in 4" electronics bay for SuperHorizon. Batteries and camera are on the left. The Christmas tree bulb is used in place of an AG1 flashbulb for bench testing.