TinyGPS

A Compact Arduino GPS/NMEA Parser

Note: TinyGPS 11 is now Arduino 1.0 compatible.

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 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 10-5 degrees, so instead of 90°30′00″, get_position() returns a longitude value of 9050000, 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, 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: TinyGPS11.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

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.

All input is appreciated.

Mikal Hart

Page last updated on January 28, 2012 at 12:41 am
218 Responses → “TinyGPS”

  1. Andreas P.

    2 years ago

    Hello. I’m having trouble reading the altitude.
    This is what it says:
    Alt(cm): 999999999
    Arduino Duemilanove with ATMega328
    Its a GPS-shield from adafruit.
    Everything else is perfect! :)

    Thanx!


  2. Mikal

    2 years ago

    Andreas, do you mean to say that you are getting lat/long and time, etc., but altitude is broken? The 9’s mean that TinyGPS doesn’t think it ever sees an altitude field. Perhaps your GPS doesn’t display altitude? The best way to tell is to capture some of the raw serial output from the GPS. Wait until you are getting a nice lat/long and then capture (print) every byte that comes in. That should tell you where the problem is.

    Mikal


  3. MACH4

    2 years ago

    I’m getting similar problems (all 9’s but with heading and speed.
    Other readouts seem accurate.
    GPS ripped from disused unit, atmega328. serial terminal.

    Using feedGPS() 3 times per loop at 5 secs interval…

    Full stream gives all correct data, but no checksum check.

    Can anyone suggest what to try next? thanks!

    MACH4


  4. Mikal

    2 years ago

    Hey, Mach4,

    Can you grab about 5 seconds worth of raw output and post it? Are you using soft serial or Serial? What baud rate? What does “no checksum check” mean?

    Mikal


  5. MACH4

    2 years ago

    Hi Mikal,

    I didn’t thank you for these libs, much appreciated, very easy to use!

    New to programming these uProcessors so please bear with me if I misunderstand something!

    I searched various code examples, below is shown the result of one such example using a full dump.

    GPS baud rate 58400

    —————
    Time in UTC (HhMmSs): 192016.000
    Status (A=OK,V=KO): A
    Latitude: 5023.1962
    Direction (N/S): N
    Longitude: 00406.0090
    Direction (E/W): W
    Velocity in knots: 1.27
    Heading in degrees: 311.84
    Date UTC (DdMmAa): 061109
    Magnetic degrees:
    (E/W):
    Mode: A
    —————
    103671808684714451494946550103671807171

    Thanks for any guidance, No doubt it is me that’s doing something wrong!


  6. Mikal

    2 years ago

    Mach4–

    What I’m really looking for is the raw NMEA sentences that begin with $G…. You can see an example of how these look in one of the TinyGPS sample apps. This is the raw data as it comes from the GPS module, just before it is sent to TinyGPS::encode.

    Mikal


  7. leucos

    2 years ago

    Mikal,

    Thanks for the library. Do you plan to make a port for avr-libc one of these days ?


  8. leucos

    2 years ago

    Me again. Well in fact, besides millis() and byte, the port is quite straightforward.
    Thanks !


  9. ircmaxell

    2 years ago

    Just one quick addition request. I made a tiny modification to the library to enable me to check the precision of the signal. I added a pair of methods, hdop() and f_hdop(). When a GPGGA signal is parsed, the result is stored in the _hdop member variable. The variable is initialized as 0. That enables me to check the hdop value to get an idea of the accuracy of the reading. If the object hasn’t parsed a GPGGA sentence yet, it returns 0 (A technically invalid value, but passes simple accuracy checks). The value occurs at position 8 (208 in the switch statement) of the GPGGA sentence.

    Then, in my code I just do:

    if(gps.f_hdop() <= 10) {
    //Good, clean signal
    } else {
    //Dirty signal, wait for cleaner one
    }

    http://en.wikipedia.org/wiki/Dilution_of_precision_%28GPS%29

    Otherwise, great job and thank you for this library!!!

    Anthony


  10. Mikal

    2 years ago

    Very nice, ircmaxell…! I’m am working through some design ideas for making the library more general, and this is a good start.

    Cheers,

    Mikal


  11. Tyler

    2 years ago

    Awesome library and great project idea with your puzzle box. I’m currently building one and have been tinkering all day because I couldn’t get information from my GPS via the TinyGPS library. Once I saw the examples you included in your source package, everything worked, and I realized my problem was that I wasn’t including an “nss.begin(4800)” in my sketches (I was just trying to copy and paste your top code example). Now it works great! Fantastic library and awesome project.


  12. Ophir Ronen

    2 years ago

    Hi Mikal,

    First off, thanks for your work on tinyGPS, it is very useful.

    I’m using tinyGPS with the EM-406a with an arduino board and am seeing some unexpected behavior in the data reported. Namely, the course returned seem to be varying widely even though the EM-406a is in a static configuration (pointing in the same direction).

    By way of example, here is the output from your gpsdump:

    Acquired Data
    ————-
    Lat/Long(float): 32.16254, 34.85036 Fix age: 213ms.
    Date: 12/15/2009 Time: 6:57:52.0 Fix age: 235ms.
    Alt(float): 99.90 Course(float): 174.20
    Speed(knots): 1.02 (mph): 1.17 (mps): 0.52 (kmph): 1.89
    Stats: characters: 44601 sentences: 212 failed checksum: 5
    ————-
    wp_la,/wp_long: 47.69284, -122.38206
    flat,flon: 32.16254, 34.85036
    ————-




    Acquired Data
    ————-
    Lat/Long(float): 32.16250, 34.85035 Fix age: 659ms.
    Date: 12/15/2009 Time: 6:58:40.0 Fix age: 698ms.
    Alt(float): 91.40 Course(float): 77.56
    Speed(knots): 0.10 (mph): 0.12 (mps): 0.05 (kmph): 0.19
    Stats: characters: 55564 sentences: 306 failed checksum: 7
    ————-

    Thank you!

    -Ophir


  13. Mikal

    2 years ago

    Ophir–

    The EM-406A is only accurate to within +/-10m and it is not at all unusual to see tiny variance like this. These data seem correct to me. It’s been my experience as well that even when at rest, the device may appear to wander slightly at 1-2mps.

    Mikal


  14. Ophir Ronen

    2 years ago

    Mikal,

    Thanks for the quick response. I’m actually not referring to the fix data being inaccurate in terms of actual position. What I’m interested in is the variation in COURSE. This is causing problems in my project which is relying on calculation of the relative bearing to a waypoint based on the calculated bearing between current position (lat/long) and the destination (lat/long).

    Receiving random course headings even though the gps is sitting statically on the table is causing my calculated relative bearing to the destination waypoint to be worthless.

    thx.

    -OPHIR


  15. Ophir Ronen

    2 years ago

    My post seems overly complex. Simply put, have you ever seen issues with intermittent course headings while the EM-406a is sitting on a table. Lat/Lon readings are correct.

    Thanks!

    -Ophir


  16. Mikal

    2 years ago

    Ophir,

    I didn’t explain very well. If you look at the latitude/longitude values that you originally posted, you’ll see that they vary very slightly. This is because, even when the EM-406A is still, inaccuracies make it seem as if it is jumping around randomly within a small radius circle. Naturally, if the GPS thinks it is jumping around randomly, the course values will appear random too. At one moment you’ll be on a 171.1 degree heading and the next at 227.6. Mine does the same thing. It’s perfectly normal. I guess I would just ignore any course values when the ground speed is less than, say, 2 meters per second.

    Mikal


  17. Ophir Ronen

    2 years ago

    Ahhh… Got it.

    Thanks Mikal both for the answer and the sweet lib. :)


  18. Andreas Gazis

    2 years ago

    Great job, thanks a lot. Saved me a hell of a lot of coding.

    I have made one addition which might be of interest to this library, a function for returning bearing and range from a waypoint to another. Unfortunately it’s heavy, it adds 1622 to a sketch as it depends on floats, cosine and atan. The equations are taken from CosineKitty’s post at:

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

    and does not take into account Earth’s curvature (so gets inaccurate at really long distances). I have hard coded a value for Earth radius at the very start of the function, if you want to be slightly more precise, change that to whatever is appropriate to your location.

    The way I added it to the library is to add the following line to TinyGPS.h:

    void wpt_comparison(long lat1, long lon1, long lat2, long lon2, float *bearing_p, float *range_p);

    and then add the function itself to the end of TinyGPS.cpp. I hope this is the right way of going about it. You use the function by feeding it lat, lon pairs of waypoints and it stores the results in the passed addresses of two float variables for bearing and range. Bearing is FROM waypoint 1 TO waypoint 2.

    Here’s the function:

    void TinyGPS::wpt_comparison(long lat1, long lon1, long lat2, long lon2, float *bearing_p, float *range_p) {

    long earth_radius = 6370073; // in metres at south europe latitude, change as appropriate
    float d2r = 1.745329252e-7; // (PI/180) for degrees to rad conversion, further divided by 100000 because the coords passed are originally in integer 100000ths of a degree

    // convert to radians
    float lat1r = d2r * (float)lat1;
    float lon1r = d2r * (float)lon1;
    float lat2r = d2r * (float)lat2;
    float lon2r = d2r * (float)lon2;

    // get the range components, NOTE!!! linear calculation, does not take into account earth’s curvature, OK approximation for short distances.
    float delta_x = cos((lat2r + lat1r) / 2) * (lon2r – lon1r) * earth_radius;
    float delta_y = (lat2r – lat1r) * earth_radius;

    // get the distance between the waypoints, again linear approximation
    *range_p = sqrt(delta_x*delta_x + delta_y*delta_y);

    // bearing calculation, check trivial cases first
    if (delta_x == 0 && delta_y == 0) { // waypoints are the same
    *bearing_p = NAN;
    return;
    }
    if (delta_x == 0 && delta_y != 0) { // waypoints are separated only in the N/S direction
    if (delta_y > 0) {
    *bearing_p = 0;
    } else {
    *bearing_p = PI;
    }
    } else if (delta_y == 0 && delta_x != 0) { // waypoints are separated only in the E/W direction
    if (delta_x > 0) {
    *bearing_p = PI/2;
    } else {
    *bearing_p = 3*PI/2;
    }
    } else { // more usual case, we have non zero components on both axes
    *bearing_p = atan(delta_x / delta_y); // gets a provisional value for the bearing, need to check its possible cases as info gets lost in the division
    // first quadrant case (++) we don’t need to change the value so no check
    if (delta_y < 0) { // this case is for both 2&3 quadrants, (+-) returns a -ve angle (0-pi/2), adding pi brings it to 2nd quadrant…
    *bearing_p += PI; // … (–) returns a +ve (0-pi/2) angle, adding PI brings it to 3rd quadrant
    } else if (delta_x < 0 && delta_y > 0) {
    *bearing_p += 2*PI; // 4th quadrant case (-+) returns -ve angle (0-pi/2), adding 2*pi brings it to 4th quadrant
    }
    }
    *bearing_p = 180 * (*bearing_p) / PI; // convert to degrees
    }


  19. .yo.

    2 years ago

    I use Arduino 0017 IDE.
    I compile your test_with_gps_device example and i never get “Acquired Data” information.

    So I change the feedgps() to check serial reception like this :

    char c = nss.read();
    Serial.print ( c, BYTE );
    if ( gps.encode(c) )

    Like this i get all chars from gps device but never “Acquired Data”

    Could you explain me why Mikal ?

    Thanks. .YO.


  20. .yo.

    2 years ago

    Sorry Mikal for previous posts,

    I find why. My gps device is inside and isn’t fixed.
    So _gps_data_good is false and nothing goes out.

    It would be nice if encode() instead true or false return a little more information like NMEA sentence receives GGA/RMC/other and if gps is fixed or not.

    I’ll try a mod and post it here.

    .YO.


  21. Mikal

    2 years ago

    That’s a good idea .yo.


  22. Andreas W

    2 years ago

    hi, i tried to use it with my HI-203E. I connected the Tx to Pin2 and the Rx to Pin3 and used your newsoftserial lib as described. When I start your example code nothing happens (the gps mouse says it has connection by blinking with 1 Hz). I tried to analyse what’s coming from the mouse, by reading the SoftSerial and transfering it to the VirtualCom over the usb-port, but all i can see doesn’t make any sense. Do you have an idea, where the Problem is?

    Greetz from Germany


  23. Andreas W

    2 years ago

    Here is an example what i get when i display the data with:

    Serial.print ( nss.read(), BYTE );

    §›—™§™Ÿ§—§›§Ÿ—‘§™—«‘yåë·q_qYS§™§›§§§››§““§—“§‘§›§™•§§›§•§›‘§§›•§
    §Ÿ›§«‘såë·q_qYS§™§™§§›“§Ÿ‘§Ÿ™Ÿ§§›™§Ÿ‘§§™“§™§Ÿ—§Ÿ—§——«—åë[µU»¶v6¬ZkÖí
    6vÖmÚö–Ö6Öövû6vöö6vÖ¶öÖíöö6ì×­»•ë·q_qq}§—•›——£•§—•£Ÿ“—§c§ŸŸ‘™£Ÿ“§u§§
    (snip)
    §™›§‘§››§™§›Ÿ§—“§›—™§™§—
    §›§Ÿ—‘§™›«‘}åë·q_qYS§™§›§§§››§““§—“§‘§›§™•§§›§•§›‘§§›•§§Ÿ›§«‘såë·
    q_qYS§™§™§§›“§Ÿ‘§Ÿ™Ÿ§§›™§Ÿ‘§§™“§™§Ÿ—§Ÿ—§——«—åë[µU»¶6¬ZkÖí6VÖvëö–Ö6Öö
    vû6vöö6vÖ¶öÖíöö6ì×­‹ë·q_qq}§—•›—‘£•§—•£Ÿ“•§c§ŸŸ‘™£Ÿ“§u§§Ÿ“§Ÿ£§ŸŸ™Ÿ
    £™§e§ŸŸ—‘£™§e§§«•uåë·q_qY}§}§™§§§—§™›§§›Ÿ§›™§§§§§§Ÿ


  24. Mikal

    2 years ago

    Andreas, just a guess here, but check to make sure that the TX line on the GPS is connected to the *RX* line on the Arduino. It’s a common mistake to connect TX to TX. Also, your capture seems to suggest a possible baud rate mismatch, so check that to make sure. Lastly, make sure that the GND from the GPS is connected to the Arduino GND. Beyond that, hmmm… well make sure that your GPS is transmitting NMEA. Some GPS devices can be configured to transmit binary packets.

    And greeting to you from Texas, USA.

    Mikal


  25. Andreas W

    2 years ago

    Thanks for the fast answer, i know that the rx at the arduino has to be connected to the tx at the gps mouse, the baudrade is set to 4800, as described in the manual of the gps reciever, the protocol:

    NMEA-0183 V3.01
    GPGGA, GPGLL, GPGSA, GPGSV,
    GPRMC, GPVTG, GPZDA
    4800 baud, 8, N, 1

    should also be right. I hope, that the gps-reciever is not damaged, but i don’t know what else i could check.


  26. Andreas W

    2 years ago

    here i am again, i just checked the manual and opened the gps-reciever. The Problem could be, that i have n rs232 and not an TTL level-signal.


  27. Andreas W

    2 years ago

    okay thanks for your help, i changed the 2 wires at the connector to get the TTL signal instead of the RS232 signal and now it works quiet fine!


  28. Peter P

    2 years ago

    Hi Mikal,

    I had a few troubles with the EM-406A and your library and I’m unsure where the problem lies. I connected the GPS unit to the RX/TX lines of my Duemilanove(having removed the atmega and using Sparkfun’s GPS Shield) allowing my computer to communicate directly using the onboard FTDI converter.

    The GPS unit seems to aquire a fix denoted by it’s led flashing but I recieve no NMEA strings.. even using your latest NewSoftSerial and Tiny GPS on the atmega to parse and forward the data has no effect. I cant for the life of me work out what I’m missing or what I’ve done wrong, your advice would be greatly appreciated.

    Cheers,

    Peter


  29. Mikal

    2 years ago

    Peter, if you write a simple sketch which simply relays bytes read from the GPS (via NewSoftSerial) to the hardware serial port, you ought to be able to read the NMEA strings directly from your computer. If you can’t, make sure that all the grounds are connected and that you have the EM-406A’s TX connected to the NewSoftSerial RX and vice-versa. It’s a common mistake to connect TX to TX and RX to RX. You should receive NMEA strings even if the GPS doesn’t have a fix — the data will show no fix.


  30. Roland

    2 years ago

    This looks like a great library, thank you for writing it. I was wondering what the license (if any) is. I would like to use it in a commercial, for profit project and was wondering if this is ok?


  31. Mikal

    2 years ago

    Hi Roland,
    The license appliced to TinyGPS is the GNU “Lesser General Public License” (LGPL).
    Mikal


  32. Matt Ball

    1 year ago

    Thanks for the great library! We got it working on the EM-406A tonight; only thing that tripped us up was not having a GPS lock. We were expecting some kind of ‘no signal’ message but the example sketch doesn’t print data sent when there’s no lock (guess we missed that..). We went outside for a few minutes with a laptop and in came the data. Thanks again!


  33. Mikal

    1 year ago

    @Matt–

    Yeah, the first time you fire up the EM-406A it takes a few minutes, but subsequent runs tend to be quicker. You’re probably right. I should rewrite that sketch to show that it’s trying to get a good fix. Glad it’s working for you and thanks for the nice comment.

    Mikal


  34. Alex

    1 year ago

    Hi, thanks for the awesome library. I just got it working on the EM-406A.

    One issue I’m having is that it gps.encode() is returning true twice in a row for the same set of data, so its outputting two sets of the same data before updating to new stuff. Here is my code:

    #include
    #include
    #include

    NewSoftSerial nss(2,3);

    TinyGPS gps;

    float lat,lon;
    float spd, alt, heading;
    unsigned long fix_age,date,time;

    void setup() {
    Serial.begin(57600);
    nss.begin(4800);
    }

    void loop(){
    //Serial.println(“test”);

    while (nss.available()){
    int c = nss.read();
    //Serial.print(c, BYTE);
    if (gps.encode(c))
    {
    gps.f_get_position(&lat, &lon, &fix_age);
    gps.get_datetime(&date, &time, &fix_age);
    alt = gps.f_altitude();
    spd = gps.f_speed_mph();
    heading = gps.f_course();
    //Serial.println(‘test’);
    Serial.print(“lat: “);
    Serial.print(lat, 5);
    Serial.print(” lon: “);
    Serial.print(lon, 5);
    Serial << ” date: ” << date << ” time: ” << time << “\n”;
    Serial << “alt: ” << alt << ” speed: ” << spd << ” course: ” << heading << “\n”;
    }
    }
    }

    Here is the output from the program:

    lat: 35.91056 lon: -79.05329 date: 180210 time: 20593800
    alt: 161.10 speed: 0.18 course: 315.76
    lat: 35.91056 lon: -79.05329 date: 180210 time: 20593900
    alt: 161.30 speed: 0.18 course: 315.76
    lat: 35.91056 lon: -79.05329 date: 180210 time: 20593900
    alt: 161.30 speed: 0.17 course: 165.49
    lat: 35.91056 lon: -79.05329 date: 180210 time: 20594000
    alt: 161.50 speed: 0.17 course: 165.49
    lat: 35.91056 lon: -79.05329 date: 180210 time: 20594000
    alt: 161.50 speed: 0.18 course: 351.40
    lat: 35.91055 lon: -79.05329 date: 180210 time: 20594100
    alt: 161.70 speed: 0.18 course: 351.40
    lat: 35.91055 lon: -79.05329 date: 180210 time: 20594100
    alt: 161.70 speed: 0.35 course: 306.13
    lat: 35.91055 lon: -79.05329 date: 180210 time: 20594200
    alt: 162.00 speed: 0.35 course: 306.13
    lat: 35.91055 lon: -79.05329 date: 180210 time: 20594200
    alt: 162.00 speed: 0.14 course: 115.45

    Here is the raw output from the EM-406A

    $GPRMC,205758.000,A,3554.6413,N,07903.2059,W,0.21,277.32,180210,,*12
    $GPGGA,205759.000,3554.6415,N,07903.2062,W,2,09,1.5,208.4,M,-32.8,M,2.8,0000*48
    $GPGSA,A,3,14,26,09,22,03,27,21,06,18,,,,2.3,1.5,1.7*33
    $GPGSV,3,1,12,18,71,019,16,26,64,278,21,21,58,189,37,09,55,076,33*72
    $GPGSV,3,2,12,27,48,061,23,22,47,313,26,24,37,164,,14,33,252,28*79
    $GPGSV,3,3,12,15,16,055,,06,10,278,18,19,05,313,,03,05,286,28*76
    $GPRMC,205759.000,A,3554.6415,N,07903.2062,W,0.20,264.63,180210,,*1A
    $GPGGA,205800.000,3554.6416,N,07903.2064,W,2,09,1.5,208.6,M,-32.8,M,0.8,0000*4E
    $GPGSA,A,3,14,26,09,22,03,27,21,06,18,,,,2.3,1.5,1.7*33
    $GPRMC,205800.000,A,3554.6416,N,07903.2064,W,0.11,273.88,180210,,*1D
    $GPGGA,205801.000,3554.6418,N,07903.2066,W,2,09,1.5,208.6,M,-32.8,M,0.8,0000*43
    $GPGSA,A,3,14,26,09,22,03,27,21,06,18,,,,2.3,1.5,1.7*33
    $GPRMC,205801.000,A,3554.6418,N,07903.2066,W,0.11,37.14,180210,,*27
    $GPGGA,205802.000,3554.6419,N,07903.2067,W,2,09,1.5,208.7,M,-32.8,M,0.8,0000*41
    $GPGSA,A,3,14,26,09,22,03,27,21,06,18,,,,2.3,1.5,1.7*33
    $GPRMC,205802.000,A,3554.6419,N,07903.2067,W,0.27,44.42,180210,,*26
    $GPGGA,205803.000,3554.6421,N,07903.2068,W,2,09,1.5,208.5,M,-32.8,M,1.8,0000*47
    $GPGSA,A,3,14,26,09,22,03,27,21,06,18,,,,2.3,1.5,1.7*33
    $GPRMC,205803.000,A,3554.6421,N,07903.2068,W,0.12,306.12,180210,,*15
    $GPGGA,205804.000,3554.6421,N,07903.2068,W,2,09,1.5,208.2,M,-32.8,M,0.8,0000*


  35. Mikal

    1 year ago

    Alex,

    A return value of “true” from encode() doesn’t mean “your position has changed”. It simply means that TinyGPS has successfully parsed a new GPGGA or GPRMC sentence, so the position might have changed. The EM-406A doesn’t update its position with every sentence, as you can see by examining sentences #2 and #7 above. Both report the same position (3554.6415N by 07903.2062W), so TinyGPS will too. If you want to do something only when the position actually changes, you’ll have to detect this in your own code.

    Mikal


  36. Phlogi

    1 year ago

    Does this library work on the arduino mega? (ATmega1280 )

    Thanks


  37. Mikal

    1 year ago

    Phlogi–

    TinyGPS is hardware agnostic. It should work on any Arduino-compatible platform.

    Mikal


  38. AlMadrid

    1 year ago

    Hi, thank you for this great library.

    It’s strange, it works fine for me, but only if I delete “if (gps.encode(nss.read()))”. Any idea?

    The GPS data that is sending my device is something like this:

    $GPGSV,3,2,10,17,33,311,43,28,16,254,38,19,14,155,21,13,07,190,17*7A
    $GPGSV,3,3,10,31,06,079,33,14,05,032,15*77
    $GPRMC,113109.009,A,4020.0167,N,00345.9586,W,000.0,355.0,230210,,,A*79


  39. Mikal

    1 year ago

    AlMadrid–

    Hmm… I wouldn’t expect it to work at all in that case. The encode() method is the only way that the GPS object gets the data feed. Are you sure?

    Mikal


  40. AlMadrid

    1 year ago

    You’re right, it doesn’t work. The data has no sense.

    I think the problem is this: When I use SoftwareSerial, I receive valid NMEA sentences, but when I use NewSoftSerial, I just receive senseless characters. Here you can see these two diferents ways. First with :

    #include <NewSoftSerial.h>
    int rxPin = 2;
    int txPin = 3;
    SoftwareSerial gpsSerial = SoftwareSerial(rxPin, txPin);

    void setup()
    {
    pinMode(rxPin, INPUT);
    pinMode(txPin, OUTPUT);
    Serial.begin(9600);
    gpsSerial.begin(9600);
    }

    void loop()
    {
    char c = gpsSerial.read();
    Serial.print(c);
    }

    Now the same with :

    #include
    int rxPin = 2;
    int txPin = 3;
    NewSoftSerial gpsSerial = NewSoftSerial(rxPin, txPin);

    void setup()
    {
    pinMode(rxPin, INPUT);
    pinMode(txPin, OUTPUT);
    Serial.begin(9600);
    gpsSerial.begin(9600);
    }

    void loop()
    {
    while (gpsSerial.available())
    {
    char c = gpsSerial.read();
    Serial.print(c);
    }
    }


  41. Mikal

    1 year ago

    AlMadrid–

    I hate to say, but this code works fine for me — when I change the baud rates to 4800. Is 9600 correct? Most GPS units I’ve encountered use 4800 or 38400. Make sure the serial monitor is configured to the right baud.

    Also, you don’t need to set the pinMode in setup(). NewSoftSerial does that automatically.

    Mikal


  42. AlMadrid

    1 year ago

    It’s very strange. The only thing that changes in my two programs are:

    This header in one of them:

    #include
    SoftwareSerial gpsSerial = SoftwareSerial(2, 3);

    and this one in the other:

    #include
    NewSoftSerial gpsSerial = NewSoftSerial(2, 3);

    The rest of the code is just the same:

    void setup()
    {
    Serial.begin(9600);
    gpsSerial.begin(9600);
    }

    void loop()
    {
    char c = gpsSerial.read();
    Serial.print(c);
    }

    The difference: The first one works fine, and the second one doesn’t. This is what I receive in the first one:

    $GPGGA,102646.418,4020.0125,N,00345.9545,W,1,06,2.2,680.1,M,54.6,M,,0000*45
    $GPGSA,A,3,28,32,14,11,20,17,,,,,,,3.1,2.2,2.3*3B
    $GPGSV,3,1,10,32,85,208,34,11,76,335,24,20,50,234,30,19,40,146,22*77
    $GPGSV,3,2,10,28,28,281,44,14,22,048,22,03,12,150,,17,10,317,33*7D
    $GPGSV,3,3,10,23,07,181,22,06,04,145,*74
    $GPRMC,102646.418,A,4020.0125,N,00345.9545,W,000.0,262.5,240210,,,A*7F
    $GPVTG,262.5,T,,M,000.0,N,000.0,K,A*0E
    $GPGGA,102647.418,4020.0125,N,00345.9545,W,1,06,2.2,680.2,M,54.6,M,,0000*47

    And this is what I receive with the second one:

    ÿÿÿÿÿÿÿÿÿ
    ïÿ»¿¯»»¯½·¯½ÿ½¯»¿¯½·¯¯¯ÿ¯¯¯ï»¯ý¯»¯ÿý¯»ï»»û¿ïëÿ·w_w[×ï»ï½ÿ¯½¿¯»»¯¿µ¯ÿ»¿¿¯»µ¯½½ïÿ··¯»»·¯»µ¯ÿ»¿¯µ¿¯»»·ÿ¯»»¯½½¯·½¯ÿ½·µ¯½·«·½íÿë·÷_w[ׯ»¯ÿÿ¯½ÿ¯»¿¯»¿ÿ¯»¿»¯·»¯½·ÿ¯»»¯¿·¿¯»»ÿ¯¿»¯½»¯½µ¿ÿ¯¯½·¯½¿¯»ÿ½·¯»·«·{íëÿ·w_w[W¯»¯»ÿ¯½¿¯¿»¯¿·¯ÿý¿½ï½¿¯¿÷¯ÿ¿¿¯½·µ¯ë·wÿíë·w[o{ï½ÿ¿»·¿¿¯·½¿¯ÿ}¯·¿»¿¯¿½½ÿ½¯o¯¿¿»··ÿ¯½µ·½¯W¯¿¿ÿ¿¯¿¯»·»¯µ¯ÿ»·¿»½¿¯¯ïýÿ
    ÿ«¿uíëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·w_ÿww}¯½¿»·¿ÿ½¯·½¿¯·¿»¿ÿ¯¿½½½¯o¯¿¿ÿ»·µ¯½µ¿¿¯×ÿÿ½¯ÿ·¿»¯¿¯ÿ··½¯¿¯m¯¿·ÿ¯·¯m¯¯¿¿¿¿ÿ«·»íë·w_w[ÿ}ï}¯»¯»¿¯»ÿ»¯½·¯½½¯»ÿ¿¯½·¯¯¯¯¯¯ÿ¯»¯½¯»¯½¯»ÿ¯»«û¿íë·wßÿ÷[×ﻯ½¯¿¿ÿ¯»»¯¿µ¯¿¿¿ÿ﻽¯½½¯··¯ÿ»»·¯»µ¯»¿¯ÿµ¿¯»»·¯»»¯ÿ½½¯·½¯½·µÿ¯½·«·½íë·wÿ_w[W¯»¯»¯½ÿ¿¯»¿¯»¿¯»¿ÿÿ¯·»¯¿¿¯»»ÿï¿·¿¯»»¯¿»ÿ¯½»¯½µ¿¯¯½ÿ·¯½¿¯»½·ï»ÿ·«·{íë·w_ÿw[W¯»¯»¯½¿ÿ¯»»¯¿·¯½¿½ÿ¯½¿¯¿·¯¿·¯ÿ½¿µï¯·íë·ÿwûm{¯¿¿»·ÿ¿½¯·½¿¯}¯·ÿ¿»¿¯¿½½½¯oÿ¯¿¿»·µ¯½µ·ÿ»¯W¯¿¿¿¯¿ÿ¯»·»¯µ¯»·¿ÿ»½¿¯¯¯}«·{ÿíë÷w_WWw¯»ÿ÷»¯õ¯W¯ïo¯ÿ¿¿¿ï¿¯o¯¿¿ÿ¿¯¿ïk¯ý«¿uÿ

    The serial monitor is well configured (9600) and the 9600 baud rate is right, because is the only baud rate that actually works, either in this program with Arduino, or in the GPS software. Anyway, I’ve tried all the baud rates, without success. The GPS is “Venus GPS Logger” configured to 1Hz (i.e., 9600) and I’m using Arduino Duemilanove.

    Thank you for your help.
    AlMadrid


  43. Mikal

    1 year ago

    AlMadrid–

    That is indeed very strange. Just as an experiment, can you try increasing the baud rate on the Serial port and console to 115200. (Leave NewSoftSerial as is.) I may have to buy one of those Venus GPS units to figure out what is going wrong!

    Mikal


  44. Blake Dayton

    1 year ago

    Hey Mikal,

    A long while ago I nagged you about adding HDOP to the library. You were very helpful in your response and I would just like to say thanks for this great code project. I’ve had it perform without error for weeks on end, even with crappy input at times. Very robust.

    Thanks,

    Blake


  45. budiwan

    1 year ago

    Hello my freands.
    I have problem, to print posision lat and lon in degree menit

    please help

    thank very much

    budiwan


  46. Mikal

    1 year ago

    budiwan,

    You can print them very easily with

    float lat, lon;
    Serial.print(lat);
    Serial.print(" ");
    Serial.print(lon);

    Mikal


  47. Pantelis Ar

    1 year ago

    Mikal, I’m using your lib and the test code.
    I’ve changed the period of time in which I take an update from the GPS from 5 to 1 sec. Do you think this could affect the library’s performance?


  48. Pantelis Ar

    1 year ago

    I’m using the Parallax GPS module. The manufacturers manual says that the precision of it is +-5m. I take a point (lat,lon) from google earth and try to reach it using the lib and Andreas Gazis formula for distance calculation. While I stand above the point I’ve set on google earth, in my serial monitor I get values of distance above 10 meters.

    I experienced the same when I used just the code example of Mikals lib and entered the values (lat,lon) I got in arduino’s monitor to google earth. The
    distance difference must have been at least 10, maybe 15 meters. Google earth showed me a point across the street and onto my neighbours house.

    Has anyone had the same precision/acurracy problem?
    I’ve already read the previous posts and I know that every GPS module has different precision, but my GPS seems to have way less precision than the one i’ve read in the manufacturers manual.


  49. Mikal

    1 year ago

    Blake, thanks for the kind update. That has to be the gentlest reminder I’ve ever had. :) I do intend to update TinyGPS someday to support more fields, but I just haven’t gotten around to it…

    Mikal


  50. Mikal

    1 year ago

    Pantelis, as long as you are not dropping any characters from the serial GPS stream, you should be fine. I use 1 second intervals with 4800 baud all the time with no problem.

    Mikal

20 Trackbacks For This Post
  1. เริ่มต้นสร้าง GPS จอสีกับอาดูอี้โน่ | Ayarafun Factory

    [...] http://www.sundial.org/arduino/?page_id=3 [...]

  2. 10

    [...] won’t. I answer a few questions on the Arduino microcontroller forum and post an update to a library I [...]

  3. The Hired Gun » GPS project: show me something

    [...] this journey I also looked into using the TinyGPS library. While it does a nice job of handling the core GPS parsing, I still had the primary desire to log [...]

  4. GPS mit Arduino - Webmeister Blog

    [...] serielle Verbindung werden die  Daten ans Display geschickt. Verwendet werden die Bibliotheken TinyGPS und [...]

  5. The Frustromantic Box, Part 4: Software « New Bright Idea

    [...] Arduino developers for the great librarires, and to Mikal Hart in particular for his work on the TinyGPS and NewSoftSerial [...]

  6. Transmission success - Projects

    [...] and sending the bits of “Hello World” in 7N1 format.  I spent some time with the TinyGPS and NewSoftSerial libraries from Mikal Hart, and got the parsing working nicely and building an [...]

  7. GPS testing with LCD Character Display

    [...] can have the LCD output latitude, longitude, or whatever. You’ll need the TinyGPS library from Arduiniana downloaded and installed for it to work. They suggest using NewSoftSerial, but I couldn’t get [...]

  8. Arduino GPS — not exactly pocket-sized, but cool! « Arduino Projects FTW

    [...] EEPROM so they are persistent between power cycles. The sketch uses Mikal Hart's excellent TinyGPS library and includes code from the ArduPilot Projectfor [...]

  9. 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 [...]

  10. 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 [...]

  11. Shield Driven Arduino GPS

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

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

    [...] 2 [...]

  13. 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. [...]

  14. Moving Forward with Arduino – Chapter 19 – GPS part II « t r o n i x s t u f f

    [...] pull-down resistor). You will need to install the SdFAT library, NewSoftSerial library, TinyGPS library and the SdFat library if not already [...]

  15. Arduinoによる放射線データ収集(3) « stastaka's Blog

    [...] http://arduiniana.org/libraries/tinygps/ USBシリアルを使っているとTX,RXが使えませんが、 [...]

  16. reptile-addict.nl | My Arduino Blog

    [...] Mikal Hart’s tinyGPS library; [...]

  17. Communicating to GPS Receiver using USB Host Shield « Circuits@Home

    [...] how to send raw GPS output to a NMEA 0183 message parser. For the following code example I used Mikal Hart’s TinyGps library. Since the library itself is not handling serial input, it was only necessary to make changes in [...]

  18. GpsBee and Seeeduino Stalker v2 « Wireless Building Automation

    [...] easy developing of the necessary Arduino application I used an existent dedicated library: TinyGPS (http://arduiniana.org/libraries/tinygps/), which help us to parse all the packets received on the serial port from the GpsBee module. Ok, so [...]

  19. SheekGeek » Blog Archive » Weather Balloon Payload Testing on a Model Rocket (Pt.1)

    [...] and gyroscope package and slapped together a simple SD card interface. The libraries I used were TinyGPS and fat16lib (for SD card use). Weather_Balloon_Code and schematic in case you’d like to [...]

  20. Moving Forward with Arduino – Chapter 19 – GPS part II « t r o n i x s t u f f

    [...] forget the 10k ohm pull-down resistor). You will need to install NewSoftSerial library, TinyGPS library and the SdFat library if not already [...]

Leave a Reply