fmemon:
I connected the GPS TX
to the Arduino RX
and I was able to get the data that I expected on the serial monitor.
Great!
fmemon:
The end goal is to pass the data from the Arduino to an app on my phone which will calculate/record lap times, speed, etc.
There are several NeoGPS lap timers here, with various degrees of complexity.
If I don't somehow grab the timestamp from the NMEA code, the GPS data, and the data from the sensors ends up drifting.
Yes, the Arduino clock frequency is not as good as the GPS atomic clock. It will drift. You can see in the sketch below how to get the UTC hours, minutes and seconds from the fix
structure that NeoGPS fills out for you.
Here's the code of the data that I'll be passing along:
Great, that gives us a better idea of what you're trying to do.
Here's a NeoGPS version:
#include <NMEAGPS.h>
NMEAGPS gps;
gps_fix fix; // the structure that holds all the parsed pieces
#define gpsPort Serial // a simple substitution
// Later, you can use a different serial object if you want.
// Change the definition of 'gpsPort' and the rest of your code still works.
// For example:
// AltSoftSerial gpsPort; // GPS TX to UNO pin 9
// Or
// #define gpsPort Serial1 // on a Mega
float tachFreq = 1333.0; // remember that floats always have a decimal point
float thrtlVolt = 2.9;
float gearVolt = 3.0;
int count = 0;
void setup() {
Serial.begin( 115200 );
//gpsPort.begin( 9600 ); // Uncomment this if you change gpsPort above.
}
void loop() {
while (gps.available( gpsPort )) { // NeoGPS reads the port for you
fix = gps.read(); // after enough reads, a fix structure is ready (10Hz)
sendMessage(); // now that we have new GPS data, send a message
}
}
#define CF(x) ((const char *)F(x)) // a helper "function" for below
void sendMessage()
{
// Instead of making one big buffer, this deals with each piece.
// The `sendPiece` function below prints the piece *and* calculates
// a checksum for that piece. The checksum accumulates all pieces' checksums.
char buf[16]; // for just one piece
uint8_t CS = 0;
strcpy_P( buf, CF("$RC3,,") ); // a string constant (from FLASH memory) is copied into the buffer
sendPiece( buf, CS ); // This saves RAM, too.
itoa( count, buf, 10 ); // an integer is written to the buffer
sendPiece( buf, CS );
strcpy_P( buf, CF(",,,,,,,") );
sendPiece( buf, CS );
//--------------------------
// This might be where you send pieces of the fix structure. Speed?
// lat/lon
if (fix.valid.location) {
dtostrf( fix.latitude(), 1, 6, buf ); // a float is written to the buffer
sendPiece( buf, CS );
}
sendChar( ',', CS );
if (fix.valid.location) {
dtostrf( fix.longitude(), 1, 6, buf );
sendPiece( buf, CS );
}
sendChar( ',', CS );
// UTC time
if (fix.valid.time) {
itoa( fix.dateTime.seconds, buf, 10 ); // an integer
sendPiece( buf, CS );
sendChar( '.', CS ); // and the hundredths of seconds
if (fix.dateTime_cs < 10) // leading zero
sendChar( '0', CS );
itoa( fix.dateTime_cs, buf, 10 );
sendPiece( buf, CS );
// minutes & hours too? Leading zeroes? ":" separators?
}
sendChar( ',', CS );
//--------------------------
dtostrf( tachFreq, 1, 2, buf ); // a float
sendPiece( buf, CS );
strcpy_P( buf, CF(",,") );
sendPiece( buf, CS );
dtostrf( thrtlVolt, 1, 2, buf );
sendPiece( buf, CS );
sendChar( ',', CS );
dtostrf( gearVolt, 1, 2, buf );
sendPiece( buf, CS );
strcpy_P( buf, CF(",,,,,,,,,,,,,*") ); // Are you sure you want to add '*' to the CS?
sendPiece( buf, CS );
// Now send the two hex digit CS
if (CS < 0x10)
Serial.print( '0' ); // leading zero...
Serial.println( CS, HEX ); // ... because this does not print leading zeroes
count = count + 1;
}
void sendPiece( char *buf, uint8_t & cs ) // '&' means it modifies the passed-in variable
{
Serial.print( buf ); // send just this much...
// ... and calculate the checksum for this piece.
for (uint8_t i = 0; buf[i] != 0; i++) {
cs ^= buf[i]; // same as 'cs = cs xor buf[i];'. Caret is XOR operator
}
}
void sendChar( char c, uint8_t & cs ) // a helper function to send one char
{
char buf[2];
buf[ 0 ] = c; // assign one character to the first element of the array...
buf[ 1 ] = 0; // ... and NUL-terminate the array (i.e. a C string).
sendPiece( buf, cs );
}
Using String
can be a recipe for disaster, so I also wanted to show you how to use C strings, aka char
arrays. Eliminating String
also saves 1600 bytes of program space. Official documentation here for dtostrf
and itoa
.
Notice how it prints each piece, instead of accumulating everything in one big buffer. This saves a lot of RAM. The checksum is also calculated for each piece, and accumulates as the pieces are sent. Now the buffer only has to be as big as the biggest piece, not the whole line.
The GPS is setup to only send RMC+GGA right now at 10Hz. Baud rate was upped to 115200 to support the extra data throughput
When you know what pieces you want to send, you can use this table to decide which NMEA sentences you really need. Maybe just RMC?
You can configure NeoGPS to parse only those sentences and fields. Everything else is quickly skipped.
This will eliminate more program space and RAM. It will also reduce the processing load on the Arduino, making your sketch run even faster.
Cheers,
/dev