This is a portable device which is used to display its current longitude and latitude on a LCD screen. It can also display other data which can be gathered from almost all GPS modules which support the SiRF Binary protocol, such as speed, heading, time, and other data. The device uses a ATMEGA644 (originally an ATMEGA32 but its RAM was not enough) microcontroller to configure and read data from the GPS module, output the data to a LCD, and read four push buttons which acts as the human interface. The buttons control the menus inside the user interface implemented in the microcontroller.
The device is built on a protoboard and housed between two sheets of polycarbonate. The battery holder door is made of a compass keychain. It's not waterproof but the housing makes it suitable for outdoor use as it protects the solder joints and wiring. I built this device so I can go Geocaching.
Want to build your own? I have the source code files, a soldering diagram, and a Eagle schematic.
The GPS module used in this project is a EM-406A GPS module. It has 20 channels, an update rate of 1 Hz, and a hot start time of 1 second. It also has a built on antenna which is convenient and built in super capacitor to retain RAM data after losing power. By default, this module outputs data in NMEA mode at 4800 baud, the ATMEGA644 commands it to switch into SiRF Binary mode at 9600 baud during the initialization routine. SiRF is chosen because it does not use ASCII to represent data and thus data can be transferred with more density and thus require less time and code to process.
The LCD is a standard 16x2 character display, a white on blue backlit model was used to increase readability at night. It is interfaced to the microcontroller with a 8 bit parallel port. The contrast control is a potentiometer accessible through the back of the device by a screwdriver. The user interface is controlled by 4 tactile push button switches. Each of the switches are connected to a signal diode's cathode, and the anodes of the 4 diodes are connected together and a few resistors and capacitors make up a hardware button debouncing circuit. This lowers the part count by having only one set of debouncing components, and the program is more space efficient and execute faster because the debouncing is handled by hardware with a single falling edge interrupt is used for all four buttons. There are also two LEDs on the device, one to indicated when the device is busy processing new data from the GPS, and one to indicate when a button press has been detected.
The user interface is implemented using several stacked menus. The main menu allows the user to either display data, go into navigation mode (which is not currently programmed), configure the device settings, or go into a sleep mode. If the user selects display, another menu asks the user about what he wants to display, such as current location, speed and heading, time, or other. If the user chooses other, then he can choose to view altitude data, data about which satellites (each satellite ID can be determined) are being used, and also the current accuracy of the GPS fix.
If the user goes into the setup sub menu, options such as the time zone, backlight, GPS update rate, and some other options can be adjusted, saved to or read from EEPROM (work in progress feature), or reset to default.
All data can be displayed in several formats (degree-decimal, degree-minute-decimal, degree-minutes-seconds), and in both SI units (km/h, meters, etc) and imperial units (mph, feet, etc). To keep the user interface responsive, the conversion is only done once per data update, and only if the conversion is needed.
If the device can't get any satellite signals, it will tell the user that if the user is trying to view data, and the update rate is automatically set to the fastest possible, however, the menu is still usable while there is no signal.
The firmware is written in C and compiled with AVR-GCC inside AVR Studio. The SiRF protocol handling is near perfect. All math is done without using floating point variables to save space and processing time. My goal was to have a fast and responsive user interface, which meant new data is processed only once and units are converted only when needed. The lengths of interrupt routines are kept to a minimum to keep a fast frame rate on the LCD.
On the current prototype, everything is soldered to a protoboard, which is mounted between two sheets of polycarbonate. The power comes from a 9 volt battery, I used a nice battery holder instead of a cheap 9 volt snap, and a battery compartment door is made from a keychain which has a thermometer and a compass. Although not water proof in any way, this setup is very nice for doing this outdoors. I use this device mainly for Geocaching.
This project is a work in progress, I hope to add navigation assistance and logging features soon.
Everything that handles the SiRF transmissions are written inside sirf.c. It uses functions from ser.c to transmit and receive data from the UART which is what the ATMEGA uses to communicate with the GPS module. The SiRFBinaryTx function and the SiRFBinaryRx are the soul of this file. Everything else uses these two functions to command or read from the GPS.
The transmit function has two parameters, one is an array of bytes (the payload) to be sent, and the second is the length of the packet. The array parameter does not need to include the message starting sequence, length, checksum, or ending sequence, as the function handles these on its own. To send the bytes, serTx is the function called inside ser.c, which waits for the previous transmission to be finished before starting a new one. This method uses less RAM than using a FIFO buffer, which is why it's chosen even though it is slower than using a FIFO buffer. Also note that the SiRFBinaryTx function is rarely used while the user interface is being shown and thus its execution speed is irrelevant.
The SiRFBinaryRx function is called only when data from the GPS module is expected. It uses the serRx function inside ser.c to read data from the UART receive buffer. Each time a new byte is received by the UART module, the interrupt routine places it in a FIFO buffer so no data is ever missed or lost. The value returned by serRx is can also be read from serRead inside sirf.c. serRead waits until the FIFO buffer is not empty to call serRx, but it also uses a 8 bit timer to implement a timeout so the firmware never freezes.
SiRFBinaryRx will verify that the first two bytes are the correct message starting sequence, then use the message length to run a loop to read every byte in the payload into a buffer while calculating the checksum. Once that is done, the checksum is verified and then the ending sequence is verified. If anything times out, the checksum and/or ending sequence will not be correct. If anything is incorrect, SiRFBinaryRx will return a 0, or else if everything is fine, it will return the message ID of the payload. The payload is stored in two different global arrays depending on whether it's GPS location data or other data.
The module, fresh from the factory, or after a factory reset, will begin outputting NMEA strings immediately after power up. Since my device uses SiRF, the strings are ignored, but it's important to know that the module uses 4800 baud if it is fresh. The device will send the "switch to SiRF binary mode at 9600" at 4800 baud as a NMEA command first, then send it in SiRF. Then it does the same thing again at 9600 baud just in case. This will make sure the module uses SiRF binary mode at 9600 baud.
Without any previous configuration, if the module goes into SiRF binary mode, it will start to output messages just like if it was in NMEA, except there are so much debug data that my firmware can't actually keep up (it actually resets or freezes my microcontroller sometimes for some reason). A for loop tries to disable all automatic messages with message set rate command.
The module is ready to use after all automatic messages are off, the firmware then enables message 41, which is the geolocation data message, and sets the rate according to how the user configures the device.
The user interface has two parts, the LCD to display information and the menu to the user, and the 4 buttons used by the user to give input to the device.
The menu system would have been better implemented if I have used a object oriented language. Each menu is a loop that checks for new GPS data (but does no calculation), check if buttons have been pressed, then update the screen. The loop is exited if the back or select button is pressed, then it either returns to the previous menu function or calls the next menu function.
The data displays are similar to menu functions. They will display a "no signal" message if there is no satellite signal. They also will calculate the data needed when they check for new GPS data. The buttons behave slightly differently to select different display modes.