Credits : Thanks to Dave Akerman who’s NMEA Parser code these examples are based on.
Ok so now we have the GPS talking to the Arduino and you have some method of debugging its time to read that NMEA from the GPS and turn it into something we can use within our code.
The basic principle of an NMEA parser is to locate the NMEA sentences we are interested in , usually GPGGA and GPRMC. These two sentences contain most of the information you need for navigation, GPGGA is the main one used. This example will show how to parse the information from GPGGA.
Side note on the Ublox 8 series GPGGA is replaced by GNGGA by default to indicate the navigation information is from multiple GNSS sources (I.e American GPS and Russian GLONASS normally). So for this example we will look for $$GNGGA sentences. GGA sentences contain essential fix data which provide a 3D location.
An example of this is :
$GNGGA,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 *
So we need to read the incoming data from the GPS module, look for a $ to indicate the start of a sentence, when the full sentence is read in go see if it is a GNGGA line. Building on the previous code we add a new procedure to read data in and transmit to the debug when a sentence is detected (or not).
#include <SoftwareSerial.h> #define GPSENABLE 2 SoftwareSerial mySerial(4, 3); // RX, TX byte GPSBuffer[82]; byte GPSIndex=0; void setup() { pinMode(GPSENABLE, OUTPUT); digitalWrite(GPSENABLE, HIGH); Serial.begin(9600); mySerial.begin(9600); mySerial.println("Test Program"); } void loop() { CheckGPS(); } void CheckGPS() { int inByte; while (Serial.available() > 0) { inByte = Serial.read(); if ((inByte =='$') || (GPSIndex >= 80)) { GPSIndex = 0; } if (inByte != '\r') { GPSBuffer[GPSIndex++] = inByte; } if (inByte == '\n') { ProcessGPSLine(); GPSIndex = 0; } } } void ProcessGPSLine() { if ((GPSBuffer[1] == 'G') && (GPSBuffer[2] == 'N') && (GPSBuffer[3] == 'G') && (GPSBuffer[4] == 'G') && (GPSBuffer[5] == 'A')) { mySerial.println("GNGGA Detected!"); } else { mySerial.println("It was something else!"); } }
Now from your software serial monitor you should see :
GNGGA Detected! It was something else! It was something else! It was something else! It was something else! It was something else! It was something else! It was something else! GNGGA Detected!
As you can see we are now detecting when we have a $GNGGA sentence and ignoring the others. So lets start extracting some data from that $GNGGA string.
We know the structure of the sentence, it starts with a $ all the fields are delimited by a comma. Firstly lets up the debug speed to 19200 as we are likely to be outputting data fairly quickly. Amend the connection speed in PuTTY to 19200 and note the change in the code below.Comment out line 31 to stop the NMEA from the GPS appearing.
#include <SoftwareSerial.h> #define GPSENABLE 2 SoftwareSerial mySerial(4, 3); // RX, TX byte GPSBuffer[82]; byte GPSIndex=0; unsigned int GPS_Satellites=0; unsigned int GPS_Altitude=0; void setup() { pinMode(GPSENABLE, OUTPUT); digitalWrite(GPSENABLE, HIGH); Serial.begin(9600); mySerial.begin(19200); mySerial.println("Test Program"); } void loop() { CheckGPS(); } void CheckGPS() { int inByte; while (Serial.available() > 0) { inByte = Serial.read(); mySerial.write(inByte); // Output exactly what we read from the GPS to debug if ((inByte =='$') || (GPSIndex >= 80)) { GPSIndex = 0; } if (inByte != '\r') { GPSBuffer[GPSIndex++] = inByte; } if (inByte == '\n') { ProcessGPSLine(); GPSIndex = 0; } } } void ProcessGPSLine() { if ((GPSBuffer[1] == 'G') && (GPSBuffer[2] == 'N') && (GPSBuffer[3] == 'G') && (GPSBuffer[4] == 'G') && (GPSBuffer[5] == 'A')) { mySerial.println("GNGGA Detected!"); ProcessGNGGACommand(); mySerial.print("Altitude :"); mySerial.println(GPS_Altitude); mySerial.print("Satellites :"); mySerial.println(GPS_Satellites); } } void ProcessGNGGACommand() { int i, j, k, IntegerPart; unsigned int Altitude; // $GNGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 // ===== <-- altitude in field 8 IntegerPart = 1; GPS_Satellites = 0; Altitude = 0; for (i=7, j=0, k=0; (i<GPSIndex) && (j<9); i++) // We start at 7 so we ignore the '$GNGGA,' { if (GPSBuffer[i] == ',') { j++; // Segment index k=0; // Index into target variable IntegerPart = 1; } else { if (j == 6) { // Satellite Count if ((GPSBuffer[i] >= '0') && (GPSBuffer[i] <= '9')) { GPS_Satellites = GPS_Satellites * 10; GPS_Satellites += (unsigned int)(GPSBuffer[i] - '0'); } } else if (j == 8) { // Altitude if ((GPSBuffer[i] >= '0') && (GPSBuffer[i] <= '9') && IntegerPart) { Altitude = Altitude * 10; Altitude += (unsigned int)(GPSBuffer[i] - '0'); } else { IntegerPart = 0; } } } GPS_Altitude = Altitude; } }
Why not the rest ? Exercise left for reader 🙂
A great article, with a good explanation of how to send the binary commands.
One small comment – I’d suggest removing the (GPSBuffer[2] == ‘N’) check in line 47 – that means it will accept NMEA from GPS-only receivers as well as multi-constellation.
Hi Norman,
Yep this was tailored around the Ublox 8 series but fair comment.
Hi,
I have managed to transmit a RTTY using the Radiometrix programme and get GNGGA serial print using the gps programme (Ublox – part 4, above), but can’t work out how to get the $GNGGA string into the Radiometrix programme. I don’t want to take up your time if it is a complicated matter, but if not, a push in the right direction would be greatly appreciated.
I have tried deriving the answer from completed HAB programmes, and asked questions on the Arduino web site, but no luck. For the moment, I am stuck!
Colin.
Hi Colin,
Basically you need to parse the information from the GNGGA string out and place the values in variables. From here you can construct a telemetry string which can be transmitted. Come have a chat on IRC there is plenty of example code out there to work from.
Cheers
Just a quick note with regards to using the software serial on the Arduino. As you well noted, this can cause problems with other processes. The problem is that the software serial uses the CPU for a real-time process which results in an interruption of any other task that is being currently executed.
I attempted to use the software serial because I needed to have another sensor running on UART in addition to the GPS. I am not faint at heart when it comes to low-level coding, but the bugs in the software serial made me give up.
My solution was to use a Teensy processor which includes 3 real serial ports. In addition, the CPU runs much faster than the Arduino and it is physically smaller. It is programmed with the Arduino studio so — in most cases — no changes are required to the code.
Hey guys !
Checksum is wrong in the example, should be 59.
Here is the complete sentence:
$GNGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*59