I have looked everywhere! Is there a NMEA library out there that supports GPRMB? The library by Maarten Lamers is as close as I can find but it doesnt have Routines for the gprmb sentences. Ive tried to add gprmb support to the TinyGps, but I'm just not that good yet. I've seen where a fe individuals have tried to use both RMB and RMC for marine applications but they didnt seem to get very far either? Thank you for any suggestions or help.
Here is some code I wrote to parse the GPGGA message. It might give you some help in parsing the GPRMB message. It keeps the Latitude, Longitude, and Altitude as strings since I was only logging the information and not doing calculations. It also skips over the fields I didn't need. Ignore the GPS_fault value and Faults variable. Those were for setting fault flags use by the rest of the system.
#include <SoftwareSerial.h>
// Fault flags
extern const unsigned int GPS_fault;
extern unsigned int Faults;
SoftwareSerial GPS(2, -1);
// Parse GPS message
byte GPS_byteCount = 0;
unsigned char GPS_buff[100];
unsigned long GPS_time = 0;
String GPS_lat = "";
String GPS_long = "";
String GPS_altitude = "";
unsigned long GPS_lastgood = 0;
unsigned long GPS_lasttest = 0;
void GPS_setup(void)
{
GPS.begin(4800);
}
void GPS_loop(void)
{
// If any characters are available from the GPS, send them to the parser
while (GPS.available())
{
GPS_parse_char(GPS.read());
}
// Check for GPS status
if ((millis() - GPS_lastgood > 3000))
Faults |= GPS_fault;
}
void GPS_log(Print &dest)
{
// Log GPS data
dest.print(GPS_time, DEC);
dest.print(',');
dest.print(GPS_lat);
dest.print(',');
dest.print(GPS_long);
dest.print(',');
dest.print(GPS_altitude);
dest.print(',');
dest.print((millis() - GPS_lastgood)/1000);
}
int GPS_parse_char(byte inputChar)
{
switch (inputChar)
{
case '\r':
return GPS_check_buffer();
case '
:
GPS_byteCount = 0; // Start of message
default:
GPS_buff[GPS_byteCount++] = inputChar;
if (GPS_byteCount == 100)
{
GPS_byteCount = 0; // Start of message
return -3; // Buffer overrun
}
return 0; // Not a full message
}
}
int GPS_check_buffer()
{
GPS_buff[GPS_byteCount] = '\0';
unsigned char *buffp = GPS_buff;
// Check the message type
for (int i=0; i<7; i++)
{
if (buffp[i] != "$GPGGA,"[i])
return 0;
}
// Verify the checksum before doing any more
if (GPS_buff[GPS_byteCount-3] != '*') // Checksum marker
return -1; // Bad message format
byte checksum = 0;
for (int i=1; i<GPS_byteCount-3; i++) // Skip '
and '*'
checksum ^= GPS_buff[i];
// Test the result
checksum -= hexval(GPS_buff[GPS_byteCount-2]) << 4;
checksum -= hexval(GPS_buff[GPS_byteCount-1]);
if (checksum != 0) return -2; // Checksum failure
// parse the time field
buffp += 7; // Skip over $GPGGA,
GPS_time = 0;
while (hexval(*buffp) != -1) // for each digit
{
GPS_time *= 10;
GPS_time += hexval(*buffp);
buffp++;
}
if (*buffp++ != '.') // Should be a decimal point
return -1;
while (*buffp == '0') // Skip zeros
buffp++;
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse latitude
GPS_lat = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_lat += *buffp++;
if (i == 1)
GPS_lat += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'S')
GPS_lat = "-" + GPS_lat;
else
if (*buffp != 'N')
return -1;
buffp++; // Skip the N or S
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse longitude
GPS_long = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_long += *buffp++;
if (i == 2)
GPS_long += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'W')
GPS_long = "-" + GPS_long;
else
if (*buffp != 'E')
return -1;
buffp++; // Skip the E or W
if (*buffp++ != ',') // Should be a comma
return -1;
if (*buffp++ == '0')
{
Faults |= GPS_fault;
return -3; // No view of sattelites
}
if (*buffp++ != ',') // Should be a comma
return -1;
GPS_lastgood = millis();
while (*buffp++ != ',') ; // Skip number of satellites
while (*buffp++ != ',') ; // Skip horizontal dillution
GPS_altitude = "";
while (*buffp != ',')
GPS_altitude += *buffp++;
//Serial.println((char *)GPS_buff);
Faults &= ~GPS_fault;
return 1; // Good fix
}
int hexval(char digit)
{
if (digit >= '0' && digit <= '9')
return digit - '0';
if (digit >= 'A' && digit <= 'F')
return digit - 'A' + 10;
if (digit >= 'a' && digit <= 'f')
return digit - 'a' + 10;
return -1;
}
Thank you, I'll see what I can figure out with it.
Thanks for the example and I have found numerous examples online for working with GPS strings but I'm wondering is there a place I can go to learn more about what all that stuff really means? I mean things like this:
for(int x=0; x<100; x++){
if(gps_buffer[x]=='*'){
checksum_received = strtol(&gps_buffer[x + 1], NULL, 16);//Parsing received checksum...
break;
I can copy other peoples work from now til doomsday but I really want to know what all this means so I can do more things in the future. I'm sorry to seem so stupid but this is my first go round with anything like this, I'm learning but I really need more.
A C tutorial is a good place to start.
Try C, C++ Programming Tutorials - Cprogramming.com. Or go to your local library a see if they have any books on the subject. Amazon has hundreds.
for(int x=0; x<100; x++) {
if(gps_buffer[x]=='*'){
checksum_received = strtol(&gps_buffer[x + 1], NULL, 16);//Parsing received checksum...
break;
}
}
That means:
Look at each of the first 100 characters in the buffer.
If that character is an asterisk (the NMEA marker for the checksum):
Convert the hexadecimal (base 16) digits starting after the '*' into a long integer and store it in "checksum_received".
Stop looking at any further characters since you already found the checksum (break out of the FOR loop)
johnwasser:
#include <SoftwareSerial.h>
// Fault flags
extern const unsigned int GPS_fault;
extern unsigned int Faults;
SoftwareSerial GPS(2, -1);
// Parse GPS message
byte GPS_byteCount = 0;
unsigned char GPS_buff[100];
unsigned long GPS_time = 0;
String GPS_lat = "";
String GPS_long = "";
String GPS_altitude = "";
unsigned long GPS_lastgood = 0;
unsigned long GPS_lasttest = 0;
void GPS_setup(void)
{
GPS.begin(4800);
}
void GPS_loop(void)
{
// If any characters are available from the GPS, send them to the parser
while (GPS.available())
{
GPS_parse_char(GPS.read());
}
// Check for GPS status
if ((millis() - GPS_lastgood > 3000))
Faults |= GPS_fault;
}
void GPS_log(Print &dest)
{
// Log GPS data
dest.print(GPS_time, DEC);
dest.print(',');
dest.print(GPS_lat);
dest.print(',');
dest.print(GPS_long);
dest.print(',');
dest.print(GPS_altitude);
dest.print(',');
dest.print((millis() - GPS_lastgood)/1000);
}
int GPS_parse_char(byte inputChar)
{
switch (inputChar)
{
case '\r':
return GPS_check_buffer();
case '
I know this is an old post, but would you mind telling me how I could use this code with hardware serial, "Serial2", instead of the software serial?
:
GPS_byteCount = 0; // Start of message
default:
GPS_buff[GPS_byteCount++] = inputChar;
if (GPS_byteCount == 100)
{
GPS_byteCount = 0; // Start of message
return -3; // Buffer overrun
}
return 0; // Not a full message
}
}
int GPS_check_buffer()
{
GPS_buff[GPS_byteCount] = '\0';
unsigned char *buffp = GPS_buff;
// Check the message type
for (int i=0; i<7; i++)
{
if (buffp[i] != "$GPGGA,"[i])
return 0;
}
// Verify the checksum before doing any more
if (GPS_buff[GPS_byteCount-3] != '*') // Checksum marker
return -1; // Bad message format
byte checksum = 0;
for (int i=1; i<GPS_byteCount-3; i++) // Skip '
I know this is an old post, but would you mind telling me how I could use this code with hardware serial, "Serial2", instead of the software serial?
and '*'
checksum ^= GPS_buff[i];
// Test the result
checksum -= hexval(GPS_buff[GPS_byteCount-2]) << 4;
checksum -= hexval(GPS_buff[GPS_byteCount-1]);
if (checksum != 0) return -2; // Checksum failure
// parse the time field
buffp += 7; // Skip over $GPGGA,
GPS_time = 0;
while (hexval(*buffp) != -1) // for each digit
{
GPS_time *= 10;
GPS_time += hexval(*buffp);
buffp++;
}
if (*buffp++ != '.') // Should be a decimal point
return -1;
while (*buffp == '0') // Skip zeros
buffp++;
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse latitude
GPS_lat = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_lat += *buffp++;
if (i == 1)
GPS_lat += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'S')
GPS_lat = "-" + GPS_lat;
else
if (*buffp != 'N')
return -1;
buffp++; // Skip the N or S
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse longitude
GPS_long = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_long += *buffp++;
if (i == 2)
GPS_long += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'W')
GPS_long = "-" + GPS_long;
else
if (*buffp != 'E')
return -1;
buffp++; // Skip the E or W
if (*buffp++ != ',') // Should be a comma
return -1;
if (*buffp++ == '0')
{
Faults |= GPS_fault;
return -3; // No view of sattelites
}
if (*buffp++ != ',') // Should be a comma
return -1;
GPS_lastgood = millis();
while (*buffp++ != ',') ; // Skip number of satellites
while (*buffp++ != ',') ; // Skip horizontal dillution
GPS_altitude = "";
while (*buffp != ',')
GPS_altitude += *buffp++;
//Serial.println((char *)GPS_buff);
Faults &= ~GPS_fault;
return 1; // Good fix
}
int hexval(char digit)
{
if (digit >= '0' && digit <= '9')
return digit - '0';
if (digit >= 'A' && digit <= 'F')
return digit - 'A' + 10;
if (digit >= 'a' && digit <= 'f')
return digit - 'a' + 10;
return -1;
}
I know this is an old post, but would you mind telling me how I could use this code with hardware serial, "Serial2", instead of the software serial?
I think if you change:
SoftwareSerial GPS(2, -1);
to
Stream *GPS = Serial2;
and then change all occurrences of "GPS." to "GPS->" it should work.
johnwasser:
I think if you change:SoftwareSerial GPS(2, -1);
to
Stream *GPS = Serial2;
and then change all occurrences of "GPS." to "GPS->" it should work.
That didn't work. The error I get is:
sketch_jun25a:7: error: cannot convert 'HardwareSerial' to 'Stream*' in initialization
sketch_jun25a.ino: In function 'void GPS_setup()':
sketch_jun25a:22: error: 'class Stream' has no member named 'begin'
Here's the complete sketch
// Fault flags
extern const unsigned int GPS_fault;
extern unsigned int Faults;
Stream *GPS = Serial2;
// Parse GPS message
byte GPS_byteCount = 0;
unsigned char GPS_buff[100];
unsigned long GPS_time = 0;
String GPS_lat = "";
String GPS_long = "";
String GPS_altitude = "";
unsigned long GPS_lastgood = 0;
unsigned long GPS_lasttest = 0;
void GPS_setup(void)
{
GPS->begin(4800);
}
void GPS_loop(void)
{
// If any characters are available from the GPS, send them to the parser
while (GPS->available())
{
GPS_parse_char(GPS->read());
}
// Check for GPS status
if ((millis() - GPS_lastgood > 3000))
Faults |= GPS_fault;
}
void GPS_log(Print &dest)
{
// Log GPS data
dest.print(GPS_time, DEC);
dest.print(',');
dest.print(GPS_lat);
dest.print(',');
dest.print(GPS_long);
dest.print(',');
dest.print(GPS_altitude);
dest.print(',');
dest.print((millis() - GPS_lastgood)/1000);
}
int GPS_parse_char(byte inputChar)
{
switch (inputChar)
{
case '\r':
return GPS_check_buffer();
case '
:
GPS_byteCount = 0; // Start of message
default:
GPS_buff[GPS_byteCount++] = inputChar;
if (GPS_byteCount == 100)
{
GPS_byteCount = 0; // Start of message
return -3; // Buffer overrun
}
return 0; // Not a full message
}
}
int GPS_check_buffer()
{
GPS_buff[GPS_byteCount] = '\0';
unsigned char *buffp = GPS_buff;
// Check the message type
for (int i=0; i<7; i++)
{
if (buffp[i] != "$GPGGA,"[i])
return 0;
}
// Verify the checksum before doing any more
if (GPS_buff[GPS_byteCount-3] != '*') // Checksum marker
return -1; // Bad message format
byte checksum = 0;
for (int i=1; i<GPS_byteCount-3; i++) // Skip '
and '*'
checksum ^= GPS_buff[i];
// Test the result
checksum -= hexval(GPS_buff[GPS_byteCount-2]) << 4;
checksum -= hexval(GPS_buff[GPS_byteCount-1]);
if (checksum != 0) return -2; // Checksum failure
// parse the time field
buffp += 7; // Skip over $GPGGA,
GPS_time = 0;
while (hexval(*buffp) != -1) // for each digit
{
GPS_time *= 10;
GPS_time += hexval(*buffp);
buffp++;
}
if (*buffp++ != '.') // Should be a decimal point
return -1;
while (*buffp == '0') // Skip zeros
buffp++;
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse latitude
GPS_lat = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_lat += *buffp++;
if (i == 1)
GPS_lat += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'S')
GPS_lat = "-" + GPS_lat;
else
if (*buffp != 'N')
return -1;
buffp++; // Skip the N or S
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse longitude
GPS_long = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_long += *buffp++;
if (i == 2)
GPS_long += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'W')
GPS_long = "-" + GPS_long;
else
if (*buffp != 'E')
return -1;
buffp++; // Skip the E or W
if (*buffp++ != ',') // Should be a comma
return -1;
if (*buffp++ == '0')
{
Faults |= GPS_fault;
return -3; // No view of sattelites
}
if (*buffp++ != ',') // Should be a comma
return -1;
GPS_lastgood = millis();
while (*buffp++ != ',') ; // Skip number of satellites
while (*buffp++ != ',') ; // Skip horizontal dillution
GPS_altitude = "";
while (*buffp != ',')
GPS_altitude += *buffp++;
//Serial.println((char *)GPS_buff);
Faults &= ~GPS_fault;
return 1; // Good fix
}
int hexval(char digit)
{
if (digit >= '0' && digit <= '9')
return digit - '0';
if (digit >= 'A' && digit <= 'F')
return digit - 'A' + 10;
if (digit >= 'a' && digit <= 'f')
return digit - 'a' + 10;
return -1;
}
Change that to:
Stream *GPS = &Serial2;
The "address of" operator '&' turns the object into an object pointer.
Also the call to ".begin()" has to be done to Serial2:
Serial2.begin(4800);
The other calls can use the pointer 'GPS' to get to Serial2.
Here is the full thing converted into a stand-alone sketch:
Stream *GPS = &Serial2;
// Parse GPS message
byte GPS_byteCount = 0;
unsigned char GPS_buff[100];
unsigned long GPS_time = 0;
String GPS_lat = "";
String GPS_long = "";
String GPS_altitude = "";
unsigned long GPS_lastgood = 0;
unsigned long GPS_lasttest = 0;
void setup(void)
{
Serial2.begin(4800);
}
void loop(void)
{
// If any characters are available from the GPS, send them to the parser
while (GPS->available())
{
if (GPS_parse_char(GPS->read()))
GPS_log(Serial);
}
}
void GPS_log(Print &dest)
{
// Log GPS data
dest.print(GPS_time, DEC);
dest.print(',');
dest.print(GPS_lat);
dest.print(',');
dest.print(GPS_long);
dest.print(',');
dest.print(GPS_altitude);
dest.print(',');
dest.print((millis() - GPS_lastgood)/1000);
}
int GPS_parse_char(byte inputChar)
{
switch (inputChar)
{
case '\r':
return GPS_check_buffer();
case '
:
GPS_byteCount = 0; // Start of message
default:
GPS_buff[GPS_byteCount++] = inputChar;
if (GPS_byteCount == 100)
{
GPS_byteCount = 0; // Start of message
return -3; // Buffer overrun
}
return 0; // Not a full message
}
}
int GPS_check_buffer()
{
GPS_buff[GPS_byteCount] = '\0';
unsigned char *buffp = GPS_buff;
// Check the message type
for (int i=0; i<7; i++)
{
if (buffp[i] != "$GPGGA,"[i])
return 0;
}
// Verify the checksum before doing any more
if (GPS_buff[GPS_byteCount-3] != '*') // Checksum marker
return -1; // Bad message format
byte checksum = 0;
for (int i=1; i<GPS_byteCount-3; i++) // Skip '
and '*'
checksum ^= GPS_buff[i];
// Test the result
checksum -= hexval(GPS_buff[GPS_byteCount-2]) << 4;
checksum -= hexval(GPS_buff[GPS_byteCount-1]);
if (checksum != 0) return -2; // Checksum failure
// parse the time field
buffp += 7; // Skip over $GPGGA,
GPS_time = 0;
while (hexval(*buffp) != -1) // for each digit
{
GPS_time *= 10;
GPS_time += hexval(*buffp);
buffp++;
}
if (*buffp++ != '.') // Should be a decimal point
return -1;
while (*buffp == '0') // Skip zeros
buffp++;
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse latitude
GPS_lat = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_lat += *buffp++;
if (i == 1)
GPS_lat += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'S')
GPS_lat = "-" + GPS_lat;
else
if (*buffp != 'N')
return -1;
buffp++; // Skip the N or S
if (*buffp++ != ',') // Should be a comma
return -1;
// Parse longitude
GPS_long = "";
for (int i=0; i<20; i++)
{
if (*buffp == ',')
break;
GPS_long += *buffp++;
if (i == 2)
GPS_long += ' '; // Separate degrees from minutes
}
buffp++; // skip the comma
if (*buffp == 'W')
GPS_long = "-" + GPS_long;
else
if (*buffp != 'E')
return -1;
buffp++; // Skip the E or W
if (*buffp++ != ',') // Should be a comma
return -1;
if (*buffp++ == '0')
{
return -3; // No view of sattelites
}
if (*buffp++ != ',') // Should be a comma
return -1;
GPS_lastgood = millis();
while (*buffp++ != ',') ; // Skip number of satellites
while (*buffp++ != ',') ; // Skip horizontal dillution
GPS_altitude = "";
while (*buffp != ',')
GPS_altitude += *buffp++;
//Serial.println((char *)GPS_buff);
return 1; // Good fix
}
int hexval(char digit)
{
if (digit >= '0' && digit <= '9')
return digit - '0';
if (digit >= 'A' && digit <= 'F')
return digit - 'A' + 10;
if (digit >= 'a' && digit <= 'f')
return digit - 'a' + 10;
return -1;
}