I made a GPS library for decoding NMEA protocol

I am a novice forum user, and do not even own an Arduino. But I have written a library for the Wiring/Arduino i/o boards, to connect them to GPS receivers. The library is called NMEA:: (after the protocol that it implements). It can be downloaded from:

http://www.maartenlamers.com/nmea/.

It comes with programming examples, and is useful for many applications. It can be used in two different modes: one for normal users and one for GPS experts. It includes routines for calculating distances and directions over the Earth's surface, so you don't have to write them.

The NMEA:: library works very well on my Wiring i/o board, but I haven't tested it with Arduino. I am very curious about how it works and open for feedback.

Kind regards, Maarten Lamers, The Netherlands

Maarten,

Nice contribution. I'm sure it will save some people a lot of work.

In your examples - "serial" is spelled "seriall". I fixed that and the examples I tried then compiled fine using the Arduino IDE.

Thanks! John

[edit]OK, OK so it's really "Serial1" like software serial I presume. So I guess the examples that use 2 serial ports will need to be changes to use the software serial libraries. [/edit]

Maarten

Nice thing Does work on my Arduino too.

But only at 1hz .... I got a GPS at 5hz so would it be possible to parse this???

Geko

Thanks Maarten for your code. It will be very useful to me if I can get it to work with Arduino.

At the moment I am getting the error:

In function ‘void loop()’:
error: ‘class SoftwareSerial’ has no member named ‘available’

The code I have added is below. I have added this code in before the setup() function:

#include <SoftwareSerial.h>

#define rxPin 2
#define txPin 3

SoftwareSerial Serial1 = SoftwareSerial(rxPin, txPin);

Since the software serial library has no functionality called “available()”, how do I connect my GPS to a serial port called “Serial1” as the code asks for?

Has anybody got any ideas how to fix this please?

Thanks in advance.

Maarten But only at 1hz .... I got a GPS at 5hz so would it be possible to parse this???

Geko

Geko, a quick reply, because I am at work. The frequency of GPS (NMEA) output was approx 1Hz on the GPS receivers that I have worked with. My library is not dependent on the actual output frequency of the GPS receiver: it simply processes whatever comes out of the GPS. The only constraint in that respect is that the host (Arduino/Wiring) could be too slow to catch all the characters. I cannot judge that, but please inform me if it works for higher-speed NMEA output.

OK, OK so it’s really “Serial1” like software serial I presume. So I guess the examples that use 2 serial ports will need to be changes to use the software serial libraries.

John (BroHogan),

I will look into this (am at work now). I do not own an Arduino, only Wiring. in Wiring “serial1” is the second hardware serial port. perhaps in Arduino this is different. it would indeed explain why it worked for you when you changed it into “serial”.

thanks for your feedback!
Maarten

I tried using the standard Serial but it didn’t work for me.

Below is code that I am using that does enable me to get GPS data but I need to find a way to parse the GPS data string for the Longitude and Latitude values. When I debug it with Serial.println() (not in example below this bit) it messes up the GPS data I can see in the debugging window.

Maarten, your gprmc_distance_to function is just what I need but I can’t get it to work :frowning:

// include the SoftwareSerial library
#include <SoftwareSerial.h>

// Constants
#define rxPin 9
#define txPin 8

// set up the serial port

SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);

// variables
byte byteGPS = 0;
int i = 0;
int indices[13];
int cont = 0;
int conta = 0;

char inBuffer[300] = "";

int k = 0;

void setup(){

  //setup for mySerial port
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  mySerial.begin(4800);
  
  //setup for Serial port
  Serial.begin(19200);

  // setup the GPS module
  Serial.println("Configurando GPS...");
  delay(1000);
  mySerial.println("$PSTMNMEACONFIG,0,4800,1,1");        // configure NMEA sentences to show only GGA sentence
  delay(100);
  
  // command for setting time and position
  mySerial.println("$PSTMINITGPS,4140.000,N,00053.000,W,0197,22,10,2007,11,40,00");    

  // "4140.000,N" means: Latitude 41º40'00.0" North
  // "00053.000,W" means: Longitude 0º53'00.0" West
  // "0197" means 197 m elevation
  // "22,10,2007,11,40,00" means date and time (October 22, 2.007 - 11h 40min 00sec UTC time)
}

void loop(){
 
  byteGPS = 0;
  i = 0;
  while(byteGPS != 42){                   // read the GGA sentence
    byteGPS = mySerial.read();         
    inBuffer[i]=byteGPS;               
    i++;                      
  }
   
  k = 1;
  while(inBuffer[k] != 42){
    Serial.print(inBuffer[k]);            // write the GGA sentence
     k++;
  }
  Serial.println();
  delay(3000);
}

Thanks in advance for any help anyone can give me.

Maarten But only at 1hz .... I got a GPS at 5hz so would it be possible to parse this???

Geko

Geko, a quick reply, because I am at work. The frequency of GPS (NMEA) output was approx 1Hz on the GPS receivers that I have worked with. My library is not dependent on the actual output frequency of the GPS receiver: it simply processes whatever comes out of the GPS. The only constraint in that respect is that the host (Arduino/Wiring) could be too slow to catch all the characters. I cannot judge that, but please inform me if it works for higher-speed NMEA output.

ooops ok now it does work... hm but I don't know why. Ok it does display the sentence at 5Hz so know I have to parse the GGA and VTG sentence...

EDIT: I took a closer look and look what happend:

Sentence = $GPGGA,... Datatype = GPVTG Number of terms = FF0,0 Sentence = $GPVTG,... Datatype = GPVTG Number of terms = 11 Sentence = $GPGGA,... Datatype = GPVTG Number of terms = FF0,0 Sentence = $GPVTG,... Datatype = GPVTG Number of terms = 11

There are still some problems....

Geko

I tried using the standard Serial but it didn’t work for me.

Below is code that I am using that does enable me to get GPS data but I need to find a way to parse the GPS data string for the Longitude and Latitude values. When I debug it with Serial.println() (not in example below this bit) it messes up the GPS data I can see in the debugging window.

Maarten, your gprmc_distance_to function is just what I need but I can’t get it to work :frowning:

// include the SoftwareSerial library

#include <SoftwareSerial.h>

// Constants
#define rxPin 9
#define txPin 8

// set up the serial port

SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);

// variables
byte byteGPS = 0;
int i = 0;
int indices[13];
int cont = 0;
int conta = 0;

char inBuffer[300] = “”;

int k = 0;

void setup(){

//setup for mySerial port
 pinMode(rxPin, INPUT);
 pinMode(txPin, OUTPUT);
 mySerial.begin(4800);
 
 //setup for Serial port
 Serial.begin(19200);

// setup the GPS module
 Serial.println(“Configurando GPS…”);
 delay(1000);
 mySerial.println("$PSTMNMEACONFIG,0,4800,1,1");        // configure NMEA sentences to show only GGA sentence
 delay(100);
 
 // command for setting time and position
 mySerial.println("$PSTMINITGPS,4140.000,N,00053.000,W,0197,22,10,2007,11,40,00");

// “4140.000,N” means: Latitude 41º40’00.0" North
 // “00053.000,W” means: Longitude 0º53’00.0" West
 // “0197” means 197 m elevation
 // “22,10,2007,11,40,00” means date and time (October 22, 2.007 - 11h 40min 00sec UTC time)
}

void loop(){

byteGPS = 0;
 i = 0;
 while(byteGPS != 42){                   // read the GGA sentence
   byteGPS = mySerial.read();        
   inBuffer[i]=byteGPS;              
   i++;                      
 }
 
 k = 1;
 while(inBuffer[k] != 42){
   Serial.print(inBuffer[k]);            // write the GGA sentence
    k++;
 }
 Serial.println();
 delay(3000);
}




Thanks in advance for any help anyone can give me.

The gprmc_distance_to function does work on my Arduino. But without Software Serial.

I had also big problems getting the SoftSerial working. Go using the “AFSoftSerial” liabary and this code which does work
for my GPS:
See next post:

#include <AFSoftSerial.h>

AFSoftSerial mySerial =  AFSoftSerial(2, 3);

#define GPSRATE 9600

#define BUFFSIZ 90
char buffer[BUFFSIZ];
char *parseptr;
char buffidx;
uint8_t hour, minute, second, year, month, date;
uint32_t latitude, longitude;
uint8_t groundspeed, trackangle;
char latdir, longdir;
char status;

void setup() 
{ 
  
  Serial.begin(GPSRATE);
  mySerial.begin(GPSRATE);
} 
 
 
void loop() 
{ 
  uint32_t tmp;
  

  readline();

  if (strncmp(buffer, "$GPRMC",6) == 0) {
    
    // hhmmss time data
    parseptr = buffer+7;
    tmp = parsedecimal(parseptr); 
    hour = tmp / 10000;
    minute = (tmp / 100) % 100;
    second = tmp % 100;
    
    parseptr = strchr(parseptr, ',') + 1;
    status = parseptr[0];
    parseptr += 2;
    
    // grab latitude & long data
    // latitude
    latitude = parsedecimal(parseptr);
    if (latitude != 0) {
      latitude *= 10000;
      parseptr = strchr(parseptr, '.')+1;
      latitude += parsedecimal(parseptr);
    }
    parseptr = strchr(parseptr, ',') + 1;
    // read latitude N/S data
    if (parseptr[0] != ',') {
      latdir = parseptr[0];
    }
    
    //Serial.println(latdir);
    
    // longitude
    parseptr = strchr(parseptr, ',')+1;
    longitude = parsedecimal(parseptr);
    if (longitude != 0) {
      longitude *= 10000;
      parseptr = strchr(parseptr, '.')+1;
      longitude += parsedecimal(parseptr);
    }
    parseptr = strchr(parseptr, ',')+1;
    // read longitude E/W data
    if (parseptr[0] != ',') {
      longdir = parseptr[0];
    }
    

    // groundspeed
    parseptr = strchr(parseptr, ',')+1;
    groundspeed = parsedecimal(parseptr);

    // track angle
    parseptr = strchr(parseptr, ',')+1;
    trackangle = parsedecimal(parseptr);


    // date
    parseptr = strchr(parseptr, ',')+1;
    tmp = parsedecimal(parseptr); 
    date = tmp / 10000;
    month = (tmp / 100) % 100;
    year = tmp % 100;
    
    Serial.print("\nZeit: ");
    Serial.print(hour, DEC); Serial.print(':');
    Serial.print(minute, DEC); Serial.print(':');
    Serial.println(second, DEC);
    Serial.print("Datum: ");
    Serial.print(date, DEC); Serial.print('-');
    Serial.print(month, DEC); Serial.print('-');
    Serial.print("0");
    Serial.println(year, DEC);
    Serial.print("Speed: ");
    Serial.println(groundspeed, DEC);
    
    
   Serial.print("Lat: "); 
    if (latdir == 'N')
       Serial.print('+');
    else if (latdir == 'S')
       Serial.print('-');
    Serial.print(latitude/1000000, DEC); Serial.print('°', BYTE); Serial.print(' ');
    Serial.print((latitude/10000)%100, DEC); Serial.print('\''); Serial.print(' ');
    Serial.print((latitude%10000)*6/1000, DEC); Serial.print('.');
    Serial.print(((latitude%10000)*6/10)%100, DEC); Serial.println('\"');
    
   
  }
  //Serial.println(buffer);
}

uint32_t parsedecimal(char *str) {
  uint32_t d = 0;
  
  while (str[0] != 0) {
   if ((str[0] > '9') || (str[0] < '0'))
     return d;
   d *= 10;
   d += str[0] - '0';
   str++;
  }
  return d;
}

void readline(void) {
  char c;
  
  buffidx = 0;
  while (1) {
      c=mySerial.read();
      if (c == -1)
        continue;
      //Serial.print(c);
      if (c == '\n')
        continue;
      if ((buffidx == BUFFSIZ-1) || (c == '\r')) {
        buffer[buffidx] = 0;
        return;
      }
      buffer[buffidx++]= c;
  }
}

PS: It does even parse the GPRMC sentence just for testing for your device…

Geko

GekoCH, you are a legend. I can't tell you what a relief it is to finally get GPS data I can use. Thank you very much.

Whereabouts do you call the gprmc_distance_to function in your code as I couldn't get that bit working still with the NMEA library?

Thanks again.

GekoCH, you are a legend. I can't tell you what a relief it is to finally get GPS data I can use. Thank you very much.

Whereabouts do you call the gprmc_distance_to function in your code as I couldn't get that bit working still with the NMEA library?

Thanks again.

So you like to parse the data with the NMEA library on the Software Serial port? I haven't tried that because I won't need it but I think I'm going to try this as well..

Geko

wow! I'm blown away by the response to my first-ever library. thank you so much for comments and sharing your thoughts. I will certainly build a second version soon that solves these problems.

please consider: - I have never tested this library on Arduino, since I don't have one. This appears to be a major reason for the reported bugs (apart from the strange parsing of GPVTG sentences, which I will look into). It was only tested on a Wiring i/o board, and works fine there. - perhaps I was a little hasty to post it here, but I will happily fix all problems. - I will try to obtain an Arduino very soon and adapt the library and examples to it, given your comments.

I hope you can still bring up the patience, and am looking forward to hearing how you used it. Kind regards, Maarten

GekoCH.

So you like to parse the data with the NMEA library on the Software Serial port?

Yes I would like to parse the data with the NMEA library but not necessarily with the Software Serial.

Thank you GekoCH and Maartenl for your efforts.

GekoCH.

So you like to parse the data with the NMEA library on the Software Serial port?

Yes I would like to parse the data with the NMEA library but not necessarily with the Software Serial.

Thank you GekoCH and Maartenl for your efforts.

So this code works on the Hardware Serial (UART) just change the Baundrate
and set your GPS to send the data in GPRMC and of course the destination :smiley:

#include <nmea.h>

NMEA myGPS(GPRMC);

float dest_latitude = xx.xxxxxxxx;
float dest_longitude = y.yyyyyyyy;

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

void loop() {
  if (Serial.available() > 0 ) {

    if (myGPS.decode(Serial.read())) {
      
    
      Serial.print("Time:     ");
      Serial.println(myGPS.gprmc_utc(), DEC); 
   
      Serial.print("Distance: ");
      Serial.print(round(myGPS.gprmc_distance_to(dest_latitude, dest_longitude, MTR)),DEC);
      Serial.println("m");
      
    }
  }
}

Geko

So this code works on the Hardware Serial (UART) just change the Baundrate and set your GPS to send the data in GPRMC

Thanks GekoCH although I can't get the latest code to work. I put the baudrate to 4800 and I also tried putting the following code into the setup function:

pinMode(2, INPUT);
pinMode(3, OUTPUT);

I told you it does just work on the UART port. Connect the GPS pin to Arduino Pin 0 (RX).

I haven't tried it with the SoftwareSerial (pin 2 or pin 3)

Would be strange if it doesn't work... What kind of Sentence does your GPS send?

Geko

Geko, thank you so much for you assistance and help on this topic! It is much appreciated by me.

I have looked into the remarks in this thread shortly, and believe that nearly all problems are caused because the example programs were written for Wiring, not Arduino! I do not have an Arduino, and as a result, I didn’t know that there is no port “Serial1” on Arduino.

I will write new example code, and try it on an actual Arduino this weekend. Will then post an update on this thread and inform you how to download the new example code. I’m pretty sure it will work fine then.

thanks for your feedback and patience,
Maarten

Geko, thank you so much for you assistance and help on this topic! It is much appreciated by me.

I have looked into the remarks in this thread shortly, and believe that nearly all problems are caused because the example programs were written for Wiring, not Arduino! I do not have an Arduino, and as a result, I didn't know that there is no port "Serial1" on Arduino.

I will write new example code, and try it on an actual Arduino this weekend. Will then post an update on this thread and inform you how to download the new example code. I'm pretty sure it will work fine then.

thanks for your feedback and patience, Maarten

Thx a lot Maarten

Yes I think there are some bugs in the Library but he core code does work for me. Just some tiny bits do not work!

Thx again for your great work! Geko

GekoCH,

I told you it does just work on the UART port. 
Connect the GPS pin to Arduino Pin 0 (RX).

OK, this won't work for me as I have a GPS/GPRS shield that sits directly on top of the Arduino and uses pins 2 and 3.

Maarten, thanks that would be great!