Melvin Green Jr.->GPS-enabled Geocaching Lockbox

Description:

The Geocaching lock box is a box to which you can place an item inside and the box remains locked until it gets to a specific destination.  The GPS coordinates of a specific location is programmed into the embedded system, a GPS reciever is used to determine the current location of the box.  Once the coordinates of the GPS compare to the corrdinates programmed into the embedded system, the box will unlock itself.  A VFD will be added to the front of the box to display the current corrdinates in real-time, along with a switch to activate it.

Components:

Embedded System:

            The embedded system used is the Arduino uno.  This device is the "brains" behind the Geocaching Lockbox.  It is responsible for interpreting the data from the GPS reciever and sending a portion of the data out to the display unit.  It uses simple programmed logic to determine when the lockbox reaches its destination.


GPS reciever:

             The GPS receiver used is the USGLOBALSTAT EM-406A SiRF III.  This GPS receiver communicates via the serial interface, and has a built-in antanna.  The supply voltage is 5v DC.  In general this model of GPS has quite a bit of trouble picking up signals inside buildings.  It works best outside in an open area.

Display:

            The display used is the Noritake CU16025ECPB-W2J.  This display was designed as a direct replacement for a 2 line X 16 character parallel interface LCD, thus the pin-out is similar.  The operating voltage is 5V.  This device used to display the Latitude/Longitude data from the GPS receiver

Lock:

The HiTec ST-311 servo is used for the lock. Due to the current required for the servo, it has its own power supply.  A metal arm is attached to the servo.  The arm is actuated either forward or backward to lock or unlock the box.  This servo gets a Pulse Width Modulation(PWM) signal from the Arduino to control its movement.  The voltage supply to the servo is 6.0V DC using 4 AA batteries.

code:

The code is written for the Arudino, using its standard inferface and software. There are several libraries used:

Servo

TinyGPS

NewSoftSerial

NMEA

LiquidCrystal

 As it was found out, the Arduino interface and software can be buggy and problematic.  Several libraries needed to be modified to work with the Arduino uno.  The software used to program the Arduino, would have compile issues with WORKING code using these libraries.  Also the Arduino itself would have issues being recognized by the computer

Items needed:
GPS->EM-406A                                     $59.95 sprakfun.com              ->ordered 3-26
box with lid                                            $5.00  Mendelson's Luquidation Outlet

Arduino->arduino uno                              $29.95 sparkfun.com

Flexiglass                                              free

switch-> mometary push button switch     free
LCD/VFD                                               free
GPS extension cable                              $2.95

4XAA batteries                                       $1.99

1X 9V battery                                         $1.99

AA battery pack                                     $4.99

9V battery pack                                      $2.95

Wiring Diagram:


Block Diagram:


Code:

/*
  The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 *****GPS module****
 *Blue->connect to serial Tranmit(pin10 digital I/o)
 *Black->connect to serial Recieve(pin9 digital I/o)
 *Green->connect to ground
 *Yellow->connect to +5V
  **The switch**
  *pushbutton attached to pin 8 from +5V
  *10K resistor attached to pin 8 from ground
*/
// include the library code:
#include <Servo.h>
#include <LiquidCrystal.h>
#include <TinyGPS.h>

#include <NewSoftSerial.h>

  // make sure this is NewSoftSerial beta 11 - now called SoftwareSerial
#include <TinyGPS.h>       // you can get TinyGPS from the same place as NewSoftSerial - Mikal Hart's site.

Servo myservo;
//#include <nmea.h>
unsigned char c;
// create a GPS data connection to GPRMC sentence type
//NMEA gps(GPRMC);
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int numRows = 2;
const int numCols = 16;
//***switch code***
const int buttonPin = 8;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

int pos = 0;

NewSoftSerial GPSSerial(9, 10);  // Set up the software serial port on pins 9 and 10 for the GPS.
TinyGPS gps;                     // instantiate the tinygps instance and call it 'gps'
 float CurLat;  // variable for the current latitude
  float CurLon;  // variable for the current longitude
  unsigned long fix_age;  // variable for the age of the location fix (ie, when did we last get a good fix?)
String CurLat_f;
String CurLon_f;
float setLat = 39.51;
float setLon = -84.73;

void printFloat_LAT(double f, int digits = 2);
void printFloat_LON(double f, int digits = 2);

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status
void setup() {
  myservo.attach(6);  // attaches the servo on pin 6 to the servo object
  //***switch code***
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);      
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);     
  //***GPS code***
  GPSSerial.begin(4800);   // start up the serial port used to talk to the GPS
   Serial.begin(9600);  // Start serial communication with GPS
  pinMode(48, OUTPUT);
  // set up the LCD's number of columns and rows:
  lcd.begin(numCols, numRows);
  // Print a message to the LCD.
  //lcd.print("hello, world!");
   //lcd.setCursor(2,0);
        // print the letter:
        //lcd.print(thisLetter, BYTE);
  //lcd.print("Whirlpool!");//Prints on the 1st line
 // lcd.print(printFloat(CurLat, 5));
 // lcd.setCursor(2,1);
   //lcd.print("Push!");//Prints on the 2nd line
   //lcd.print(printFloat(CurLon, 5));
}

void loop() {
  //**switch code***
   // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  if (buttonState == HIGH) {     
    // turn LED Off:    
    digitalWrite(ledPin, LOW);
    lcd.noDisplay();
  }
  else {
    // turn LED ON:
    digitalWrite(ledPin, HIGH);
    lcd.display();
  }
  //***GPS Code***
//if (Serial.available() > 0 ) {
//    // read incoming character from GPS
//     c = Serial.read();
//    Serial.print(c, BYTE);
//   }
 
  while (GPSSerial.available())  // if serial data is available then do the stuff below..
  {
    char c = GPSSerial.read();
    if (gps.encode(c))  // valid data was received from the gps and has been parsed, ready for use
    {
 
     // gps.get_position(&CurLat, &CurLon, &fix_age);  // get the current location data from the GPS

      Serial.print("Current Latitude: ");
gps.f_get_position(&CurLat, &CurLon, &fix_age);
//lcd.begin(numCols, numRows);
//lcd.setCursor(0,0);
        // print the letter:
        //lcd.print(thisLetter, BYTE);
  //lcd.print("Whirlpool!");//Prints on the 1st line
//  lcd.print("LAT:");
//  lcd.setCursor(4,0);
//  lcd.print(CurLat);
//  
//  lcd.setCursor(0,1);
//   //lcd.print("Push!");//Prints on the 2nd line
//   lcd.print("LOG:");
//   lcd.setCursor(4,1);
   //lcd.print(CurLon);
   CurLat_f = "";
   CurLon_f = "";
   
  Serial.print("Lat/Long(float): ");
 
  //printFloat_LAT(CurLat, 5);
  lcd.setCursor(0,0);
  lcd.print("LAT:");
  lcd.setCursor(4,0);
   printFloat_LAT(CurLat, 5);
  lcd.print(CurLat_f);
  Serial.print(", ");
  //printFloat_LON(CurLon, 5);
  lcd.setCursor(0,1);
   //lcd.print("Push!");//Prints on the 2nd line
   lcd.print("LOG:");
   lcd.setCursor(4,1);
   printFloat_LON(CurLon, 5);
   lcd.print(CurLon_f);
  Serial.print(" Fix age: "); Serial.print(fix_age); Serial.println("ms.");
      //Serial.print(CurLat);  // output current latitude
      //Serial.print("Current Longitude: ");
     // Serial.print(CurLon);  // output current longitude
      //Serial.print("Age of Fix: ");
      //Serial.print(fix_age);  // output age of GPS fix
      Serial.print("  |  ");  // put a break between this output and the next output, maybe Serial.println(); would put the next output on a new line..
      
      delay(1000);             // wait 3 seconds then continue - this stops the serial debugger from getting swamped.
    
//    if((CurLat == setLat)&& (CurLon == setLon))
//    {
//             for(pos = 0; pos < 70; pos += 1)  // goes from 0 degrees to 180 degrees
//        {                                  // in steps of 1 degree
//          myservo.write(pos);              // tell servo to go to position in variable 'pos'
//          delay(10000);                       // waits 15ms for the servo to reach the position
//        }
//    } else{
//           for(pos = 30; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees
//      {                                
//        myservo.write(pos);              // tell servo to go to position in variable 'pos'
//        delay(3000);                       // waits 15ms for the servo to reach the position
//      }
//    }
    }
  }
  //lcd.autoscroll();
  lcd.display();
 // delay(500);
 
}
void printFloat_LAT(double number, int digits)
{
  // Handle negative numbers
  if (number < 0.0)
  {
     Serial.print('-');
     number = -number;
     CurLat_f = CurLat_f + '-';
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
 
  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  Serial.print(int_part);
  CurLat_f = CurLat_f + int_part;

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    Serial.print(".");
    CurLat_f = CurLat_f + ".";
  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    Serial.print(toPrint);
    CurLat_f = CurLat_f + toPrint;
    remainder -= toPrint;
  }
}
void printFloat_LON(double number, int digits)
{
  // Handle negative numbers
  if (number < 0.0)
  {
     Serial.print('-');
     number = -number;
     CurLon_f = CurLon_f + '-';
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
 
  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  Serial.print(int_part);
  CurLon_f = CurLon_f + int_part;

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    Serial.print(".");
    CurLon_f = CurLon_f + ".";
  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    Serial.print(toPrint);
    CurLon_f = CurLon_f + toPrint;
    remainder -= toPrint;
  }
}

Pictures:

Geocaching Lockbox Pictures


LOG:

Geocaching lockbox progress log

 

Demo video:

You can see a demo video here: http://www.youtube.com/watch?v=4OQFVJRJ0no

 

Datasheets:

CU16025ECPB-W2J-03.pdf

em406a_ug.pdf

Some helpful websites:

http://wiring.org.co/learning/libraries/basicpositioning.html

http://www.sparkfun.com/products/465

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1295292941/all

http://arduiniana.org/libraries/NewSoftSerial/

http://arduino.cc/forum/index.php?action=printpage;topic=56361.0

http://www.maartenlamers.com/nmea/gps-nav-001.html#photos