Arduino GPS Logger Code I Wrote
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'; } }