TinyGPS+ and SD hiccups

Hello Arduino community

I want to write some GPS data to a SD card:

Latitude, longitude, altitude, speed, date, time and the distance to a custom destination.

To do this I am using the modified TinyGPS+ “Kitchen Sink” example sketch. I only added some code for the SD functionalities.

The result seems to be good, but from time to time, data logging has some kind of hiccups. Information is skipped and there is no clean writing to the SD card. See the attached screenshot.

This also happens without inserted SD card.

I am relatively new to the Arduino world, but I guess it has something to do with the GPS update… Can someone help me?

I am using:
Arduino UNO R3, Adafruit Ultimate GPS Breakout with an external antenna, Adafruit MicroSD Breakout Board and a microSDHC class 10.

Sketch:

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SD.h>
const int chipSelect = 10;

static const int RXPin = 6, TXPin = 5;
static const uint32_t GPSBaud = 9600;

File dataFile;
char filename[] = "LOGGER00.CSV";

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

// For stats that happen every 5 seconds
unsigned long last = 0UL;

void setup()
{
  Serial.begin(115200);
  ss.begin(GPSBaud);

  Serial.print(F("Initializing SD card. "));
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println(F("Card failed."));
    // don't do anything more:
    return;
  }
  Serial.println(F("Card initialized."));
  
// create a new file with a new filenumber
for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
      if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      dataFile = SD.open(filename, FILE_WRITE);
      break; // leave the loop
      }
      dataFile.close();
  }
if (! dataFile) {
  Serial.println("couldnt create file");
  }
  Serial.print("Logging to: ");
  Serial.println(filename);
  Serial.println("");  
  
    //_________________________Write Log File Header__________________________________________________
File dataFile = SD.open(filename, FILE_WRITE); 
  if (dataFile)
  {                                 
    dataFile.print(F("Latitude"));          
    dataFile.print(",");
    dataFile.print(F("Longitude"));
    dataFile.print(",");
    dataFile.print(F("Altitude"));
    dataFile.print(",");
    dataFile.print(F("Speed"));
    dataFile.print(",");
    dataFile.print(F("Date"));
    dataFile.print(",");
    dataFile.print(F("Time"));
    dataFile.print(",");
    dataFile.println(F("Zielentfernung"));
    dataFile.close();
  }
    Serial.print(F("Latitude"));          
    Serial.print(",");
    Serial.print(F("Longitude"));
    Serial.print(",");
    Serial.print(F("Altitude"));
    Serial.print(",");
    Serial.print(F("Speed"));
    Serial.print(",");
    Serial.print(F("Date"));
    Serial.print(",");
    Serial.print(F("Time"));
    Serial.print(",");
    Serial.println(F("Zielentfernung"));
}

void loop()
{
  // Dispatch incoming characters
  while (ss.available() > 0)
    gps.encode(ss.read());
    
if (gps.location.isUpdated())
  {

    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
    Serial.print(F(","));
    File dataFile = SD.open(filename, FILE_WRITE);
    dataFile.print(gps.location.lat(), 6);
    dataFile.print(F(","));
    dataFile.print(gps.location.lng(), 6);
    dataFile.print(F(","));
    dataFile.close();
  }
 
  
else if (gps.altitude.isUpdated())
  {

    Serial.print(gps.altitude.meters());
    Serial.print(F(","));

    File dataFile = SD.open(filename, FILE_WRITE);
    dataFile.print(gps.altitude.meters());
    dataFile.print(F(","));
    dataFile.close();
  } 

else if (gps.speed.isUpdated())
  {

    Serial.print(gps.speed.kmph());
    Serial.print(F(","));
    File dataFile = SD.open(filename, FILE_WRITE);
    dataFile.print(gps.speed.kmph());
    dataFile.print(F(","));
    dataFile.close();
  }
  
else if (gps.date.isUpdated())
  {

    Serial.print(gps.date.day());
    Serial.print(F("/"));
    Serial.print(gps.date.month());
    Serial.print(F("/"));
    Serial.print(gps.date.year());
    Serial.print(F(","));
    File dataFile = SD.open(filename, FILE_WRITE);
    if (gps.date.day()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.date.day());
    dataFile.print(F("."));
    
    if (gps.date.month()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.date.month());
    dataFile.print(F("."));
    
    dataFile.print(gps.date.year());
    dataFile.print(F(","));
    dataFile.close();
  }

else if (gps.time.isUpdated())
  {
    Serial.print(gps.time.hour());
    Serial.print(F(":"));
    Serial.print(gps.time.minute());
    Serial.print(F(":"));
    Serial.print(gps.time.second());
    Serial.print(F(","));
    File dataFile = SD.open(filename, FILE_WRITE);
    if (gps.time.hour()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.time.hour());
    dataFile.print(F(":"));
    
    if (gps.time.minute()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.time.minute());
    dataFile.print(F(":"));
    
    if (gps.time.second()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.time.second());
    dataFile.print(F(","));
    dataFile.close();
  }
  
  else if (millis() - last > 1000)
  {
//    Serial.println();
    if (gps.location.isValid())
    {
      static const double LONDON_LAT = 47.492756, LONDON_LON = 8.761898; //custom location
      double distanceToLondon =
        TinyGPSPlus::distanceBetween(
          gps.location.lat(),
          gps.location.lng(),
          LONDON_LAT, 
          LONDON_LON);
      double courseToLondon =
        TinyGPSPlus::courseTo(
          gps.location.lat(),
          gps.location.lng(),
          LONDON_LAT, 
          LONDON_LON);
          
File dataFile = SD.open(filename, FILE_WRITE);
    dataFile.println(distanceToLondon/1000, 6);
    dataFile.close();
    Serial.println(distanceToLondon/1000, 6);
    }

    last = millis();

  }
}

There are several things interacting to cause your problem:

  • Serial.print() will "block", which means it keeps the Arduino from doing anything else, like reading GPS data. Although a buffer accumulates GPS data for a little while, this buffer will eventually overflow because the Arduino is busy sending characters to the Serial Monitor.
  • SD operations also block. Too many SD operations will also cause the input buffer to overflow. It looks like you are opening and closing the file way too often. I think there's a flush method you could use instead, and just open the file in setup. Maybe someone else has a better suggestion for the SD log.
  • You are performing Serial and SD operations when any one piece changes. You should probably wait until all the pieces have changed, then write out the group of pieces. I have written a little about "coherency" of a GPS fix for my library NeoGPS. I do not recommend that you switch libraries, but you would probably benefit from reading the NeoGPS documentation.
  • You are performing Serial and SD operations while GPS data is being received. It would be better to wait until the "quiet" time between GPS data. In some of my example programs, I wait until 5ms have elapsed without receiving any data before performing any kind of "blocking" operations, like Serial or SD. See loop() in the NMEAfused.ino example program.

Although simple Arduino programs do not have to worry about these kind of things, you have crossed the threshold where timing matters. Congrats, I think, and good luck! :slight_smile:

Cheers,
/dev

Solved: I have written the desired print commands for the SD file into

if (gps.location.isUpdated())
  {
...
}

and deleted all the other “.isUpdated” commands of the kitchen sink example. The sketch now works, it creates a new log file with a new filenumber, prints a header with titles for the colums of the .csv-file followed by a row with the location coordinates Lat/Long, altitude, speed, date, time and distance to a custom location. The log intervall is adjustable as well.

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SD.h>
const int chipSelect = 10;

static const int RXPin = 6, TXPin = 5;
static const uint32_t GPSBaud = 9600;

File dataFile;
char filename[] = "LOGGER00.CSV";

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

// For stats that happen every 5 seconds
//unsigned long last = 0UL;

unsigned long interval=5000;  // the time we need to wait
unsigned long previousMillis=0; // millis() returns an unsigned long.

void setup()
{
  Serial.begin(115200);
  ss.begin(GPSBaud);

  Serial.print(F("Initializing SD card. "));
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println(F("Card failed."));
    // don't do anything more:
    return;
  }
  Serial.println(F("Card initialized."));
  
// create a new file with a new filenumber
for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
      if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      dataFile = SD.open(filename, FILE_WRITE);
      break; // leave the loop
      }
      dataFile.close();
  }
if (! dataFile) {
  Serial.println("couldnt create file");
  }
  Serial.print("Logging to: ");
  Serial.println(filename);
  Serial.println("");  
  
    //_________________________Write Log File Header__________________________________________________
File dataFile = SD.open(filename, FILE_WRITE); 
  if (dataFile)
  {                                 
    dataFile.print(F("Latitude"));          
    dataFile.print(",");
    dataFile.print(F("Longitude"));
    dataFile.print(",");
    dataFile.print(F("Altitude [MuM]"));
    dataFile.print(",");
    dataFile.print(F("Speed [km/h]"));
    dataFile.print(",");
    dataFile.print(F("Date"));
    dataFile.print(",");
    dataFile.print(F("Time"));
    dataFile.print(",");
    dataFile.println(F("Destination dist. [km]"));
    dataFile.close();
  }
    Serial.print(F("Latitude"));          
    Serial.print(",");
    Serial.print(F("Longitude"));
    Serial.print(",");
    Serial.print(F("Altitude"));
    Serial.print(",");
    Serial.print(F("Speed"));
    Serial.print(",");
    Serial.print(F("Date"));
    Serial.print(",");
    Serial.print(F("Time"));
    Serial.print(",");
    Serial.println(F("Zielentfernung"));
}

void loop()
{
  // Dispatch incoming characters
  while (ss.available() > 0)
    gps.encode(ss.read());
    
if ((unsigned long)(millis() - previousMillis) >= interval) { //Logintervall
    previousMillis = millis();
    
if (gps.location.isUpdated())
  {


    File dataFile = SD.open(filename, FILE_WRITE);
    dataFile.print(gps.location.lat(), 6);                //print location data
    dataFile.print(F(","));
    dataFile.print(gps.location.lng(), 6);
    dataFile.print(F(","));

    dataFile.print(gps.altitude.meters());                //print altitude
    dataFile.print(F(","));

    dataFile.print(gps.speed.kmph());                    //print speed
    dataFile.print(F(","));

    if (gps.date.day()<10){  // Print date. Add a leading zero, if necessary.
     dataFile.print(0);
     }
    dataFile.print(gps.date.day());
    dataFile.print(F("."));
    
    if (gps.date.month()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.date.month());
    dataFile.print(F("."));
    
    dataFile.print(gps.date.year());
    dataFile.print(F(","));

    if (gps.time.hour()<10){            //Print GMT time. Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.time.hour());
    dataFile.print(F(":"));
    
    if (gps.time.minute()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.time.minute());
    dataFile.print(F(":"));
    
    if (gps.time.second()<10){  // Add a zero, if necessary, as above
     dataFile.print(0);
     }
    dataFile.print(gps.time.second());
    dataFile.print(F(","));


    if (gps.location.isValid())
    {
      static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;     //custom location
      double distanceToLondon =
        TinyGPSPlus::distanceBetween(
          gps.location.lat(),
          gps.location.lng(),
          LONDON_LAT, 
          LONDON_LON);
      double courseToLondon =
        TinyGPSPlus::courseTo(
          gps.location.lat(),
          gps.location.lng(),
          LONDON_LAT, 
          LONDON_LON);
          
    dataFile.println(distanceToLondon/1000, 6);                //print the distance to your custom location
    dataFile.close();

    }
  }
 }
}