Decode GPS NMEA code using arduino

17,415

Solution 1

You can use TinyGPS to parse the NMEA strings. If you are interested in only 1 sentence. You can write a custom parser as below for that sentence only.

int handle_byte(int byteGPS) {
buf[counter1] = byteGPS;
//Serial.print((char)byteGPS);
counter1++;
if (counter1 == 300) {
  return 0;
}

if (byteGPS == ',') {
    counter2++;
    offsets[counter2] = counter1;
    if (counter2 == 13) {
      return 0;
    }   }   if (byteGPS == '*') {
    offsets[12] = counter1;   }

  // Check if we got a <LF>, which indicates the end of line   if (byteGPS == 10) {

    // Check that we got 12 pieces, and that the first piece is 6 characters
    if (counter2 != 12 || (get_size(0) != 6)) {
      return 0;
    }

    // Check that we received $GPRMC
    // CMD buffer contains $GPRMC
    for (int j=0; j<6; j++) {

      if (buf[j] != cmd[j]) {
        return 0;
      }
    }

    // Check that time is well formed
    if (get_size(1) != 10) {

      return 0;
    }

    // Check that date is well formed
    if (get_size(9) != 6) {
      return 0;
    }

    SeeedOled.setTextXY(7,0);
    for (int j=0; j<6; j++) {
      SeeedOled.putChar(*(buf+offsets[1]+j));
    }
    SeeedOled.setTextXY(7,7);

    for (int j=0; j<6; j++) {
      SeeedOled.putChar(*(buf+offsets[9]+j));
    }

    // TODO: compute and validate checksum

    // TODO: handle timezone offset

      return 0;   }   
return 1; }

Solution 2

I have searched the Internet, and the best answer would be using the "TinyGPS++" library for Arduino. Almost all GPS-related codes are already included on the Library.

Solution 3

NMEA data is in a GPS-style (ddmm.ssss) format, Google wants it in Decimal Style (dd.mmssss), there is a coversion function at the bottom of the code for this step.

I wrote this because I don't like the large, complicated libraries to do simple little things, especially when I am trying to figure out how it works. This parses the GLL sentence, but you can change the sentence it's looking for and rearrange the sections if needed.

String ReadString;

void setup() {  
  Serial.begin(9600);  //Arduino serial monitor thru USB cable 
  Serial1.begin(9600); // Serial1 port connected to GPS
}

void loop() {  
  ReadString=Serial1.readStringUntil(13);   //NMEA data ends with 'return' character, which is ascii(13)
  ReadString.trim();                      // they say NMEA data starts with "$", but the Arduino doesn't think so.
  // Serial.println(ReadString);         //All the raw sentences will be sent to monitor, if you want them, maybe to see the labels and data order.

  //Start Parsing by finding data, put it in a string of character array, then removing it, leaving the rest of thes sentence for the next 'find'
   if (ReadString.startsWith("$GPGLL")) {   //I picked this sentence, you can pick any of the other labels and rearrange/add sections as needed. 
      Serial.println(ReadString);     // display raw GLL data in Serial Monitor
     // mine looks like this: "$GPGLL,4053.16598,N,10458.93997,E,224431.00,A,D*7D"

//This section gets repeated for each delimeted bit of data by looking for the commas
     //Find Lattitude is first in GLL sentence, other senetences have data in different order
      int Pos=ReadString.indexOf(',');   //look for comma delimetrer
      ReadString.remove(0, Pos+1); // Remove Pos+1 characters starting at index=0, this one strips off "$GPGLL" in my sentence
      Pos=ReadString.indexOf(','); //looks for next comma delimetrer, which is now the first comma because I removed the first segment   
        char Lat[Pos];            //declare character array Lat with a size of the dbit of data
           for (int i=0; i <= Pos-1; i++){    // load charcters into array
            Lat[i]=ReadString.charAt(i);           
           }   
            Serial.print(Lat);          // display raw latitude data in Serial Monitor, I'll use Lat again in a few lines for converting   
//repeating with a different char array variable        
       //Get Lattitude North or South
        ReadString.remove(0, Pos+1);               
        Pos=ReadString.indexOf(',');    
        char LatSide[Pos];           //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            LatSide[i]=ReadString.charAt(i);  //fill the array          
            Serial.println(LatSide[i]);       //display N or S
           }

          //convert the variable array Lat to degrees Google can use
          float LatAsFloat = atof (Lat);            //atof converts the char array to a float type
          float LatInDeg;
           if(LatSide[0]==char(78)) {        //char(69) is decimal for the letter "N" in ascii chart   
               LatInDeg= ConvertData(LatAsFloat);   //call the conversion funcion (see below) 
           }
           if(LatSide[0]==char(83)) {        //char(69) is decimal for the letter "S" in ascii chart   
               LatInDeg= -( ConvertData(LatAsFloat));   //call the conversion funcion (see below) 
           }
           Serial.println(LatInDeg,15); //display value Google can use in Serial Monitor, set decimal point value high
//repeating with a different char array variable               
       //Get Longitude
        ReadString.remove(0, Pos+1);               
        Pos=ReadString.indexOf(',');    
        char Longit[Pos];             //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            Longit[i]=ReadString.charAt(i);      //fill the array  
           }   
            Serial.print(Longit);      //display raw longitude data in Serial Monitor      
//repeating with a different char array variable 
            //Get Longitude East or West
        ReadString.remove(0, Pos+1);              
        Pos=ReadString.indexOf(',');    
        char LongitSide[Pos];         //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            LongitSide[i]=ReadString.charAt(i);      //fill the array          
            Serial.println(LongitSide[i]);        //display raw longitude data in Serial Monitor
           }       
           //convert to degrees Google can use  
          float LongitAsFloat = atof (Longit);    //atof converts the char array to a float type
          float LongInDeg;
         if(LongitSide[0]==char(69)) {        //char(69) is decimal for the letter "E" in ascii chart
                 LongInDeg=ConvertData(LongitAsFloat);   //call the conversion funcion (see below
         }    
         if(LongitSide[0]==char(87)) {         //char(87) is decimal for the letter "W" in ascii chart
                 LongInDeg=-(ConvertData(LongitAsFloat)); //call the conversion funcion (see below
         }             
           Serial.println(LongInDeg,15);  //display value Google can use in Serial Monitor, set decimal point value high
//repeating with a different char array variable 
            //Get TimeStamp - GMT
        ReadString.remove(0, Pos+1);                
        Pos=ReadString.indexOf(',');    
        char TimeStamp[Pos];          //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            TimeStamp[i]=ReadString.charAt(i);         //fill the array     
            }
           Serial.print(TimeStamp);   //display raw longitude data in Serial Monitor, GMT
           Serial.println("");       
   }
}

//Conversion function
float ConvertData(float RawDegrees)
{ 
  float RawAsFloat = RawDegrees; 
  int firstdigits = ((int)RawAsFloat)/100; // Get the first digits by turning f into an integer, then doing an integer divide by 100;
  float nexttwodigits = RawAsFloat - (float)(firstdigits*100);
  float Converted = (float)(firstdigits + nexttwodigits/60.0);
  return Converted;
}

Solution 4

I wrote this decent code, and it works up to two decimal places.

Code:

String gpsData;
String LATval = "######";
String LNGval = "######";
char inChar;
String gpsData;
String latt;
String la;
String lonn;
String lo;
float lattt;
float lonnn;
int latDeg;
int lonDeg;
float latMin;
float lonMin;
float latttt;
float lonnnn;
String sGPRMC;
void setup() {
   Serial.begin(9600);
}

void loop() {

  while (Serial.available()) {
    inChar = Serial.read();
    gpsData += inChar;
    if (inChar == '$') {
      gpsData = Serial.readStringUntil('\n');
      break;
    }
  }

  Serial.println(gpsData);
  sGPRMC = gpsData.substring(0, 5);
  if (sGPRMC == "GPRMC") {
    Serial.flush();
    latt = gpsData.substring(18, 28);
    la = gpsData.substring(29, 30);
    lonn = gpsData.substring(31, 42);
    lo = gpsData.substring(43, 44);
    Serial.print("latt:");
    Serial.println(latt);
    Serial.print("la:");
    Serial.println(la);
    Serial.print("lonn:");
    Serial.println(lonn);
    Serial.print("lo:");
    Serial.println(lo);
    lattt = latt.toFloat();
    lonnn = lonn.toFloat();
    Serial.print("lattt:");
    Serial.println(lattt);
    Serial.print("lonnn:");
    Serial.println(lonnn);

    if (la == "N" and lo == "E") {

      latDeg = float(int(lattt / 100));
      latMin = float(lattt - (latDeg * 100));
      latMin = latMin / 60;
        
      lonDeg = float(int(lonnn / 100));
      lonMin = float(lonnn - (lonDeg * 100));
      lonMin = lonMin / 60;
          
      latttt = latDeg + latMin;
      lonnnn = lonDeg + lonMin;
      LATval = String(latttt);
      LNGval = String(lonnnn);
      Serial.print("latDeg:");
      Serial.println(latDeg);
      Serial.print("latMin:");
      Serial.println(latMin);
      Serial.print("lonDeg:");
      Serial.println(lonDeg);
      Serial.print("lonMin:");
      Serial.println(lonMin);
      Serial.print("LATval:");
      Serial.println(LATval);
      Serial.print("LNGval:");
      Serial.println(LNGval);
    }
  }
}
Share:
17,415
weyhei
Author by

weyhei

I AM GLEN. I know PHP, MySQL, Javascript, HTML;

Updated on June 04, 2022

Comments

  • weyhei
    weyhei almost 2 years

    I know this has been asked many times but I really can't find what I am really searching.

    I am using an Arduino Uno and a GPS Shield that shows GPS data through serial.

    Here is the code I am uploading to my Arduino to interface the GPS Shield:

    void loop() // run over and over
    {
        while(!(mySerial.available())){}
          Serial.write(mySerial.read());
    }
    

    That is just the code. Nevertheless, as it continuously loops, on a Serial Monitor, it also output GPS data every second.

    Here is its output every second:

    $GPGGA,013856.000,000.9090,N,9090.90,E,1,09,1.1,316.97,M,0.00,M,,*66
    $GPGSA,A,3,07,08,11,1ÿ3,16,19,23,27,42,,,,2.8,1.1,2.5*3F
    $GPRMC,013856.000,A,000.9090,N,9090.90,E,0.0,038.1,310814,,,A*62
    $GPGSV,ÿ3,1,12,16,26,059,33,27,33,025,44,08,30,330,32,07,31,326,34*7A
    $GPGSV,3,2,12,19,58,354,31,01,33,186,18,23,32,221,24,11,5ÿ9,198,31*70
    $GPGSV,3,3,12,42,60,129,32,13,38,253,27,32,06,161,,31,01,140,*7E
    

    As it updates every second, the coordinates changes to minimal, which means the GPS Shield is working.

    The problem here is, I wanted to parse the GPS data, especially on the GPGGA line only, and ignore the other lines. I would like to parse the Status, Latitude, N/S Indicator, Longitude, and E/W Indicator.

    I have searched for the NMEA Library (http://nmea.sourceforge.net/), but I have no idea how to use it.

    Can someone please help me here? Thank you.