TinyGPS

A Compact Arduino GPS/NMEA Parser

TinyGPS is designed to provide most of the NMEA GPS functionality I imagine an Arduino user would want – position, date, time, altitude, speed and course – without the large size that seems to accompany similar bodies of code.  To keep resource consumption low, the library avoids any mandatory floating point dependency and ignores all but a few key GPS fields.

Usage

To use, simply create an instance of an object like this:

#include "TinyGPS.h"
TinyGPS gps;

Feed the object serial NMEA data one character at a time using the encode() method. (TinyGPS does not handle retrieving serial data from a GPS unit.) When encode() returns “true”, a valid sentence has just changed the TinyGPS object’s internal state. For example:

#define RXPIN 3
#define TXPIN 2
SoftwareSerial nss(RXPIN, TXPIN);
void loop()
{
  while (nss.available())
  {
    int c = nss.read();
    if (gps.encode(c))
    {
      // process new gps info here
    }
  }
}

You can then query the object to get various tidbits of data. To test whether the data returned is stale, examine the (optional) parameter “fix_age” which returns the number of milliseconds since the data was encoded.

long lat, lon;
unsigned long fix_age, time, date, speed, course;
unsigned long chars;
unsigned short sentences, failed_checksum;

// retrieves +/- lat/long in 100000ths of a degree
gps.get_position(&lat, &lon, &fix_age);

// time in hhmmsscc, date in ddmmyy
gps.get_datetime(&date, &time, &fix_age);

// returns speed in 100ths of a knot
speed = gps.speed();

// course in 100ths of a degree
course = gps.course();

Statistics

The stats method provides a clue whether you are getting good data or not. It provides statistics that help with troubleshooting.

// statistics
gps.stats(&chars, &sentences, &failed_checksum);
  • chars – the number of characters fed to the object
  • sentences – the number of valid $GPGGA and $GPRMC sentences processed
  • failed_checksum – the number of sentences that failed the checksum test

Integral values

Values returned by the core TinyGPS methods are integral. Angular latitude and longitude measurements, for example, are provided in units of millionths of a degree, so instead of 90°30’00”, get_position() returns a longitude value of 90,500,000, or 90.5 degrees. But…

Using Floating Point

…for applications which are not resource constrained, it may be more convenient to use floating-point numbers. For these, TinyGPS offers several inline functions that return more easily-managed data. Don’t use these unless you can afford to link the floating-point libraries. Doing so may add 2000 or more bytes to the size of your application.

float flat, flon;

// returns +/- latitude/longitude in degrees
gps.f_get_position(&flat, &flon, &fix_age);
float falt = gps.f_altitude(); // +/- altitude in meters
float fc = gps.f_course(); // course in degrees
float fk = gps.f_speed_knots(); // speed in knots
float fmph = gps.f_speed_mph(); // speed in miles/hr
float fmps = gps.f_speed_mps(); // speed in m/sec
float fkmph = gps.f_speed_kmph(); // speed in km/hr

Date/time cracking

For more convenient access to date/time use this:

int year;
byte month, day, hour, minutes, second, hundredths;
unsigned long fix_age;

gps.crack_datetime(&year, &month, &day,
  &hour, &minute, &second, &hundredths, &fix_age);

Establishing a fix

TinyGPS objects depend on an external source, i.e. its host program, to feed valid and up-to-date NMEA GPS data. This is the only way to make sure that TinyGPS’s notion of the “fix” is current. Three things must happen to get valid position and time/date:

  1. You must feed the object serial NMEA data.
  2. The NMEA sentences must pass the checksum test.
  3. The NMEA sentences must report valid data. If the $GPRMC sentence reports a validity of “V” (void) instead of “A” (active), or if the $GPGGA sentence reports fix type “0” (no fix) then those sentences are discarded.

To test whether the TinyGPS object contains valid fix data, pass the address of an unsigned long variable for the “fix_age” parameter in the methods that support it. If the returned value is TinyGPS::GPS_INVALID_AGE, then you know the object has never received a valid fix. If not, then fix_age is the number of milliseconds since the last valid fix. If you are “feeding” the object regularly, fix_age should probably never get much over 1000. If fix_age starts getting large, that may be a sign that you once had a fix, but have lost it.

float flat, flon;
unsigned long fix_age; // returns +- latitude/longitude in degrees
gps.f_get_position(&flat, &flon, &fix_age);
if (fix_age == TinyGPS::GPS_INVALID_AGE)
  Serial.println("No fix detected");
else if (fix_age > 5000)
  Serial.println("Warning: possible stale data!");
else
  Serial.println("Data is current.");

Interfacing with Serial GPS

To get valid and timely GPS fixes, you must provide a reliable NMEA sentence feed. If your NMEA data is coming from a serial GPS unit, connect it to Arduino’s hardware serial port, or, if using a “soft” serial port, make sure that you are using a reliable SoftSerial library. As of this writing (Arduino 0013), the SoftwareSerial library provided with the IDE is inadequate. It’s best to use my NewSoftSerial library, which builds upon the fine work ladyada did with the AFSoftSerial library.

Library Version

You can retrieve the version of the TinyGPS library by calling the static member library_version().

int ver = TinyGPS::library_version();

Resource Consumption

Linking the TinyGPS library to your application adds approximately 2500 bytes to its size, unless you are invoking any of the f_* methods. These require the floating point libraries, which might add another 600+ bytes.

Download

The latest version of TinyGPS is available here: TinyGPS13.zip

Change Log

  1. initial version
  2. << streaming, supports $GPGGA for altitude, floating point inline functions
  3. also extract lat/long/time from $GPGGA for compatibility with devices with no $GPRMC
  4. bug fixes
  5. API re-org, attach separate fix_age’s to date/time and position.
  6. Prefer encode() over operator<<. Encode() returns boolean indicating whether TinyGPS object has changed state.
  7. Changed examples to use NewSoftSerial in lieu of AFSoftSerial; rearranged the distribution package.
  8. Greater precision in latitude and longitude.  Angles measured in 10-5 degrees instead of 10-4 as previously.  Some constants redefined.
  9. Minor bug fix release: the fix_age parameter of get_datetime() was not being set correctly.
  10. Added Maarten Lamers’ distance_to() as a static function.
  11. Arduino 1.0 compatibility
  12. Added satellites(), hdop(), course_to(), and cardinal()
  13. Improved precision in latitude and longitude rendering. get_position() now returns angles in millionths of a degree.

Acknowledgements

Many thanks to Arduino forum users mem and Brad Burleson for outstanding help in alpha testing this code. Thanks also to Maarten Lamers, who wrote the wiring library that originally gave me the idea of how to organize TinyGPS.  Thanks also to Dan P. for suggesting that I increase the lat/long precision in version 8.  Thanks to many people who suggested new useful features for TinyGPS, especially Matt Monson, who wrote some nice sample code to do so.

All input is appreciated.

Mikal Hart

Page last updated on August 31, 2013 at 7:00 pm
702 Responses → “TinyGPS”

  1. Guile

    15 years ago

    Hi Rusty,

    @Mikal – Great library you have here, really efficient and compact !

    @Rusty – I’m in need of the DOP information as well. I notice that you have ammended code that extracts this from the incoming GPGSA sentences. Would you mind sharing it with us?

    Cheers,
    Guile


  2. PhilG

    15 years ago

    I vote for better documentation in the library.

    I know writing documentation sucks, but it would most likely pay dividends in the decline in questions about how to use all the features…


  3. Andi

    15 years ago

    Hi Mikal,

    is there a chance to get the course_to from the wiring library in your library also?

    I´m trying to build a kind of analog compass with a servo not showing to north, but to some gps coordinates.
    So the course_to would be something really great :)

    Thank you very much

    Andi


  4. Mikal

    15 years ago

    Andreas, I think that and the distance_to functions would make great additions. When I make a new version I’ll probably include that. Thanks,

    Mikal


  5. Andi

    15 years ago

    Hi Mikal,

    thank you very much. For now I think I will make it with an additional compass module and a little bit of trigonometry :)

    Is a little bit more expensive, but perhaps claculating the course out of GPS isn´t that easy if you just move with 1-1,5m / sec, or even don´t move at all.

    So I´ll take a compass sensor for that.


  6. Natasha

    15 years ago

    Thanks a lot for the library!!

    I had a question though..
    The lat/lng returned only has two decimal points I need it to have 6 decimal points for better accuracy. What needs to modified in the library file? or any other suggestions…


  7. Mikal

    15 years ago

    Hi Natasha,

    TinyGPS does support 6+ decimal digits. Perhaps you are running into the fact that Arduino’s float printing routines default to only printing 2 digits.

    Mikal


  8. JoAnn Sullivan

    15 years ago

    Hi

    I’m new to Arduino based programing. I was able to get TinyGPS working with my unit but I need to read magnetic deviation in the $GPRMC sentence. Is there a fairly easy way to modify your library to retrieve this data?

    Thanks JoAnn


  9. sundownr

    15 years ago

    Hi Mike

    Great code… Thanks. It saved me a bunch time. May the big programmer in the sky bless you.


  10. newbee

    15 years ago

    Hi Mikal,

    thanks for your TinyGPS library!
    I am planning to parse a number of NMEA sentences for my boat and show it on a 8 x 24 display with an arduino UNO (Atmega328 chip). Can you help me with some advice, please:
    is it possible to parse more than the GPS sentences you implemented?
    How many sentences do you think I could parse maximally?
    I would need waypoint information (XTE, bearing to WP, distance to WP, and ETA to WP if possibly ETA of last WP of route), RMC and depth, speed through water, distance trip and time.
    Do you think it possible to implement this with the Atmega328 ?
    Thank you for any hints !
    Andreas


  11. Mikal

    15 years ago

    JoAnn,

    It’s not terribly hard. You’d have to modify the term_complete() function a bit and add some new member variables to store the new data. Then write an accessor function like getMagDev() or something. If I were you, I think I would look for something like “speed” and follow the logic for how that’s already implemented.

    Mikal


  12. Mikal

    15 years ago

    newbee–

    Thanks for the note.

    Adding a new NMEA field to TinyGPS is not that difficult and would add somewhere between 12-32 bytes to the memory footprint of the library. Given that you have 2K of RAM in the 328, I would guess that you could easily support the various fields you mention above.

    You and others have inspired me to think about extending TinyGPS! :) Send another note when you project gets underway. It sounds fascinating.

    Mikal


  13. newbee

    15 years ago

    Hello Mikal,

    I tested TinyGPS with your static example, but 2 of the NMEA sentences don’t get decoded properly, for instance I get a speed of 10000000, instead of the real speed. Also date and time don’t get decoded min the second sentence while the first sentence gets decoded all right.

    Could you tell what’s wrong?

    I will try to figure out how to decode NMEA along the lines you suggest above.

    Andreas


  14. Andi

    15 years ago

    @Andi

    Hi,
    knowing this isn´t a forum, perhaps the following page might help you with some of your problems, as it did for me :)

    Have a look at
    http://blog.deathpod3000.com/?p=207

    There Eric Moore is programming things like bearing etc. for waypoint navigation.

    Cheers,
    Andi


  15. Rose

    15 years ago

    Hi

    I have a GPS and an Arduino Mega
    I’ve done data logging from my GPS device to my laptop
    Now I would like to use TinyGPS to parse the NMEA data to the format that my
    Arduino can understand
    I’ve read through this entire page and the code in the .zip folders
    But I can barely understand anything
    Where should I start?


  16. Norbert

    15 years ago

    Good Day

    I was going through the code and I notice the NSS call is set to 4800. If your GPS is set to 57600 would this be a problem? Would I change the NSS to 57600? Can I not use the Serial call and just use NSS?


  17. Mikal

    15 years ago

    Rose,

    The basic idea is that every time you get a new character from your GPS device you send it to a TinyGPS object by calling

    gps.encode(c);

    If gps.encode() returns true, that means a complete NMEA “sentence” has been processed, which indicates the possibility that the gps object has new location information available.

    You can get that by calling gps.f_get_position().

    Mikal


  18. Mikal

    15 years ago

    Hello, Norbert,

    Yes, you need to make sure that the baud rates match. 4800 is commonly used for GPS; hence the example code. But devices do use other rates; some are even configurable. Be careful though. On Arduino 57,600 baud is sometimes a little difficult for software serial.

    Mikal


  19. Daniel

    15 years ago

    Hi there, Great site! I have a GPS-PA6B and have Tiny GPS running though the time is incorrect buy almost a day so I am assuming there are Time Zone settings but being a new to programing and the arduino i cannot find an entry to change the time zone to GMT +10 (Melbourne,Australia) Daylight Savings.

    I would also like to know if possible to set the Daylight Savings and automatically change over to no Daylight Saving time as I want to use it in an Alarm Clock and don’t want to wake up 1 Hour early or 1 hour late. :)

    Reading through the arduino forums I found someone said: GPS time is UTC (GMT). You need to add the offset from your local time to the hours (-5 for CDT at the moment).. but i have no idea where to set this offset.

    Thanks,
    Daniel


  20. Mikal

    15 years ago

    Hi Daniel,

    As you read, I don’t think it falls within the GPS purview to be aware of local time or daylight saving times. I think the best you’ll be able to do is capture the universal time and date and then manually make the adjustment for Melbourne. This shouldn’t be too difficult, as the daylight savings algorithm should be readily available. But you won’t get it from the GPS stream or from TinyGPS itself.

    Mikal


  21. Bill.jr

    15 years ago

    Hi,

    is there any way how to parse NMEA sentences by TinyGPS and also forward them to ftdi chip to PC for use with eg. navigation software?


  22. Mikal

    15 years ago

    Bill, it’s pretty easy to do what you just describe. Every time a character arrives from the GPS device, presumably via a NewSoftSerial port, you do three things:

    1. Retrieve the character with c = nss.read();
    2. Send it to TinyGPS for parsing: tinygps.encode(c);
    3. Send it out the hardware serial port to the PC: Serial.write(c);

    Make sure the baud rate of the Serial connection is at least as high as the NewSoftSerial connection.

    Mikal


  23. JW

    15 years ago

    Great library, how do I send latitude/longitutde of waypoints to the GPS to get a $GPWPL response in the NMEA sentance when I reach that lat/long?

    Basically when I import the data into Google Maps or whatever I want to see markers of where it stopped (like at a red light, etc)

    Thanks!


  24. Mikal

    15 years ago

    @JW,

    I read a little bit about sending $GPWPL to a GPS today. It seems interesting, but is out of the purview of TinyGPS, which is just an NMEA parser. Anyone have any code to help JW?

    M


  25. Miles

    15 years ago

    Hi Mikal,

    On using the distance_to() static function, could you give a brief example of how to use it?

    do i call gps::distance_to(TARGET_LAT, TARGET_LON, KM)?

    eg:

    if gps::distance_to(TARGET_LAT, TARGET_LON, KM) <= 2
    {
    //do stuff
    }

    ?

    Hope that's correct.. Otherwise, can you point out where I'd be going wrong? Haven't worked with c-style coding much before..

    Thanks :)


  26. Miles

    15 years ago

    Well, I figured distance_between out. Just for other people, it’s as follows:

    gps.distance_between(TARGET_LAT, TARGET_LON, MyLAT, MyLON)

    Hope this helps someone else. I was confused to say the least..


  27. Jakob B

    15 years ago

    Hello Mikal,

    i tried the gps example with my Arduino-Mega1280-Board but it doesn’t work. I find out that the problem is the nss libary with the mega. I heard that you have just a beta version of the nss libary that works with mega boards! Can you send me that beta libary?

    Best Regards, Jakob

    Ps: sorry for my english ;)


  28. Mikal

    15 years ago

    Hi Jakob–

    Please check http://arduiniana.org/2011/01/newsoftserial-11-beta/ and make sure you are aware that Soft RX only works on certain pins on the Mega. Good luck!

    Mikal


  29. Jakob B

    15 years ago

    Hi,

    i tried the NewSoftwareSerial-Libary 11, but unfortunaly it doesn’t work (no Data was received, perhaps because of the LVTTL – GPS-Receiver, but with HardwareSerial i received the nmea string!).

    I tried to make some changes in the tinygps-example to use my hardware serial pins. But the gps.encoder doesn’t understand what i want ;)

    Here are my changes in the example (test_with_gps_device.pde)

    #include

    TinyGPS gps;

    void gpsdump(TinyGPS &gps);
    bool feedgps();
    void printFloat(double f, int digits = 2);

    void setup()
    {
    Serial.begin(115200);
    Serial1.begin(38400);


    }

    bool feedgps()
    {
    while (Serial1.available())
    {
    if (gps.encode(Serial1.read()))
    return true;
    }
    return false;
    }

    I get nmea strings when i print Serial1.read(), so the hardware works. But gps.encode doesn’t returns true.

    Can you help me with an example that works with hardware serial pins?


  30. Jakob B

    15 years ago

    Sorry!
    It works with hardware serial!

    i just need a valid gps signal, i thougt i just need the nmea string.


  31. Kevin

    15 years ago

    Hi Mikal, I really appreciate the libraries you have created. I am working on a vehicle for the SparkFun autonomous vehicle contest. I’ve noticed that every time a GPS set of data is retrieved by TinyGPS my steering servo pulse is affected. The steering twitches significantly a higher pulse length. I am guessing that since TinyGPS is using an interrupt to retrieve the data it is affecting the pulse length (using writeMicroseconds). Is there any way to get around this?


  32. Mikal

    15 years ago

    Kevin,

    TinyGPS has no hardware component–it’s strictly a parser–but what you’re probably seeing is an incompatibility between NewSoftSerial (which disables interrupt during a TX or RX) and Servo, which requires interrupts to work smoothly. If so, my solution to the problem is to use the old version of the servo library, which used a different technology. I’ve posted it on this site as PWMServo.

    Thanks for the note…

    Mikal


  33. Federico

    15 years ago

    Hi Mikal.
    I am struggling from a few days with a MT3329 GPS receiver connected to an Arduino Uno, and whose data are decoded by tinyGPS.
    Everything (almost) works. Only the date is not readed:
    ——-

    Acquired Data
    ————-
    Lat/Long(10^-5 deg): 4184582, 1245252 Fix age: 565ms.
    Lat/Long(float): 41.84582, 12.45252 Fix age: 571ms.
    Date(ddmmyy): 0 Time(hhmmsscc): 22262400 Fix age: 579ms.
    Date: 0/0/2000 Time: 22:26:24.0 Fix age: 585ms.
    Alt(cm): 10390 Course(10^-2 deg): 999999999 Speed(10^-2 knots): 999999999
    Alt(float): 103.90 Course(float): 10000000.00
    Speed(knots): 10000000.00 (mph): 11507795.00 (mps): 5144444.00 (kmph): 18520000.00
    Stats: characters: 1350 sentences: 10 failed checksum: 4
    ————-

    I do use the test_with_gps_device sketch.
    Needless to say, on a serial terminal program the strings are correct, including the date.
    Many thanks,

    –Federico


  34. Mikal

    15 years ago

    Federico, can you please send me about 10 seconds worth of NMEA sentences from your device? I’d like to take a look.

    Mikal


  35. Mike

    15 years ago

    When I run the example code from SoftwareSerial I get a nice stream of data from my GPS unit. But, when I run the TinyGPS example code, I get:

    Testing TinyGPS library v. 10
    by Mikal Hart

    Sizeof(gpsobject) = 103

    and that’s it. It doesn’t display any more.

    Please help.

    Mike


  36. Mikal

    15 years ago

    Mike, that probably means you are reading from the wrong pins. Make sure you modify the code to declare the NewSoftSerial object with the correct pins.

    You’re not running on a Mega, are you?

    Mikal


  37. Mike

    15 years ago

    I am using the same exact pins for both examples. I’ve even tried swapping the wires around – nothing.


  38. Mike

    15 years ago

    No I am using an Uno. Also tried it on a Duemilanove. Also tried different pins. Nothing works.

    As soon as I go back to the NewSoftSerial example it works fine and shows the NMEA string on the screen. When I go back to the TinyGPS example, nothing.


  39. Willem

    15 years ago

    Hi Mikal!

    I’ve been using your library, and I’ve added two(well three if you include float) functions. satellites() returns the number of satellites in view, and hor_acc() returns the current horizontal dilution.

    This allows me to get some handle on the accuracy of the information coming out of the library when using floating point.

    Hope this is useful!

    http://www.secretbatcave.co.uk/TinyGPS.zip

    -Willem


  40. Mikal

    15 years ago

    Well, it’s failing in one of two places. Either you’re not getting the data out of the GPS, or the data is not getting parsed. It’s easy to tell which. Once you’ve read the character from the (soft) serial port, display it on a display or send it to the serial port or something. Whether or not you can see this data will tell you where the failure is.


  41. Nicholas

    15 years ago

    Hi Mikal,

    I’ve just recently begun using your TinyGPS library to read data from a Rockwell GPS of mine. Testing the device with the example sketch in the library, latitude, longitude, and time come through just fine, but I’m getting huge errors in calculation of speed and altitude. The date is also incorrect. Is there anything I could be doing wrong?

    Here is a read of the GPS data:

    Acquired Data
    ————-
    Lat/Long(10^-5 deg): 3403233, -11839735 Fix age: 273ms.
    Lat/Long(float): 34.03233, -118.39735 Fix age: 278ms.
    Date(ddmmyy): 0 Time(hhmmsscc): 9002622 Fix age: 297ms.
    Date: 0/0/2000 Time: 9:0:26.22 Fix age: 302ms.
    Alt(cm): -42900 Course(10^-2 deg): 999999999 Speed(10^-2 knots): 999999999
    Alt(float): -429.00 Course(float): 10000000.00
    Speed(knots): 10000000.00 (mph): 11507795.00 (mps): 5144444.00 (kmph): 18520000.00
    Stats: characters: 5814 sentences: 51 failed checksum: 0

    Thank you,
    -Nicholas


  42. JP

    15 years ago

    I am having a similar issue with the same GPS. I need to first try it outdoors as I do not get a steady blue LED to indicate the GPS acquired satellites. Looks like the code waits for the GPS to first acquire data before you can see anything on the serial monitor. Is this correct? I only see this:

    Testing TinyGPS library v. 10
    by Mikal Hart

    Sizeof(gpsobject) = 103

    Nothing else so I think my GPS is just not putting out data yet. It’s only blinking at me! Is this a correct assumption.


  43. JP

    15 years ago

    I did some more checking and according to my o-scope the TX from the GPS is just plugging away sending data. I suspect it’s in the binary format and your expecting NMEA formatted structures. I will check to see how to force it into the NMEA format and try again.


  44. Mikal

    15 years ago

    Thanks, JP. You’re right. Unless the GPS is transmitting in ASCII (NMEA), TinyGPS will be completely befuddled.


  45. Mikal

    15 years ago

    Nicholas,

    I should rewrite the example to make it clear, but the values you see indicate that TinyGPS hasn’t received any valid date, altitude, or course information. Does your device transmit GPRMC sentences?

5 Trackbacks For This Post
  1. A fully functional Arduino GPS receiver | Embedded projects from around the web

    […] uses a TinyGPS library for easier GPS shield access. So device can be used as normal GPS tracking device the only […]

  2. Moving Forward with Arduino – Chapter 17 – GPS « t r o n i x s t u f f

    […] point you will need to install two libraries into the Arduino software – NewSoftSerial and TinyGPS. Extract the folders into the libraries folder within your arduino-001x […]

  3. Shield Driven Arduino GPS

    […] sketch uses Mikal Hart’s excellent TinyGPS library and includes code from the ArduPilot Project for […]

  4. GPS bővítés « Nikon D5000 DSLR

    […] 2 […]

  5. The Maritime Geocaching Association » Build Your Own Steampunk GPS

    […] attached code should pretty much speak for itself. Using the TinyGPS library, your current position is taken and the direction to the final location is calculated. […]

Leave a Reply