Below is theΒ current version of the code I use for my GPS logger,Β which is powered by a Neo 6M GPS unit, an Arduino SD Card Reader with an 8 GB MicroSD Card, and an Arduino Nano. It logs a wide variety of information about my current location, speed of travel, and other information so I can easily look back at my trip.
/*
GPS Logger
SD card attached to SPI bus as follows:
* ** CS - pin 10
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
*/
#include <TinyGPS++.h>
char p_buffer[100];
#define P(str) (strcpy_P(p_buffer, PSTR(str)), p_buffer)
#define HOMELAT 42.614277
#define HOMELNG -73.851662
#include <SoftwareSerial.h>
// The serial connection to the GPS module
SoftwareSerial ss(4, 3);
// The TinyGPS++ object
TinyGPSPlus gps;
// Line Buffer to Save to File
char lnBuffer[115];
// Decimal to Number Char String Buffer
char numStr[30];
// delay
uint8_t prevSecond;
// data logger
#include <SPI.h>
#include <SD.h>
const uint8_t chipSelect = 10;
// location of previous location, used
// to calculate distance traveled
float prevLat = 0;
float prevLng = 0;
float prevAlt = 0; // stored in meters
float prevSpeed = 0; // stored in meters per second
float prevTime = 0;
// keep a running total of miles since last boot
float milesSinceBoot = 0;
// filename
char fileName[13];
// don't do anything if card is bad
bool cardWorks = false;
void setup() {
Serial.begin(115200);
ss.begin(9600);
for (uint8_t i = 6; i < 9; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println(F("Card failed, or not present"));
digitalWrite(8, HIGH); //red
cardWorks = false;
// don't do anything more:
return;
}
cardWorks = true;
Serial.println(F("card initialized."));
digitalWrite(6, HIGH); // green
fileName[0] = '\0';
}
void loop() {
if (!cardWorks) {
return;
}
while (ss.available() > 0) {
gps.encode(ss.read());
}
// if no GPS blink red LED
if (millis() > 5000 && gps.charsProcessed() < 10 && millis() % 1000 == 0) {
digitalWrite(8, HIGH); //red
}
else if (millis() > 5000 && gps.charsProcessed() < 10 && millis() % 500 == 0) {
digitalWrite(8, LOW); // red
}
// check if we have a valid location and blink yellow if invalid
if (gps.location.isValid()) {
digitalWrite(7, LOW); // yellow
}
// next two lines will blink yellow (eye catching!)
else if (!gps.location.isValid() && millis() % 1000 == 0) {
digitalWrite(7, HIGH); // yellow
}
else if (!gps.location.isValid() && millis() % 500 == 0) {
digitalWrite(7, LOW); // yellow
}
if (gps.location.isValid() && gps.time.second() != prevSecond) {
// before we forget, log previous second
prevSecond = gps.time.second();
// if no file name, create
if (fileName[0] == '\0') {
// create filename
// Example Filename = 03140201 (year=17, month=03, date=14, hour=02, tripnumber = 0
uint8_t trip = 1;
sprintf(fileName, P("%02d%02d%02d%02d.CSV"), gps.date.year() - 2000, gps.date.month(), gps.date.day(), trip);
// well, if we have made 99 trips today (usually due to bad power connection) only write final trip
while (SD.exists(fileName) && trip < 99) {
trip++;
sprintf(fileName, P("%02d%02d%02d%02d.CSV"), gps.date.year() - 2000, gps.date.month(), gps.date.day(), trip);
}
// print to serial where this data is being saved
Serial.print(P("Writing to ... "));
Serial.print(fileName);
Serial.print(P("\n"));
// write header to file
File dataFileHead = SD.open(fileName, FILE_WRITE);
// if not able to write, then turn LED red
if (!dataFileHead) {
digitalWrite(6, LOW); // green
digitalWrite(8, HIGH); //red
}
if (dataFileHead && trip != 99) {
dataFileHead.println(P("year,month,day,hour,minute,second,sat,latitude,longitude,elev,mph,dir,cdir,grade,accel,trip,mihm,dirhm"));
Serial.println( P("year,month,day,hour,minute,second,sat,latitude,longitude,elev,mph,dir,cdir,grade,accel,trip,mihm,dirhm"));
lnBuffer[0] = '\0';
dataFileHead.close();
digitalWrite(6, HIGH); // green
digitalWrite(8, LOW); //red
}
}
// dont log if no satellites found
if (!gps.satellites.value()) return;
sprintf(numStr, P("%04d,%02d,%02d,%02d,%02d,%02d, "), gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second());
strcat(lnBuffer, numStr);
dtostrf(gps.satellites.value(), 3, 0, numStr);
if (gps.satellites.isValid())
strcat(lnBuffer, numStr);
strcat(lnBuffer, P(", "));
dtostrf(gps.location.lat(), 10, 6, numStr);
if (gps.location.isValid())
strcat(lnBuffer, numStr);
strcat(lnBuffer, P(", "));
dtostrf(gps.location.lng(), 10, 6, numStr);
if (gps.location.isValid())
strcat(lnBuffer, numStr);
strcat(lnBuffer, P(", "));
if (gps.altitude.isValid())
dtostrf(gps.altitude.feet(), 5, 0, numStr);
strcat(lnBuffer, numStr);
// we only add the additional info if we have enough buffer space
// as occassionally before I was accidentially causing the buffer to
// overflow. In an idea world, I'd use a bigger buffer but RAM is limited
// e.g. if (strlen(lnBuffer) < 100)
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P(", "));
dtostrf(gps.speed.mph(), 0, 1, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P(", "));
dtostrf(gps.course.deg(), 0, 1, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P(", "));
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, TinyGPSPlus::cardinal(gps.course.deg()));
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P(", "));
// calculate current grade -- if we have a change in altitude
// and we've traveled some kind of distance
if (gps.altitude.isValid() && (gps.altitude.meters() - prevAlt) != 0 && TinyGPSPlus::distanceBetween(
gps.location.lat(),
gps.location.lng(),
prevLat,
prevLng) != 0) {
dtostrf(
(gps.altitude.meters() - prevAlt) /
TinyGPSPlus::distanceBetween(
gps.location.lat(),
gps.location.lng(),
prevLat,
prevLng)
, 5, 2, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, numStr);
}
else {
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P("0.00"));
}
// acceleration
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P(", "));
dtostrf((gps.speed.mps() - prevSpeed) / (millis() - prevTime), 7, 4, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P(", "));
// total milage of trip at this part of logging
// only increment when moving to minimize
// "idle" gps hop
if (prevLat && gps.speed.mph() > 1) {
milesSinceBoot += TinyGPSPlus::distanceBetween(
gps.location.lat(),
gps.location.lng(),
prevLat,
prevLng) * 0.00062137112; // meters to miles
}
dtostrf( milesSinceBoot, 5, 2, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, numStr);
if (strlen(lnBuffer) < 100)
strcat(lnBuffer, P(", "));
// miles from home
dtostrf( TinyGPSPlus::distanceBetween(
gps.location.lat(),
gps.location.lng(),
HOMELAT, HOMELNG) * 0.00062137112, 5, 2, numStr);
if (strlen(lnBuffer) < 105)
strcat(lnBuffer, numStr);
// direction from home (for shits and giggles)
if (strlen(lnBuffer) < 105)
strcat(lnBuffer, P(", "));
if (strlen(lnBuffer) < 110)
strcat(lnBuffer, TinyGPSPlus::cardinal(TinyGPSPlus::courseTo(
HOMELAT, HOMELNG,
gps.location.lat(),
gps.location.lng()
)));
// save previous lat/lng/alt
prevLat = gps.location.lat();
prevLng = gps.location.lng();
prevAlt = gps.altitude.meters();
prevSpeed = gps.speed.mps();
prevTime = millis();
File dataFile = SD.open(fileName, FILE_WRITE);
if (dataFile) {
dataFile.println(lnBuffer);
dataFile.close();
// print to the serial port too:
Serial.println(lnBuffer);
digitalWrite(6, HIGH); // green
digitalWrite(8, LOW); //red
}
// if the file isn't open, pop up an error:
else {
Serial.print(F("error opening "));
Serial.print(fileName);
Serial.print("\n");
digitalWrite(6, LOW); // green
digitalWrite(8, HIGH); //red
}
// clear buffer
lnBuffer[0] = '\0';
}
}