Tuesday, April 29, 2014

GPS Lockbox: Code Done, Assembly Begun

Last time I posted about the "guts" of a lockbox which would open when a certain location was reached.  The user would input a destination, start the program, the box would lock, and would not open again until the user were close enough to the destination.

I had worked out the details of reading the GPS, using the keypad through a GPIO I2C board, using a 8x2 LCD, and using some RGB lights as indicators.  Since that point, I have upgraded to a 16x2 LCD, and greatly upgraded the code, which now asks for user input (a destination location), calculates the distance to the location, modifies the RGB lights to have a color proportional to the distance to target (from blue == far away to red == close), and "unlocks" the box when close enough.

Original prototype for a 16x2 breakout board
for a Sparkfun LCD.  This is the one I am using
for the GPS lockbox.
The first modification was to use a 16x2 LCD from Sparkfun.  Data sheet is here.   It is SO much bigger than the 8x2, and it operates in 4bit mode meaning that only four of the eight data lines (DB0 - DB7) are needed to "run" the LCD.  I made a PCB breakout board for this one:  the Eagle data files can be found here. This process also opened up four more pins on the Arduino Micro... Enable, R/W, DB4, DB5, DB6, and DB7 are running to the analog inputs, freeing up four of the digital pins.

For the newer prototypes, the potentiometer
is underneath the LCD to minimize space.

I had previously had some confusion about the differences between different ways to represent GPS data.  I have standardized the code such that it always represents data in the format dd mm.mmmm.  This happens both when data is printed back to the computer over the serial port, and also when data is printed on the LCD.  This format can also be found easily online in many places like GPS Visualizer (see the picture below).  So this should make the process of inputting a destination pretty easy.

Next I added in code which allows the user to put in the target destination.  The most current version of the code is on GitHub.     The flowchart below shows the way that the keypad is read.  This is important, because it takes about a second to read the GPS and we only want this to be happening when the user is not entering data.  Otherwise the delay is impossibly confusing.  In addition, the flowchart always allows us to "start over" at any point by pressing A, so that we can always enter a new destination without having to restart the microprocessor.

The right hand side of the code is very similar to what I had in the first iteration.  The biggest change is that I realized that I needed to look for the $GPGGA ANYWHERE in the GPS string and not just at the beginning of the string.  I had thought my previous code did this, but I was wrong.  The line below looks for $GPGGA and also looks for an astericks after $GPGGA.  Since $GPGGA is at the start of the string and * indicates the checksum, the Parse flag is only set if a full string has been seen.

 if(StringContains(buffer, "$GPGGA")!= -1 && StringContains(buffer, "*") > StringContains(buffer, "$GPGGA")) {
        startPosition = StringContains(buffer, "$GPGGA") +7;
        Parse = true;
      }

The distance function on the right side is one that I just pulled directly from the internet.  I have no idea how it works; has to do with finding the distance along a great circle between the current location and the destination location.

The rest of the code is straightforward although the implementation is pretty course.  The Setup Program drops the Latitude Setup Flag, prints a prompt on the LCD for the operator ("Latitude: ddmmmmmm"), zeros the input buffer, and turns the LEDs red to show that we are waiting for input.

The Latitude Setup Program reads in the latitude as it is typed (slowly shifting the LEDs towards green) and prints the inputs on the LCD so that they replace the ddmmmmmm.  It takes 8 digits, then prompts the user to "Hit D".  Hitting D raises the Latitude Setup Flag, drops the Longitude Setup Flag, prints a prompt on the LCD for the operator ("Longitude: dddmmmmmm"), turns the LEDs red, and zeros the input buffer.  The Longitude Setup Program does the same thing as the Latitude Setup Program, raising the Setup Flag at the end.

The structure of the program allows the user to type A at any time which will lower the Setup Flag (bypassing GPS readings until the flag is set again) and allowing the setup program to run again in its entirety.

The last piece of this puzzle has to do with the RGB LEDs.  Originally I had hoped to have them fade throughout the program with the rate of fading increasing as the user got closer to the target destination.  While I updated the function fadeColor to be asynchronous (so the colors would change as the main loop cycled without a separate loop within the function), I found that the fade rate was limited by the loop cycle time, which was relatively slow because of the time required to read the GPS.  Fades either go impossibly slow OR huge jumps in color must be done each loop, which looks stupid.  So I will only use the fading at the very end as a sort of celebration once the box is opened.  Otherwise, once setup is done, the color of the LEDs is just set on a scale between RED (255, 0, 0) or BLUE (0, 0, 255) using a scale factor (eventually i'll program in the user to enter the approximate distance to the target and that will be the scale factor).  The variable "precision" is a threshold for how close the user needs to be to the target before the box opens.


I am working on putting all the components into a hinged box bought from Staples, product #942401.  Fortunately it laser prints beautifully.  I will have to figure out how the box physically locks.