Saturday, June 21, 2014

GPS Location Sensing with the Arduino Mega

This project shows how to get location in latitude and longitude coordinates using an electronic circuit built with the Arduino Mega 2560 and the ITEAD GPS Shield v1.1 . If you want to see how to do GPS location sensing with the Arduino Uno and an LCD display check out my other blog.

The GPS shield has a GPS antenna connected. This is very important as you can't get a location fix if you don't have an antenna.

Using this circuit I got a location fix indoors on the ground floor of a 2 story house in 1 to 2 minutes. Outdoors it got a fix in about 30s or less. Accuracy was very good, to within 10-20ft initially, and improving the longer you leave it (it gets a new reading every second.

The wiring of this diagram is straightforward. First you should set the voltage switch on your GPS shield to 5v before you power it on and connect it up. There is also a 3.3v setting, but for our circuit we are using the Arduino Mega 2560 so the voltage setting I used is 5v. Next you need to set the jumpers for Rx (receive) and Tx (transmit) on the GPS shield. I set my GPS Rx to pin 6 and GPS Tx to pin 5. I wanted to receive the output of this circuit on my computer so I needed the default hardware serial interface for that. To communicate with the GPS shield I used the mega hardware serial interface 1. So I wired GPS Rx pin 6 to Mega Serial1 Tx1 pin 18, and GPS Tx pin 5 to Mega Serial 1 Rx1 pin 19. Using this circuit I was able to get a location fix even inside a house on the ground floor of a 2 story house. If used outside with a clear view of the sky it will get a GPS fix even faster.

You will need TinyGPS to run the sketch I used. Specifically you will need the TinyGPS.cpp and TinyGPS.h files to run this. I just dropped them in the same folder as the Arduino sketch and then when I loaded the sketch the Arduino IDE automatically found these two files and compiled and uploaded them with my sketch to the Mega for testing.

Below is a screenshot of the Arduino IDE serial console showing output from this sketch running. The GPS latitude / longitude coordinates each have a precision of 6 decimal places.

Below is the Arduino sketch I used for testing:

#include "TinyGPS.h"

TinyGPS gps;

#define GPS_TX_DIGITAL_OUT_PIN 5
#define GPS_RX_DIGITAL_OUT_PIN 6

long startMillis;
long secondsToFirstLocation = 0;

#define DEBUG

float latitude = 0.0;
float longitude = 0.0;

void setup()
{
  #ifdef DEBUG
  Serial.begin(19200);
  #endif
  
  // Serial1 is GPS
  Serial1.begin(9600);
  
  // prevent controller pins 5 and 6 from interfering with the comms from GPS
  pinMode(GPS_TX_DIGITAL_OUT_PIN, INPUT);
  pinMode(GPS_RX_DIGITAL_OUT_PIN, INPUT);
  
  startMillis = millis();
  Serial.println("Starting");
}

void loop()
{
  readLocation();
}

//--------------------------------------------------------------------------------------------
void readLocation(){
  bool newData = false;
  unsigned long chars = 0;
  unsigned short sentences, failed;

  // For one second we parse GPS data and report some key values
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (Serial1.available())
    {
      int c = Serial1.read();
//      Serial.print((char)c); // if you uncomment this you will see the raw data from the GPS
      ++chars;
      if (gps.encode(c)) // Did a new valid sentence come in?
        newData = true;
    }
  }
  
  if (newData)
  {
    // we have a location fix so output the lat / long and time to acquire
    if(secondsToFirstLocation == 0){
      secondsToFirstLocation = (millis() - startMillis) / 1000;
      Serial.print("Acquired in:");
      Serial.print(secondsToFirstLocation);
      Serial.println("s");
    }
    
    unsigned long age;
    gps.f_get_position(&latitude, &longitude, &age);
    
    latitude == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : latitude;
    longitude == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : longitude;
    
    Serial.print("Location: ");
    Serial.print(latitude, 6);
    Serial.print(" , ");
    Serial.print(longitude, 6);
    Serial.println("");
  }
  
  if (chars == 0){
    // if you haven't got any chars then likely a wiring issue
    Serial.println("Check wiring");
  }
  else if(secondsToFirstLocation == 0){
    // still working
  }
}


If you are interested in how location sensing can be integrated into a broader solution that includes notification of location and geo-fence alerts on a users Android phone see Geo-Fencing

Hope you found this useful. Let me know if you create any cool enhancements to it. Have fun!

If you need any of the parts for this projects you can find them below:

Saturday, June 7, 2014

GPS Location Sensing with the ITEAD GPS Shield and Arduino Uno

Location is a valuable input for many applications. In this project I use the ITEAD GPS Shield v1.1 with a GPS antenna , an Arduino Uno , and an LCD Display to sense location in terms of latitude and longitude coordinates.

With this setup I was able to acquire lat / long coordinates outdoors with a clear view of the sky in 23s to 32s with an average of 28s. Indoors on the bottom floor of a 2 story house I was able to get a location in 61s to 140s with an average of 101s from time of power up to time of first GPS lat / long coordinate acquisition. With this circuit the longer it is given the more accurate the reading. Typically I found the first reading within a hundred or so feet and given more time it would get down to a few feet accuracy.

Below is the picture of the complete circuit.

The wiring of the circuit is fairly straightforward. There are two parts. The first is how to connect the GPS shield. It connects easily on top of the Arduino Uno so thats no problem. You will also need to set the jumpers for the GPS Rx (receive) pin 7 and GPS Tx (transmit) pin 6 parts of the serial interface.

Note that later on in the sketch for the Arduino code you will see the Uno Rx pin is 6 and Uno Tx pin is 7. This is because the Uno (Tx) transmits to the GPS (Rx) on pin 7 and conversely the Uno (Rx) receives from the GPS (Tx) on pin 6.

The second part of the wiring is the LCD display. Your wiring may vary depending on the display you have, so check its instructions. For my wiring I used the hookup below:

 LCD Pin Connect to
 1 (VSS) GND Arduino pin*
 2 (VDD) + 5v Arduino pin
 3 (contrast)  2.2k resistor to GND
 4 RS Arduino pin 12
 5 R/W Arduino pin 11
 6 Enable Arduino pin 10
 7 No connection 
 8 No connection 
 9 No connection 
 10 No connection 
 11 (Data 4) Arduino pin 5
 12 (Data 5) Arduino pin 4
 13 (Data 6) Arduino pin 3
 14 (Data 7) Arduino pin 2
 15 Backlight +
  1.5k resistor to Arduino pin 13
 16 Backlight GND GND Arduino pin*

I found this blog helpful in wiring my display.

Make sure you have a GPS antenna. I was not able to get a location fix without one.

I used a small 9v battery to power the circuit. This was fine for testing the acquisition time in a variety of locations. If you intend to run this circuit for longer periods of time you may need a more powerful power supply.

You will need TinyGPS to run the sketch I used. Specifically you will need the TinyGPS.cpp and TinyGPS.h files to run this. I just dropped them in the same folder as the GPS_LCD_Display.ino Arduino sketch and then when I loaded the sketch the Arduino IDE automatically found these two files and compiled and uploaded them with my sketch to the Uno for testing.

Below is the Arduino sketch I used for testing:

#include <SoftwareSerial.h>
#include <LiquidCrystal.h>

#include "TinyGPS.h"

TinyGPS gps;

int unoRxPin = 6; // connected to Tx pin of the GPS
int unoTxPin = 7; // connected to Rx pin of the GPS
SoftwareSerial ss(unoRxPin, unoTxPin);

LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
int backLight = 13;    // pin 13 will control the backlight

long startMillis;
long secondsToFirstLocation = 0;

void setup()
{
  ss.begin(9600);
  
  pinMode(backLight, OUTPUT);
  digitalWrite(backLight, HIGH); // turn backlight on. Replace 'HIGH' with 'LOW' to turn it off.
  lcd.begin(20,4); // columns, rows.  use 16,2 for a 16x2 LCD, etc.
  lcd.clear();  // start with a blank screen
  
  startMillis = millis();
}

void loop()
{
  bool newData = false;
  unsigned long chars = 0;
  unsigned short sentences, failed;

  // For one second we parse GPS data and report some key values
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (ss.available())
    {
      int c = ss.read();
      ++chars;
      if (gps.encode(c)) // Did a new valid sentence come in?
        newData = true;
    }
  }

  if (newData)
  {
    // we have a location fix so output the lat / long and time to acquire
    if(secondsToFirstLocation == 0){
      secondsToFirstLocation = (millis() - startMillis) / 1000;
    }
    
    lcd.clear();  // start with a blank screen
    
    float flat, flon;
    unsigned long age;
    gps.f_get_position(&flat, &flon, &age);
    lcd.setCursor(0,0);           // set cursor to column 0, row 0 (the first row)
    lcd.print("Lat=");
    lcd.print(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6);

    lcd.setCursor(0,1);
    lcd.print("Long=");
    lcd.print(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6);

    lcd.setCursor(0,2);
    lcd.print("Acquire Time=");
    lcd.print(secondsToFirstLocation);
    lcd.print("s");
  }
  
  if (chars == 0){
    // if you haven't got any chars then likely a wiring issue
    lcd.setCursor(0,0);           // set cursor to column 0, row 0 (the first row)
    lcd.print("No GPS: check wiring");
  }
  else if(secondsToFirstLocation == 0){
    // if you have received some chars but not yet got a fix then indicate still searching and elapsed time
    lcd.clear();  // start with a blank screen

    long seconds = (millis() - startMillis) / 1000;
    
    lcd.setCursor(0,0);           // set cursor to column 0, row 0 (the first row)
    lcd.print("Searching ");
    for(int i = 0; i < seconds % 4; ++i){
      lcd.print(".");
    }
    
    lcd.setCursor(0,1);
    lcd.print("Elapsed time:");
    lcd.print(seconds);
    lcd.print("s");
  }
}

If you are interested in how location sensing can be integrated into a broader solution that includes notification of location and geo-fence alerts on a users Android phone see Geo-Fencing

Hope you found this useful. Let me know if you create any cool enhancements to it. Have fun!