GPS Module with Arduino
d. bodnar   revised 03-02-2015 

 I have been experimenting with a small GPS module that was purchased from Amazon for about $25.00.  It also available on eBay and from other vendors for nearly half the price.  These units work well and can easily be interfaced to an Arduino.

 

 

 Hardware Setup
There are four connections to the GPS module, VCC (3-5 volts), ground, Transmit (TX) and Receive (RX).  The TX and RX are standard serial signals.  The default speed is 9600 baud.

I have tested this module at both 3.7 and 5 volts.  There is a 3.3 volt regulator on the board just under the "VCC RX TX" labels.

RX and TX connect to the Arduino TX and RX pins, if hardware serial is being used, or to two other pins, determined in the software, if software serial is used.

Notes on GPS data sentences:

$GPRMC,122947.00,A,4023.16420,N,08002.99191,W,0.054,,140215,,,A*6C
$GPVTG,,T,,M,0.054,N,0.100,K,A*23
$GPGGA,122947.00,4023.16420,N,08002.99191,W,1,06,1.35,356.8,M,-34.2,M,,*63
$GPGSA,A,3,29,02,21,05,13,15,,,,,,,2.50,1.35,2.10*08
$GPGSV,3,1,12,02,40,102,31,05,64,040,25,07,03,043,,10,06,060,18*70
$GPGSV,3,2,12,13,51,138,30,15,30,186,34,18,00,246,,21,15,297,26*7D
$GPGSV,3,3,12,25,09,236,18,26,54,114,24,29,63,274,39,30,04,071,13*7B
$GPGLL,4023.16420,N,08002.99191,W,122947.00,A,A*77


$GPGGA,123639.00,4023.16752,N,08002.99406,W,1,06,1.32,364.1,M,-34.2,M,,*66

time lat lon fix S Hdop alt M checksum



The most important NMEA sentences include the GGA which provides the current Fix data, the RMC which provides the minimum gps sentences information, and the GSA which provides the Satellite status data.

GGA - essential fix data which provide 3D location and accuracy data.

$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47

Where:
GGA Global Positioning System Fix Data
123519 Fix taken at 12:35:19 UTC
4807.038,N Latitude 48 deg 07.038' N
01131.000,E Longitude 11 deg 31.000' E
1 Fix quality: 0 = invalid
1 = GPS fix (SPS)
2 = DGPS fix
3 = PPS fix
4 = Real Time Kinematic
5 = Float RTK
6 = estimated (dead reckoning) (2.3 feature)
7 = Manual input mode
8 = Simulation mode
08 Number of satellites being tracked
0.9 Horizontal dilution of position
545.4,M Altitude, Meters, above mean sea level
46.9,M Height of geoid (mean sea level) above WGS84
ellipsoid
(empty field) time in seconds since last DGPS update
(empty field) DGPS station ID number
*47 the checksum data, always begins with *

 

 Software for Testing
The program that I used for testing is available in the examples folder for TinyGPS++, a libray that you must install.  Note that the RX and TX pins are swapped in this code and the baud rate has been changed from 4800 to 9600.
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
/*
   This sample code demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
static const int RXPin = 3, TXPin = 4;  // changed from RXPin = 4 and TRPin = 3
static const uint32_t GPSBaud = 9600;  // baud rate changed from 4800

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  Serial.begin(115200);
  ss.begin(GPSBaud);

  Serial.println(F("FullExample.ino"));
  Serial.println(F("An extensive example of many interesting TinyGPS++ features"));
  Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion());
  Serial.println(F("by Mikal Hart"));
  Serial.println();
  Serial.println(F("Sats HDOP Latitude   Longitude   Fix  Date       Time     Date Alt    Course Speed Card  Distance Course Card  Chars Sentences Checksum"));
  Serial.println(F("          (deg)      (deg)       Age                      Age  (m)    --- from GPS ----  ---- to London  ----  RX    RX        Fail"));
  Serial.println(F("---------------------------------------------------------------------------------------------------------------------------------------"));
}

void loop()
{
  static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;

  printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
  printInt(gps.hdop.value(), gps.hdop.isValid(), 5);
  printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
  printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
  printInt(gps.location.age(), gps.location.isValid(), 5);
  printDateTime(gps.date, gps.time);
  printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2);
  printFloat(gps.course.deg(), gps.course.isValid(), 7, 2);
  printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
  printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6);

  unsigned long distanceKmToLondon =
    (unsigned long)TinyGPSPlus::distanceBetween(
      gps.location.lat(),
      gps.location.lng(),
      LONDON_LAT, 
      LONDON_LON) / 1000;
  printInt(distanceKmToLondon, gps.location.isValid(), 9);

  double courseToLondon =
    TinyGPSPlus::courseTo(
      gps.location.lat(),
      gps.location.lng(),
      LONDON_LAT, 
      LONDON_LON);

  printFloat(courseToLondon, gps.location.isValid(), 7, 2);

  const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon);

  printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6);

  printInt(gps.charsProcessed(), true, 6);
  printInt(gps.sentencesWithFix(), true, 10);
  printInt(gps.failedChecksum(), true, 9);
  Serial.println();
  
  smartDelay(1000);

  if (millis() > 5000 && gps.charsProcessed() < 10)
    Serial.println(F("No GPS data received: check wiring"));
}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(' ');
  }
  smartDelay(0);
}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }
  
  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }

  printInt(d.age(), d.isValid(), 5);
  smartDelay(0);
}

static void printStr(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  smartDelay(0);
}

 

Output Example 

The serial output on the Arduino terminal is shown here.  Make sure that you change the baud rate on the terminal to 115200.

FullExample.ino
An extensive example of many interesting TinyGPS++ features
Testing TinyGPS++ library v. 0.92
by Mikal Hart

Sats HDOP Latitude   Longitude   Fix  Date       Time     Date Alt    Course Speed Card  Distance Course Card  Chars Sentences Checksum
          (deg)      (deg)       Age                      Age  (m)    --- from GPS ----  ---- to London  ----  RX    RX        Fail
---------------------------------------------------------------------------------------------------------------------------------------
**** **** ********** *********** **** ********** ******** **** ****** ****** ***** ***   ******** ****** ***   70    0         0        
7    125  40.386100  -80.049896  251  02/15/2015 12:33:04 417  354.50 0.00   0.06  N     5987     49.38  NE    511   2         5        
7    125  40.386100  -80.049896  259  02/15/2015 12:33:05 424  354.30 0.00   0.22  N     5987     49.38  NE    953   4         9        
7    125  40.386096  -80.049896  267  02/15/2015 12:33:06 432  354.40 0.00   0.20  N     5987     49.38  NE    1395  6         13       
7    125  40.386096  -80.049896  277  02/15/2015 12:33:07 442  354.40 0.00   0.11  N     5987     49.38  NE    1836  8         16       
7    125  40.386096  -80.049896  286  02/15/2015 12:33:08 452  354.70 0.00   0.04  N     5987     49.38  NE    2277  10        19       
7    125  40.386096  -80.049896  292  02/15/2015 12:33:09 458  354.80 0.00   0.04  N     5987     49.38  NE    2718  12        23       
7    125  40.386096  -80.049896  303  02/15/2015 12:33:10 467  355.00 0.00   0.13  N     5987     49.38  NE    3159  14        25       
7    125  40.386100  -80.049896  312  02/15/2015 12:33:11 476  355.20 0.00   0.06  N     5987     49.38  NE    3600  16        30       
7    125  40.386100  -80.049896  319  02/15/2015 12:33:12 485  355.40 0.00   0.11  N     5987     49.38  NE    4042  18        34       

 

Software revision for LCD and 4800 baud   GPS_LCD_Display-v2.ino
The software below works well with the GPS unit and an LCD display - note that I had to reduce the baud rate of the GPS from 9600 to 4800 to avoid checksum errors.
The baud rate was changed using u-Center software and following the documentation steps to change baud rate.   NOTE:  I used View / Configuration View / PRT (Ports) -
after changing to 4800 it asked if I wanted to write the change.
(for the BangGood.com GPS use this program to change the baud rate - see notes in ratings page on BangGood.) 
 
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR    0x27 // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
int potPin = A3;    // select the input pin for the potentiometer
int potValue = 0;  // variable to store the value coming from the pot
byte buffer[10];
LiquidCrystal_I2C	lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);

/*
   This sample code demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
static const int RXPin = 3, TXPin = 4;
static const uint32_t GPSBaud = 4800;

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  lcd.begin (20, 4); //  <<----- My LCD was 16x2
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE); // Switch on the backlight
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  lcd.print("GPS Test Display");
 lcd.setCursor(0,1);
 lcd.print("d. bodnar 02-17-15");
 lcd.setCursor(0,2);
 lcd.print("Units = MPH & Feet");
  Serial.begin(115200);
  ss.begin(GPSBaud);

  Serial.println(F("FullExample.ino"));
  Serial.println(F("An extensive example of many interesting TinyGPS++ features"));
  Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion());
  Serial.println(F("by Mikal Hart"));
  Serial.println();
  Serial.println(F("Sats HDOP Latitude   Longitude   Fix  Date       Time     Date Alt    Course Speed Card  Distance Course Card  Chars Sentences Checksum"));
  Serial.println(F("          (deg)      (deg)       Age                      Age  (m)    --- from GPS ----  ---- to London  ----  RX    RX        Fail"));
  Serial.println(F("---------------------------------------------------------------------------------------------------------------------------------------"));
}

void loop()
{
  static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;

  printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
  printInt(gps.hdop.value(), gps.hdop.isValid(), 5);
  printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
  printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
  printInt(gps.location.age(), gps.location.isValid(), 5);
  printDateTime(gps.date, gps.time);
  printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2);
  printFloat(gps.course.deg(), gps.course.isValid(), 7, 2);
  printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
  printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6);


  unsigned long distanceKmToLondon =
    (unsigned long)TinyGPSPlus::distanceBetween(
      gps.location.lat(),
      gps.location.lng(),
      LONDON_LAT,
      LONDON_LON) / 1000;
  printInt(distanceKmToLondon, gps.location.isValid(), 9);

  double courseToLondon =
    TinyGPSPlus::courseTo(
      gps.location.lat(),
      gps.location.lng(),
      LONDON_LAT,
      LONDON_LON);

  printFloat(courseToLondon, gps.location.isValid(), 7, 2);

  const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon);

  printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6);

  printInt(gps.charsProcessed(), true, 6);
  printInt(gps.sentencesWithFix(), true, 10);
  printInt(gps.failedChecksum(), true, 9);
 // Serial.println();
 Serial.print(" 6 dig test "); Serial.println(gps.location.lat(), 6);
  smartDelay(1000);

  if (millis() > 5000 && gps.charsProcessed() < 10)
    Serial.println(F("No GPS data received: check wiring"));

  lcd.clear();
  float Satellites = gps.satellites.value();
  lcd.setCursor(0, 0); lcd.print("Sat.= "); lcd.print(Satellites, 0);
  lcd.setCursor(10, 0);
  float hdop = gps.hdop.value(); lcd.print("dop= "); lcd.print(hdop, 0);
  lcd.setCursor(0, 1);
  float SpeedM  = gps.speed.mps();
  lcd.print("Speed= "); lcd.print(SpeedM, 1);
  lcd.setCursor(0, 2);
  float altitude = gps.altitude.feet();

  lcd.print("alt.= "); lcd.print(altitude, 0);
  lcd.setCursor(0, 3);
  float latitude = gps.location.lat();
  lcd.print(latitude, 6);
  lcd.setCursor(10, 3);
  float longitude = gps.location.lng();
  lcd.print(longitude, 6);
 
}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i = flen; i < len; ++i)
      Serial.print(' ');
  }
  smartDelay(0);
}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i = strlen(sz); i < len; ++i)
    sz[i] = ' ';
  if (len > 0)
    sz[len - 1] = ' ';
  Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }

  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }

  printInt(d.age(), d.isValid(), 5);
  smartDelay(0);
}

static void printStr(const char *str, int len)
{
  int slen = strlen(str);
  for (int i = 0; i < len; ++i)
    Serial.print(i < slen ? str[i] : ' ');
  smartDelay(0);
}