From b8ce3c6a4d7405fd1fe41b73d57c0a181afd30c1 Mon Sep 17 00:00:00 2001 From: Ladyada Date: Wed, 28 Mar 2012 12:20:38 -0400 Subject: [PATCH] Parsing works with RMC & GGA with a nice example as well --- Adafruit_GPS.cpp | 81 ++++++++++--------- Adafruit_GPS.h | 12 ++- examples/parsing/parsing.pde | 150 +++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 45 deletions(-) create mode 100644 examples/parsing/parsing.pde diff --git a/Adafruit_GPS.cpp b/Adafruit_GPS.cpp index 88abf37..acf5f30 100644 --- a/Adafruit_GPS.cpp +++ b/Adafruit_GPS.cpp @@ -12,6 +12,8 @@ All text above must be included in any redistribution #include +// how long are max NMEA lines to parse? +#define MAXLINELENGTH 120 // we double buffer: read one line in and leave one for the main program volatile char line1[MAXLINELENGTH]; @@ -25,13 +27,27 @@ volatile boolean recvdflag; boolean Adafruit_GPS::parse(char *nmea) { + // do checksum check + + // first look if we even have one + if (nmea[strlen(nmea)-4] == '*') { + uint16_t sum = parseHex(nmea[strlen(nmea)-3]) * 16; + sum += parseHex(nmea[strlen(nmea)-2]); + + // check checksum + for (uint8_t i=1; i < (strlen(nmea)-4); i++) { + sum ^= nmea[i]; + } + if (sum != 0) { + // bad checksum :( + //return false; + } + } // look for a few common sentences - if (strstr(nmea, "$GPGGA")) { // found GGA char *p = nmea; - // get time p = strchr(p, ',')+1; float timef = atof(p); @@ -41,7 +57,6 @@ boolean Adafruit_GPS::parse(char *nmea) { seconds = (time % 100); milliseconds = fmod(timef, 1.0) * 1000; - p = strchr(p, ',')+1; // parse out latitude p = strchr(p, ',')+1; @@ -80,7 +95,7 @@ boolean Adafruit_GPS::parse(char *nmea) { return true; } if (strstr(nmea, "$GPRMC")) { - // found RMC + // found RMC char *p = nmea; // get time @@ -94,6 +109,7 @@ boolean Adafruit_GPS::parse(char *nmea) { milliseconds = fmod(timef, 1.0) * 1000; p = strchr(p, ',')+1; + // Serial.println(p); if (p[0] == 'A') fix = true; else if (p[0] == 'V') @@ -142,13 +158,15 @@ boolean Adafruit_GPS::parse(char *nmea) { return false; } -void Adafruit_GPS::read(void) { +char Adafruit_GPS::read(void) { + char c = 0; + if (paused) - return; + return c; if (gpsSwSerial->available()) { - char c = gpsSwSerial->read(); + c = gpsSwSerial->read(); //Serial.print(c); @@ -166,37 +184,32 @@ void Adafruit_GPS::read(void) { currentline = line1; lastline = line2; } - /* - Serial.println("----"); - Serial.println((char *)lastline); - Serial.println("----"); - */ - // do checksum check + - // first look if we even have one - if (lastline[lineidx-4] == '*') { - uint16_t sum = parseHex(lastline[lineidx-3]) * 16; - sum += parseHex(lastline[lineidx-2]); - - // check checksum - for (uint8_t i=1; i < (lineidx-4); i++) { - sum ^= lastline[i]; - } - if (sum == 0) { - recvdflag = true; - } - } + //Serial.println("----"); + //Serial.println((char *)lastline); + //Serial.println("----"); lineidx = 0; - + recvdflag = true; } + currentline[lineidx++] = c; if (lineidx >= MAXLINELENGTH) lineidx = MAXLINELENGTH-1; } + return c; } -Adafruit_GPS::Adafruit_GPS(void) { +// Constructor when using SoftwareSerial or NewSoftSerial +#if ARDUINO >= 100 +Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser) { +#else + Adafruit_GPS::Adafruit_GPS(NewSoftSerial *ser) { +#endif + common_init(); // Set everything to common state, then... + gpsSwSerial = ser; // ...override swSerial with value passed. + recvdflag = false; paused = false; lineidx = 0; @@ -211,17 +224,9 @@ Adafruit_GPS::Adafruit_GPS(void) { fixquality = satellites = 0; } -// Constructor when using SoftwareSerial or NewSoftSerial -#if ARDUINO >= 100 -void Adafruit_GPS::begin(SoftwareSerial *ser, uint16_t baud) -#else -void Adafruit_GPS::begin(NewSoftSerial *ser, uint16_t baud) -#endif + +void Adafruit_GPS::begin(uint16_t baud) { - gpsSwSerial = ser; // ...override swSerial with value passed. - - - // 9600 NMEA is the default baud rate gpsSwSerial->begin(baud); } diff --git a/Adafruit_GPS.h b/Adafruit_GPS.h index 83e3b00..58772d8 100644 --- a/Adafruit_GPS.h +++ b/Adafruit_GPS.h @@ -40,19 +40,17 @@ All text above must be included in any redistribution #include "NewSoftSerial.h" #endif -// how long are max NMEA lines to parse? -#define MAXLINELENGTH 100 class Adafruit_GPS { public: - Adafruit_GPS(void); // Constructor when using SoftwareSerial + void begin(uint16_t baud); #if ARDUINO >= 100 - void begin(SoftwareSerial *ser, uint16_t baud); // Constructor when using SoftwareSerial + Adafruit_GPS(SoftwareSerial *ser); // Constructor when using SoftwareSerial #else - void begin(NewSoftSerial *ser, uint16_t baud); // Constructor when using NewSoftSerial + Adafruit_GPS(NewSoftSerial *ser); // Constructor when using NewSoftSerial #endif - void begin(HardwareSerial *ser, uint16_t baud); // Constructor when using HardwareSerial + Adafruit_GPS(HardwareSerial *ser); // Constructor when using HardwareSerial char *lastNMEA(void); boolean newNMEAreceived(); @@ -63,7 +61,7 @@ class Adafruit_GPS { boolean parseNMEA(char *response); uint8_t parseHex(char c); - void read(void); + char read(void); boolean parse(char *); void interruptReads(boolean r); diff --git a/examples/parsing/parsing.pde b/examples/parsing/parsing.pde new file mode 100644 index 0000000..7fb172c --- /dev/null +++ b/examples/parsing/parsing.pde @@ -0,0 +1,150 @@ +// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver +// +// This code shows how to listen to the GPS module in an interrupt +// which allows the program to have more 'freedom' - just parse +// when a new NMEA sentence is available! Then access data when +// desired. +// +// Tested and works great with the Adafruit Ultimate GPS module +// using MTK33x9 chipset +// ------> http://www.adafruit.com/products/746 +// Pick one up today at the Adafruit electronics shop +// and help support open source hardware & software! -ada + +#include + +// these are for Arduino 1.0 +#include +SoftwareSerial mySerial(3, 2); + +// if using Arduino v23 or earlier, uncomment these +// two lines and comment out the above. You will +// need to install NewSoftSerial +// #include +// NewSoftSerial mySerial(3, 2); + +// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console +// Set to 'true' if you want to debug and listen to the raw GPS sentences +#define GPSECHO true + +// Connect the GPS Power pin to 5V +// Connect the GPS Ground pin to ground +// Connect the GPS TX (transmit) pin to Digital 3 +// Connect the GPS RX (receive) pin to Digital 2 +Adafruit_GPS GPS(&mySerial); + + +// this keeps track of whether we're using the interrupt +// off by default! +boolean usingInterrupt = false; + +void setup() +{ + + // connect at 115200 so we can read the GPS fast enough and echo without dropping chars + // also spit it out + Serial.begin(115200); + Serial.println("Adafruit GPS library basic test!"); + + // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 + GPS.begin(9600); + + // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude + GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); + // uncomment this line to turn on only the "minimum recommended" data + //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); + // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since + // the parser doesn't care about other sentences at this time + + // Set the update rate + GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate + // For the parsing code to work nicely and have time to sort thru the data, and + // print it out we don't suggest using anything higher than 1 Hz + + // the nice thing about this code is you can have a timer0 interrupt go off + // every 1 millisecond, and read data from the GPS for you. that makes the + // loop code a heck of a lot easier! + useInterrupt(true); + + delay(1000); +} + + +// Interrupt is called once a millisecond, looks for any new GPS data, and stores it +SIGNAL(TIMER0_COMPA_vect) { + char c = GPS.read(); + // if you want to debug, this is a good time to do it! + if (GPSECHO) + if (c) UDR0 = c; + // writing direct to UDR0 is much much faster than Serial.print + // but only one character can be written at a time. +} + +void useInterrupt(boolean v) { + if (v) { + // Timer0 is already used for millis() - we'll just interrupt somewhere + // in the middle and call the "Compare A" function above + OCR0A = 0xAF; + TIMSK0 |= _BV(OCIE0A); + usingInterrupt = true; + } else { + // do not call the interrupt function COMPA anymore + TIMSK0 &= ~_BV(OCIE0A); + usingInterrupt = false; + } +} + +uint16_t timer = millis(); +void loop() // run over and over again +{ + // in case you are not using the interrupt above, you'll + // need to 'hand query' the GPS, not suggested :( + if (! usingInterrupt) { + // read data from the GPS in the 'main loop' + char c = GPS.read(); + // if you want to debug, this is a good time to do it! + if (GPSECHO) + if (c) UDR0 = c; + // writing direct to UDR0 is much much faster than Serial.print + // but only one character can be written at a time. + } + + // if a sentence is received, we can check the checksum, parse it... + if (GPS.newNMEAreceived()) { + // a tricky thing here is if we print the NMEA sentence, or data + // we end up not listening and catching other sentences! + // so be very wary if using OUTPUT_ALLDATA and trytng to print out data + //Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false + + if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false + return; // we can fail to parse a sentence in which case we should just wait for another + } + + // approximately every 2 seconds or so, print out the current stats + if (millis() - timer > 2000) { + timer = millis(); // reset the timer + + Serial.print("\nTime: "); + Serial.print(GPS.hour, DEC); Serial.print(':'); + Serial.print(GPS.minute, DEC); Serial.print(':'); + Serial.print(GPS.seconds, DEC); Serial.print('.'); + Serial.println(GPS.milliseconds); + Serial.print("Date: "); + Serial.print(GPS.day, DEC); Serial.print('/'); + Serial.print(GPS.month, DEC); Serial.print("/20"); + Serial.println(GPS.year, DEC); + Serial.print("Fix: "); Serial.print(GPS.fix); + Serial.print(" quality: "); Serial.println(GPS.fixquality); + if (GPS.fix) { + Serial.print("Location: "); + Serial.print(GPS.latitude, 4); Serial.print(GPS.lat); + Serial.print(", "); + Serial.print(GPS.longitude, 4); Serial.println(GPS.lon); + + Serial.print("Speed (knots): "); Serial.println(GPS.speed); + Serial.print("Angle: "); Serial.println(GPS.angle); + Serial.print("Altitude: "); Serial.println(GPS.altitude); + Serial.print("Satellites: "); Serial.println(GPS.satellites); + } + } +} \ No newline at end of file