On the 6th of August, we held J's bachelor party, the very cause of this project. We wanted to streamline the day by taking control out of our drunk hands and putting it into a device J would have to obey blindly. Instead of manually keeping track of time and location (quite a challenge sometimes, even when sober), we offloaded that to a GPS-enabled device with pre-programmed routes. When we originally came up with the idea, we were mainly thinking about tram routes, but we ended up with a mix of routes by tram and by foot instead.
This is a project I did in collaboration with Chris Pockelé.
On the left: the main components we intended to use. On the right: the actual result. As you can see, the 16x2 LCD was replaced by a larger one.
As most important part for our project, we bought a EM-411 GPS module from DealExtreme, which features a SiRF Star III chipset (datasheet and info). DealExtreme also sells several other GPS modules, but this was the only model with an integrated antenna. We could have bought an other module and an external antenna for a few cents less, but that was not worth the trouble. A quick search also ensured us that the EM-411 was good enough.
This module was the most important component of this project as well as the most power-hunger part: in full operation, the GPS module draws ~30mA. We only had power from 2x 9V batteries at 220mAh, so we had to shut the module down in between routes. The first idea was to use a traditional bipolar junction transistor (BJT) since that was what we had readily available at the time. However, after discussing the project with one of his colleagues, Chris decided to use an FET instead.
When using an FET instead of a BJT, the ground level of the switched device (GPS module) is closer to the ground level of the Arduino controller, because of the low drain-source resistance when switched on. A BJT would typically cause a voltage difference of about 0.7V. In the end, replacing the BJT with an FET increases reliability of communication between the controller and the GPS module, since the signal levels also match more closely.
There is a second advantage: the FET is effectively voltage-controlled, i.e. the input (gate) current is very low (order of a few nA), while the base current of the BJT would be about 5~10mA. This is important since the point of electronically switching the GPS module was to save battery power.
Our GPS module (and all others as well, I suppose) also reports the current heading (the bearing of the measured movement), but we figured (from experience with commercial GPS-devices) that would be too unreliable when afoot. Besides, having no indication of direction when standing still was no option. So we bought a LSM303DLH 3D Compass and Accelerometer Carrier with Voltage Regulators from Watterott, which was (at the time) the cheapest accelerometer/magnetometer available. We were first looking at LSM303 Breakout Board, which carries the same chip, but we couldn't provide the 1.8V so we went for this board instead.
Although it was very easy to interface with the sensor (there is an Arduino library for the LSM303 that talks to it over I²C, thanks guys), we had some trouble getting reliable readings from it. We soon discovered that reported values were (obviously) very dependent on the surrounding electronics, so we had to assemble the entire project first before we could calibrate the sensor. By then, I had too little time to figure out why the 3D readings (3 axes, tilt compensated with the accelerometer) seemed off, so we settled for a dumber XY compass by ignoring the Z-axis. I had to redo the calibration process several times because I forgot to enable the GPS module in the calibration sketch, which causes entirely different extrema, even at lowest sensitivity. I suspect that the troubles we experienced were mainly a case of did-not-read-the-documentation-well-enoughitis.
Together with the compass module, we bought a 16x2 character LCD display (DEM16216SYH-LY). After a good wrestle, we got it working and all was well. But then we got our hands on a larger 16x4 display with the same interface. Although we could have managed with only two lines, the larger display allowed us to display extra information like the current time, number of satellites, current route and current target point.
We actually planned on using a Seeeduino board with an ATmega168 chip, but we soon ran out of flash space. Luckily, I also have an Arduino Duemilanove with an ATmega328 (32kb flash), so we could just switch to that instead of reducing the code size. We probably could have done that by ditching all the Arduino overhead, but that would have cost us too much time: interfacing the LCD and the compass was done with existing Arduino libraries.
We put all of the parts above in a large top hat, together with some smaller stuff and a meter of black wire. An hour later, a fully assembled Arduino shield came out. (We honestly have no idea where the breadboard came from.) Parts go in, shield comes out. Never a miscommunication, can't explain that.
On the picture, you can see:
In contrast to a commercial GPS-device, our project would not contain street maps of the area, nor would it have enough CPU power to (re)calculate routes on the fly. Instead, we had to preset the device with a list of routes, each with a list of target points and a start time.
We created and managed the routes with Google Earth, a nice, free application by Google. With it, we could easily gather multiple routes into a folder and export it to .kml, which is based on XML. I started the soft side of this project by creating two XSLT documents. simplify.xsl to filter and simplify a KML into an intermediate XML format and simple2c.xsl to convert that simpler XML into a C-file filled with structs and floating point numbers. I spent quite some time creating these XSLT documents, but won much more time by using them.
I've put an example route (.kmz) online (shown above, view in Google Maps) that contains two routes: at noon from The Louvre to Vatican city and at 17:00 from Paris to Santiago de Compostela. While the routes for the bachelor day were on an entirely different scale, it can give you an idea of a simplified XML and a C-file that can be plugged into the code.
The actual routing information for the bachelors day had 65 target points, each represented by a couple of floats to specify latitude and longitude, good for 520 bytes. Although we probably could have squeezed it next to the other data into the RAM memory of the ATmega, we decided to store the routing data in flash. The Arduino reference on PROGMEM states that floating point numbers are unsupported, but that worked just fine after defining our own prog_float (cf. platform.h).
The source (and some Makefile-fu) is available on http://publictrac.natox.be/trac.fcgi/jgpx/browser, I've tried to split it out a bit in what follows.
The Arduino code is driven by the input from the GPS module. Every byte read from the serial port is fed into a minimal NMEA parser (gpsparse.cpp). If the parser signals that a GGA line is read (at least at 1Hz, even without proper GPS reception), the screen is updated. The rest of the time, the Arduino waits for serial input.
At each update, it is checked if navigation is active. If so, the (unvisited) target point closest to the current GPS location is determined. If the distance between the current position and the retained target point is larger than a certain threshold (e.g. 15m), the display is updated to show distance and bearing to that point. If the distance is smaller than the threshold, the target point is marked as visited and course is set to the next target point. If the last point has been reached, either the next route is activated, or the device goes into idle mode. This is implemented in router.cpp.
If navigation is not active, the device checks when the next route is to be started. In order to save some of our precious battery-juice, we decided to put the system into standby instead of idling longer than 10 minutes. Just before going into standby (actually SLEEP_MODE_PWR_DOWN), the watchdog timer is set to a timeout of 8 seconds. Because of this, the chip should wake up every 8 seconds, check if it can go back to sleep and stay awake otherwise. Chris tested this with my Seeeduino and concluded that the power saving is substantial, but that specific chip in that setup only sleeps for about 6 seconds, which means it doesn't sleep long enough.
At (supposedly) 125mHz, a fixed value is subtracted from the sleep time until it hits zero. Further experiments on the Arduino Duemilanove showed that on that device, using a decrement of 8 overshoots wake up time, which is bad. So instead, we subtract 9 seconds on each watchdog timer interrupt. This way, the device wakes up a little earlier than intended, which is better than later, obviously.
In full operation (GPS on, ATmega running) the entire device draws ~60mA. With the GPS off, it drops to about 30mA, and in power down mode, only ~12 mA is required. Also shutting down the compass and the screen doesn't change that. The relevant code can be found in gpsarduino.cpp, function gotosleep.
We also added a buzzer to the project to draw the attention of the user whenever something changed. The relevant code is grouped in beeper.cpp. It uses Timer1 (16-bit timer) in CTC mode and allows to specify a frequency (one HIGH+LOW=one period) and a repeat count. With those parameters, we could emit a lot of different beeps, each with its own meaning, e.g. no reception, target reached, etc.
We made a basic GPS device that instructed our bachelor where to go at which time. The core consists of an Arduino, equipped with a GPS module and a magnetometer. The device contains routes and target points, created and managed with Google Earth. We had a fun day and a lot of interested looks.