GPS to SD card project in CSV failing

Hi All,

Sorry for the delay. I stepped back to do some additional research and learn the process behind the ino code a bit better. I'm in a bit better position (bad GPS pun) now, but I'm still having trouble integrating the GPS and the SD logging.

I switched over to the NeoGPS library as it avoids using delay and it uses about half the memory and storage. I've been able to see the NMEA data parsed correctly using NMEA.ino and NMEA_isr.ino. I have also been able to use SDFat.h and examples to read, format, and write to my SD card. But when I use NMEASDlog.ino all I get in the serial monitor is:

Here is my code.

#include <Arduino.h>
#include <NMEAGPS.h>

//======================================================================
//  Program: NMEASDlog.ino
//
//  Description:  This program is an interrupt-driven GPS logger.
//    It uses the alternative serial port libraries NeoHWSerial,
//    NeoSWSerial, or NeoICSerial.
//
//  Prerequisites:
//     1) You have completed the requirements for NMEA_isr.ino
//     2) You have connected an SPI SD card and verified it is working
//        with other SD utilities.
//     3) For logging faster than the default 1Hz rate, you have
//        identified the required commands for your GPS device.
//
//  'Serial' is for debug output to the Serial Monitor window.
//
//  License:
//    Copyright (C) 2014-2017, SlashDevin
//
//    This file is part of NeoGPS
//
//    NeoGPS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    NeoGPS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with NeoGPS.  If not, see <http://www.gnu.org/licenses/>.
//
//======================================================================
// SDFat examples work
// NEMA and NEMAisr both work

#include <GPSport.h>

//#include <Streamers.h>

//----------------------------------------------------------------
// Check configuration

#if !defined( GPS_FIX_TIME ) || !defined( GPS_FIX_LOCATION )
  #error You must define TIME and LOCATION in GPSfix_cfg.h
#endif

#if !defined( NMEAGPS_PARSE_RMC )
  #error You must define NMEAGPS_PARSE_RMC in NMEAGPS_cfg.h!
#endif

#ifndef NMEAGPS_INTERRUPT_PROCESSING
  #error You must define NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h!
#endif

static const int LED = 13;

static NMEAGPS  gps; 

//----------------------------------------------------------------
// SD card includes and declarations

#include <SPI.h>
#include <SdFat.h>

SdFat SD;
const int chipSelect = 10;

//----------------------------------------------------------------
// For testing, it may be more convenient to simply print the
//   GPS fix fields to the Serial Monitor.  Simply uncomment
//   this define to skip all SD operations.  An SD card module
//   does not have to be connected.
#define SIMULATE_SD

#ifdef SIMULATE_SD

  auto &logfile = DEBUG_PORT;

#else

  File logfile;

#endif

//----------------------------------------------------------------
//  Utility to print a long integer like it's a float 
//    with 9 significant digits.

void printL( Print & outs, int32_t degE7 )
{
  // Extract and print negative sign
  if (degE7 < 0) {
    degE7 = -degE7;
    outs.print( '-' );
  }

  // Whole degrees
  int32_t deg = degE7 / 10000000L;
  outs.print( deg );
  outs.print( '.' );

  // Get fractional degrees
  degE7 -= deg*10000000L;

  // Print leading zeroes, if needed
  if (degE7 < 10L)
    outs.print( F("000000") );
  else if (degE7 < 100L)
    outs.print( F("00000") );
  else if (degE7 < 1000L)
    outs.print( F("0000") );
  else if (degE7 < 10000L)
    outs.print( F("000") );
  else if (degE7 < 100000L)
    outs.print( F("00") );
  else if (degE7 < 1000000L)
    outs.print( F("0") );
  
  // Print fractional degrees
  outs.print( degE7 );
}

//----------------------------------------------------------------
//  Because the SD write can take a long time, GPSisr will store 
//  parsed data in the NMEAGPS 'buffer' array until GPSloop can get to it.
//
//  The number of elements you should have in the array depends on
//  two things: the speed of your SD card, and the update rate you
//  have chosen.
//
//  For an update rate of 10Hz (100ms period), two fixes are probably
//  enough.  Most cards take ~100ms to complete a write of 512 bytes.
//  With FIX_MAX=2, two complete fixes can be stored, which were
//  received in 200ms.  A third fix can be started, giving a little
//  more time before an overrun occurs, a total of about 250ms.
//
//  If your card is slower or your update rate is faster, you should
//  first build and run this program to determine the speed of your
//  card.  The time it takes to log one record is printed to the log
//  file.
//
//  After the program has run for a minute or two, remove the
//  card and examine the loggingTimes.  You may see that an interval
//  was skipped, and you will also see an OVERRUN message on the
//  DEBUG_PORT (usually Serial).
//
//  You should calculate a new FIX_MAX from the maximum loggingTime 
//  you see in the log file:
//
//    FIX_MAX = (max loggingTime)/(update period) + 1;
//
//  For example, if the max loggingTime is 160ms, and the update period is
//  100ms (10Hz), then FIX_MAX = 160/100 + 1 = 2.
//
//  Change the FIX_MAX value, build and run the program again.  The
//  SD log file should now contain all records, and no OVERRUN
//  messages should have been printed on the DEBUG_PORT.
//
//  If you do see an OVERRUN message, examine the loggingTime to see
//  if it exceeded the maximum value from the previous build, and
//  increase FIX_MAX.
//
//  If you are also printing data to a Serial device, you could be
//  printing too much information.  In general, you must print less than
//  (baudrate/10) characters per second.  For example, if your baudrate 
//  is 9600, you must print less than 960 characters per second.  And if
//  the update rate is 10Hz, you must print no more than 96 characters
//  per update.
//
//  There are also restrictions on how much you should print to Serial in one 
//  section of code.  If you print more than 64 characters (the output buffer 
//  size), then some prints will block until all characters can be stored in the
//  output buffer.
//
//  For example, if you try to print 80 characters, the first 64 characters
//  will be immediately stored in the output buffer. However, the last 16 
//  characters must wait until the output buffer has room for 16 more 
//  characters.  That takes 16 * (10/baudrate) milliseconds.  At 9600 baud
//  that will take 17ms.  The loggingTimes will show no less than 17ms per
//  record, and will occasionally include the longer SD write time of ~100ms.

//----------------------------------------------------------------

static void GPSloop()
{
  if (gps.available()) {

    gps_fix fix = gps.read();

    // Log the fix information if we have a location and time
    
    if (fix.valid.location && fix.valid.time) {

      static uint16_t lastLoggingTime  = 0;
             uint16_t startLoggingTime = millis();

      // If you like the CSV format implemented in Streamers.h,
      //   you could replace all these prints with 
      // trace_all( logFile, fix ); // uncomment include Streamers.h

      printL( logfile, fix.latitudeL() );
      logfile.print( ',' );
      printL( logfile, fix.longitudeL() );
      logfile.print(',');

      if (fix.dateTime.hours < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.hours);
      logfile.print( ':' );
      if (fix.dateTime.minutes < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.minutes);
      logfile.print( ':' );
      if (fix.dateTime.seconds < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.seconds);
      logfile.print( '.' );
      if (fix.dateTime_cs < 10)
         logfile.print( '0' ); // leading zero for .05, for example
      logfile.print(fix.dateTime_cs);
      logfile.print(',');

      logfile.print( lastLoggingTime ); // write how long the previous logging took
      logfile.println();

      // flush() is used to empty the contents of the SD buffer to the SD. 
      // If you don't call flush, the data will fill up the SdFat buffer 
      // of 512bytes and flush itself automatically.
      //
      // To avoid losing data or corrupting the SD card file system, you must 
      // call flush() at least once (or close()) before powering down or pulling 
      // the SD card.
      //
      // It is *strongly* recommended that you use some external event 
      // to close the file.  For example, staying within 50m of the moving
      // average location for 1 minute, or using a switch to start and stop 
      // logging.  It would also be good to provide a visual indication 
      // that it is safe to power down and/or remove the card,  perhaps via
      // the LED.
      //
      // To reduce the amount of data that may be lost by an abnormal shut down,
      // you can call flush() periodically.
      //
      // Depending on the amount of data you are printing, you can save 
      // *a lot* of CPU time by not flushing too frequently.  BTW, flushing
      // every time at 5Hz is too frequent.

      // This shows how to flush once a second.
      static uint16_t lastFlushTime = 0;

      if (startLoggingTime - lastFlushTime > 1000) {
        lastFlushTime = startLoggingTime; // close enough
        logfile.flush();
      }

      #ifdef SIMULATE_SD
        // Simulate the delay of writing to an SD card.  These times are
        //   very long.  This is intended to show (and test) the overrun detection.
        //
        // On a 1Hz GPS, delaying more than 2 seconds here, or more than
        //   2 seconds in two consecutive updates, will cause OVERRUN.
        //
        // Feel free to try different delays to simulate the actual behavior
        //   of your SD card.

        uint16_t t = random(0,5); // 0..4
        t += 3;                   // 3..7
        t = t*t*t*t;              // 81..2401ms
        delay( t ); // cause an OVERRUN
      #endif

      // All logging is finished, figure out how long that took.
      //   This will be written in the *next* record.    
      lastLoggingTime = (uint16_t) millis() - startLoggingTime;
    }
  }

} // GPSloop

//----------------------------------------------------------------

void GPSisr( uint8_t c )
{
  gps.handle( c );

} // GPSisr

//----------------------------------------------------------------
//  This routine waits for GPSisr to provide 
//  a fix that has a valid location.
//
//  The LED is slowly flashed while it's waiting.

static void waitForFix()
{
  DEBUG_PORT.print( F("Waiting for GPS fix...") );

  uint16_t lastToggle = millis();

  for (;;) {
    if (gps.available()) {
      if (gps.read().valid.location)
        break; // Got it!
    }

    // Slowly flash the LED until we get a fix
    if ((uint16_t) millis() - lastToggle > 500) {
      lastToggle += 500;
      digitalWrite( LED, !digitalRead(LED) );
      DEBUG_PORT.write( '.' );
    }
  }
  DEBUG_PORT.println();

  digitalWrite( LED, LOW );

  gps.overrun( false ); // we had to wait a while...

} // waitForFix

//----------------------------------------------------------------

void setup()
{
  DEBUG_PORT.begin(9600);
  while (!DEBUG_PORT)
    ; // wait for serial port to connect. 

  DEBUG_PORT.println( F("NMEASDlog_UnoAttempt.ino started!") );
  DEBUG_PORT.print( F("fix size = ") );
  DEBUG_PORT.println( sizeof(gps_fix) );
  DEBUG_PORT.print( NMEAGPS_FIX_MAX );
  DEBUG_PORT.println( F(" GPS updates can be buffered.") );
  
  if (gps.merging != NMEAGPS::EXPLICIT_MERGING)
    DEBUG_PORT.println( F("Warning: EXPLICIT_MERGING should be enabled for best results!") );

  gpsPort.attachInterrupt( GPSisr );
  gpsPort.begin( 9600 );

  //  Configure the GPS.  These are commands for MTK GPS devices.  Other
  //    brands will have different commands.
  gps.send_P( &gpsPort, F("PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") ); // RMC only for MTK GPS devices
  gps.send_P( &gpsPort, F("PMTK220,100") ); // 10Hz update rate for MTK GPS devices

  // Enable the LED for blinking feedback
  pinMode( LED, OUTPUT );

  initSD();

  waitForFix();

} // setup

//----------------------------------------------------------------

void loop()
{
  GPSloop();

  if (gps.overrun()) {
    gps.overrun( false );
    DEBUG_PORT.println( F("DATA OVERRUN: fix data lost!") );
  }
}

//----------------------------------------------------------------

void initSD()
{
  #ifdef SIMULATE_SD

    DEBUG_PORT.println( F("  Simulating SD.") );
    
  #else

    DEBUG_PORT.println( F("Initializing SD card...") );

    // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) {
      DEBUG_PORT.println( F("  SD card failed, or not present") );
      // don't do anything more:

      // Flicker the LED
      while (true) {
        digitalWrite(LED,HIGH);
        delay(75);
        digitalWrite(LED,LOW);
        delay(75);
      }
    }

    DEBUG_PORT.println( F("  SD card initialized.") );

    // Pick a numbered filename, 00 to 99.
    char filename[15] = "data_##.txt";

    for (uint8_t i=0; i<100; i++) {
      filename[5] = '0' + i/10;
      filename[6] = '0' + i%10;
      if (!SD.exists(filename)) {
        // Use this one!
        break;
      }
    }

    logfile = SD.open(filename, FILE_WRITE);
    if (!logfile) {
      DEBUG_PORT.print( F("Couldn't create ") ); 
      DEBUG_PORT.println(filename);

      // If the file can't be created for some reason this leaves the LED on
      //   so I know there is a problem
      digitalWrite(LED,HIGH);

      while (true) {}
    }

    DEBUG_PORT.print( F("Writing to ") ); 
    DEBUG_PORT.println(filename);

    // GPS Visualizer requires a header to identify the CSV fields.
    // If you are saving other data or don't need this, simply remove/change it
    logfile.println( F("latitude,longitude,time,loggingTime") ); 

    //trace_header( logfile ); // and uncomment #include Streamers.h
    
  #endif
} // initSD

I'm using an Arduino Uno, pins 0(Rx) & 1(Tx) for the GPS, and pins 10 (CS), 11 (MOSI), 12 (MISO), 13(SCK) for the SD card.

But even without the SDcard set-up I should get the complete void setup print and the simulate SD info in the serial monitor, right?

Thank you for any guidance you can offer.

Post that code.

Also, a big difference between your two codes is the second one tests to make sure the gps data is valid before using it. The first code just blindly uses it. Also, look at what SmartDelay() is doing vs. your normal delay() function.

Hi All,

Sorry for the delay. I stepped back to do some additional research and learn the process behind the ino code a bit better. I'm in a bit better position (bad GPS pun) now, but I'm still having trouble integrating the GPS and the SD logging.

I switched over to the NeoGPS library as it avoids using delay and it uses about half the memory and storage. I've been able to see the NMEA data parsed correctly using NMEA.ino and NMEA_isr.ino. I have also been able to use SDFat.h and examples to read, format, and write to my SD card. But when I use NMEASDlog.ino all I get in the serial monitor is:

Here is my code.

#include <Arduino.h>
#include <NMEAGPS.h>

//======================================================================
//  Program: NMEASDlog.ino
//
//  Description:  This program is an interrupt-driven GPS logger.
//    It uses the alternative serial port libraries NeoHWSerial,
//    NeoSWSerial, or NeoICSerial.
//
//  Prerequisites:
//     1) You have completed the requirements for NMEA_isr.ino
//     2) You have connected an SPI SD card and verified it is working
//        with other SD utilities.
//     3) For logging faster than the default 1Hz rate, you have
//        identified the required commands for your GPS device.
//
//  'Serial' is for debug output to the Serial Monitor window.
//
//  License:
//    Copyright (C) 2014-2017, SlashDevin
//
//    This file is part of NeoGPS
//
//    NeoGPS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    NeoGPS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with NeoGPS.  If not, see <http://www.gnu.org/licenses/>.
//
//======================================================================
// SDFat examples work
// NEMA and NEMAisr both work

#include <GPSport.h>

//#include <Streamers.h>

//----------------------------------------------------------------
// Check configuration

#if !defined( GPS_FIX_TIME ) || !defined( GPS_FIX_LOCATION )
  #error You must define TIME and LOCATION in GPSfix_cfg.h
#endif

#if !defined( NMEAGPS_PARSE_RMC )
  #error You must define NMEAGPS_PARSE_RMC in NMEAGPS_cfg.h!
#endif

#ifndef NMEAGPS_INTERRUPT_PROCESSING
  #error You must define NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h!
#endif

static const int LED = 13;

static NMEAGPS  gps; 

//----------------------------------------------------------------
// SD card includes and declarations

#include <SPI.h>
#include <SdFat.h>

SdFat SD;
const int chipSelect = 10;

//----------------------------------------------------------------
// For testing, it may be more convenient to simply print the
//   GPS fix fields to the Serial Monitor.  Simply uncomment
//   this define to skip all SD operations.  An SD card module
//   does not have to be connected.
#define SIMULATE_SD

#ifdef SIMULATE_SD

  auto &logfile = DEBUG_PORT;

#else

  File logfile;

#endif

//----------------------------------------------------------------
//  Utility to print a long integer like it's a float 
//    with 9 significant digits.

void printL( Print & outs, int32_t degE7 )
{
  // Extract and print negative sign
  if (degE7 < 0) {
    degE7 = -degE7;
    outs.print( '-' );
  }

  // Whole degrees
  int32_t deg = degE7 / 10000000L;
  outs.print( deg );
  outs.print( '.' );

  // Get fractional degrees
  degE7 -= deg*10000000L;

  // Print leading zeroes, if needed
  if (degE7 < 10L)
    outs.print( F("000000") );
  else if (degE7 < 100L)
    outs.print( F("00000") );
  else if (degE7 < 1000L)
    outs.print( F("0000") );
  else if (degE7 < 10000L)
    outs.print( F("000") );
  else if (degE7 < 100000L)
    outs.print( F("00") );
  else if (degE7 < 1000000L)
    outs.print( F("0") );
  
  // Print fractional degrees
  outs.print( degE7 );
}

//----------------------------------------------------------------
//  Because the SD write can take a long time, GPSisr will store 
//  parsed data in the NMEAGPS 'buffer' array until GPSloop can get to it.
//
//  The number of elements you should have in the array depends on
//  two things: the speed of your SD card, and the update rate you
//  have chosen.
//
//  For an update rate of 10Hz (100ms period), two fixes are probably
//  enough.  Most cards take ~100ms to complete a write of 512 bytes.
//  With FIX_MAX=2, two complete fixes can be stored, which were
//  received in 200ms.  A third fix can be started, giving a little
//  more time before an overrun occurs, a total of about 250ms.
//
//  If your card is slower or your update rate is faster, you should
//  first build and run this program to determine the speed of your
//  card.  The time it takes to log one record is printed to the log
//  file.
//
//  After the program has run for a minute or two, remove the
//  card and examine the loggingTimes.  You may see that an interval
//  was skipped, and you will also see an OVERRUN message on the
//  DEBUG_PORT (usually Serial).
//
//  You should calculate a new FIX_MAX from the maximum loggingTime 
//  you see in the log file:
//
//    FIX_MAX = (max loggingTime)/(update period) + 1;
//
//  For example, if the max loggingTime is 160ms, and the update period is
//  100ms (10Hz), then FIX_MAX = 160/100 + 1 = 2.
//
//  Change the FIX_MAX value, build and run the program again.  The
//  SD log file should now contain all records, and no OVERRUN
//  messages should have been printed on the DEBUG_PORT.
//
//  If you do see an OVERRUN message, examine the loggingTime to see
//  if it exceeded the maximum value from the previous build, and
//  increase FIX_MAX.
//
//  If you are also printing data to a Serial device, you could be
//  printing too much information.  In general, you must print less than
//  (baudrate/10) characters per second.  For example, if your baudrate 
//  is 9600, you must print less than 960 characters per second.  And if
//  the update rate is 10Hz, you must print no more than 96 characters
//  per update.
//
//  There are also restrictions on how much you should print to Serial in one 
//  section of code.  If you print more than 64 characters (the output buffer 
//  size), then some prints will block until all characters can be stored in the
//  output buffer.
//
//  For example, if you try to print 80 characters, the first 64 characters
//  will be immediately stored in the output buffer. However, the last 16 
//  characters must wait until the output buffer has room for 16 more 
//  characters.  That takes 16 * (10/baudrate) milliseconds.  At 9600 baud
//  that will take 17ms.  The loggingTimes will show no less than 17ms per
//  record, and will occasionally include the longer SD write time of ~100ms.

//----------------------------------------------------------------

static void GPSloop()
{
  if (gps.available()) {

    gps_fix fix = gps.read();

    // Log the fix information if we have a location and time
    
    if (fix.valid.location && fix.valid.time) {

      static uint16_t lastLoggingTime  = 0;
             uint16_t startLoggingTime = millis();

      // If you like the CSV format implemented in Streamers.h,
      //   you could replace all these prints with 
      // trace_all( logFile, fix ); // uncomment include Streamers.h

      printL( logfile, fix.latitudeL() );
      logfile.print( ',' );
      printL( logfile, fix.longitudeL() );
      logfile.print(',');

      if (fix.dateTime.hours < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.hours);
      logfile.print( ':' );
      if (fix.dateTime.minutes < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.minutes);
      logfile.print( ':' );
      if (fix.dateTime.seconds < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.seconds);
      logfile.print( '.' );
      if (fix.dateTime_cs < 10)
         logfile.print( '0' ); // leading zero for .05, for example
      logfile.print(fix.dateTime_cs);
      logfile.print(',');

      logfile.print( lastLoggingTime ); // write how long the previous logging took
      logfile.println();

      // flush() is used to empty the contents of the SD buffer to the SD. 
      // If you don't call flush, the data will fill up the SdFat buffer 
      // of 512bytes and flush itself automatically.
      //
      // To avoid losing data or corrupting the SD card file system, you must 
      // call flush() at least once (or close()) before powering down or pulling 
      // the SD card.
      //
      // It is *strongly* recommended that you use some external event 
      // to close the file.  For example, staying within 50m of the moving
      // average location for 1 minute, or using a switch to start and stop 
      // logging.  It would also be good to provide a visual indication 
      // that it is safe to power down and/or remove the card,  perhaps via
      // the LED.
      //
      // To reduce the amount of data that may be lost by an abnormal shut down,
      // you can call flush() periodically.
      //
      // Depending on the amount of data you are printing, you can save 
      // *a lot* of CPU time by not flushing too frequently.  BTW, flushing
      // every time at 5Hz is too frequent.

      // This shows how to flush once a second.
      static uint16_t lastFlushTime = 0;

      if (startLoggingTime - lastFlushTime > 1000) {
        lastFlushTime = startLoggingTime; // close enough
        logfile.flush();
      }

      #ifdef SIMULATE_SD
        // Simulate the delay of writing to an SD card.  These times are
        //   very long.  This is intended to show (and test) the overrun detection.
        //
        // On a 1Hz GPS, delaying more than 2 seconds here, or more than
        //   2 seconds in two consecutive updates, will cause OVERRUN.
        //
        // Feel free to try different delays to simulate the actual behavior
        //   of your SD card.

        uint16_t t = random(0,5); // 0..4
        t += 3;                   // 3..7
        t = t*t*t*t;              // 81..2401ms
        delay( t ); // cause an OVERRUN
      #endif

      // All logging is finished, figure out how long that took.
      //   This will be written in the *next* record.    
      lastLoggingTime = (uint16_t) millis() - startLoggingTime;
    }
  }

} // GPSloop

//----------------------------------------------------------------

void GPSisr( uint8_t c )
{
  gps.handle( c );

} // GPSisr

//----------------------------------------------------------------
//  This routine waits for GPSisr to provide 
//  a fix that has a valid location.
//
//  The LED is slowly flashed while it's waiting.

static void waitForFix()
{
  DEBUG_PORT.print( F("Waiting for GPS fix...") );

  uint16_t lastToggle = millis();

  for (;;) {
    if (gps.available()) {
      if (gps.read().valid.location)
        break; // Got it!
    }

    // Slowly flash the LED until we get a fix
    if ((uint16_t) millis() - lastToggle > 500) {
      lastToggle += 500;
      digitalWrite( LED, !digitalRead(LED) );
      DEBUG_PORT.write( '.' );
    }
  }
  DEBUG_PORT.println();

  digitalWrite( LED, LOW );

  gps.overrun( false ); // we had to wait a while...

} // waitForFix

//----------------------------------------------------------------

void setup()
{
  DEBUG_PORT.begin(9600);
  while (!DEBUG_PORT)
    ; // wait for serial port to connect. 

  DEBUG_PORT.println( F("NMEASDlog_UnoAttempt.ino started!") );
  DEBUG_PORT.print( F("fix size = ") );
  DEBUG_PORT.println( sizeof(gps_fix) );
  DEBUG_PORT.print( NMEAGPS_FIX_MAX );
  DEBUG_PORT.println( F(" GPS updates can be buffered.") );
  
  if (gps.merging != NMEAGPS::EXPLICIT_MERGING)
    DEBUG_PORT.println( F("Warning: EXPLICIT_MERGING should be enabled for best results!") );

  gpsPort.attachInterrupt( GPSisr );
  gpsPort.begin( 9600 );

  //  Configure the GPS.  These are commands for MTK GPS devices.  Other
  //    brands will have different commands.
  gps.send_P( &gpsPort, F("PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") ); // RMC only for MTK GPS devices
  gps.send_P( &gpsPort, F("PMTK220,100") ); // 10Hz update rate for MTK GPS devices

  // Enable the LED for blinking feedback
  pinMode( LED, OUTPUT );

  initSD();

  waitForFix();

} // setup

//----------------------------------------------------------------

void loop()
{
  GPSloop();

  if (gps.overrun()) {
    gps.overrun( false );
    DEBUG_PORT.println( F("DATA OVERRUN: fix data lost!") );
  }
}

//----------------------------------------------------------------

void initSD()
{
  #ifdef SIMULATE_SD

    DEBUG_PORT.println( F("  Simulating SD.") );
    
  #else

    DEBUG_PORT.println( F("Initializing SD card...") );

    // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) {
      DEBUG_PORT.println( F("  SD card failed, or not present") );
      // don't do anything more:

      // Flicker the LED
      while (true) {
        digitalWrite(LED,HIGH);
        delay(75);
        digitalWrite(LED,LOW);
        delay(75);
      }
    }

    DEBUG_PORT.println( F("  SD card initialized.") );

    // Pick a numbered filename, 00 to 99.
    char filename[15] = "data_##.txt";

    for (uint8_t i=0; i<100; i++) {
      filename[5] = '0' + i/10;
      filename[6] = '0' + i%10;
      if (!SD.exists(filename)) {
        // Use this one!
        break;
      }
    }

    logfile = SD.open(filename, FILE_WRITE);
    if (!logfile) {
      DEBUG_PORT.print( F("Couldn't create ") ); 
      DEBUG_PORT.println(filename);

      // If the file can't be created for some reason this leaves the LED on
      //   so I know there is a problem
      digitalWrite(LED,HIGH);

      while (true) {}
    }

    DEBUG_PORT.print( F("Writing to ") ); 
    DEBUG_PORT.println(filename);

    // GPS Visualizer requires a header to identify the CSV fields.
    // If you are saving other data or don't need this, simply remove/change it
    logfile.println( F("latitude,longitude,time,loggingTime") ); 

    //trace_header( logfile ); // and uncomment #include Streamers.h
    
  #endif
} // initSD

I'm using an Arduino Uno, pins 0(Rx) & 1(Tx) for the GPS, and pins 10 (CS), 11 (MOSI), 12 (MISO), 13(SCK) for the SD card.

But even without the SDcard set-up I should get the complete void setup print and the simulate SD info in the serial monitor, right?

Thank you for any guidance you can offer.

You can't use pins 0 and 1 for the GPS and the serial monitor. You need to either move your GPS to other pins and use SoftwareSerial or use a board that has multiple hardware serial ports.

I am not sure why, but it works in the example sketch NMEA_isr (posted below). I can't upload a sketch while pins 0 and 1 are connected to the GPS, but I can write to the serial monitor while "listening" on pin 0. Here is the Serial Monitor while running the NMEA_isr.ino with the M10Q attached at pins 0 and 1 but without a gps fix (I'm inside at the moment, but I've had it return complete, accurate data lines with a fix when working on a laptop outdoors).

10:30:31.958 -> NMEA_isr.INO: started
10:30:31.958 -> fix object size = 31
10:30:31.991 -> NMEAGPS object size = 84
10:30:32.024 -> Looking for GPS device on NeoSerial
10:30:32.057 -> Status,UTC Date/Time,Lat,Lon,Hdg,Spd,Alt,Sats,Rx ok,Rx err,Rx chars,
10:30:32.845 -> 0,,,,,,,,2,0,59,
10:30:33.853 -> 0,,,,,,,0,11,0,395,
10:30:34.846 -> 0,,,,,,,0,20,0,731,
10:30:35.821 -> 0,,,,,,,0,34,0,1167,
10:30:36.836 -> 0,,,,,,,0,43,0,1503,
10:30:37.824 -> 0,,,,,,,0,52,0,1839,

#include <NMEAGPS.h>

//======================================================================
//  Program: NMEA_isr.ino
//
//  Prerequisites:
//     1) NMEA.ino works with your device
//
//  Description:  This minimal program parses the GPS data during the 
//     RX character interrupt.  The ISR passes the character to
//     the GPS object for parsing.  The GPS object will add gps_fix 
//     structures to a buffer that can be later read() by loop().
//
//  License:
//    Copyright (C) 2014-2017, SlashDevin
//
//    This file is part of NeoGPS
//
//    NeoGPS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    NeoGPS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with NeoGPS.  If not, see <http://www.gnu.org/licenses/>.
//
//======================================================================

#include <GPSport.h>

#include <Streamers.h>

// Check configuration

#ifndef NMEAGPS_INTERRUPT_PROCESSING
  #error You must define NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h! There are two instances of this file, in src AND extras_congif!
#endif

static NMEAGPS   gps;

//--------------------------

static void GPSisr( uint8_t c )
{
  gps.handle( c );

} // GPSisr

//--------------------------

void setup()
{
  DEBUG_PORT.begin(9600);
  while (!DEBUG_PORT)
    ;

  DEBUG_PORT.print( F("NMEA_isr.INO: started\n") );
  DEBUG_PORT.print( F("fix object size = ") );
  DEBUG_PORT.println( sizeof(gps.fix()) );
  DEBUG_PORT.print( F("NMEAGPS object size = ") );
  DEBUG_PORT.println( sizeof(gps) );
  DEBUG_PORT.println( F("Looking for GPS device on " GPS_PORT_NAME) );

  trace_header( DEBUG_PORT );

  DEBUG_PORT.flush();

  gpsPort.attachInterrupt( GPSisr );
  gpsPort.begin( 9600 );
}

//--------------------------

void loop()
{
  if (gps.available()) {
    // Print all the things!
    trace_all( DEBUG_PORT, gps, gps.read() );
  }

  if (gps.overrun()) {
    gps.overrun( false );
    DEBUG_PORT.println( F("DATA OVERRUN: took too long to print GPS data!") );
  }
}

But something is obviously different with the code from the first post, because in there the serial monitor can't even print the first line of void setup before it goes to gibberish. I'll examine the two sets of code for discrepancies in using NeoHWSerial.h or something along those lines though.

If anyone else sees something glaring, I'm open to more troubleshooting hints or direction.
Thanks!

Update:

I've managed to get it through the void setup by adding the

DEBUG_PORT.flush();

line from the NMEA_isr code above.

Everything else is working well now!
Here is the finished script in case it helps anyone else.

#include <Arduino.h>
#include <NMEAGPS.h>

//======================================================================
//  Program: NMEASDlog.ino
  //
  //  Description:  This program is an interrupt-driven GPS logger.
  //    It uses the alternative serial port libraries NeoHWSerial,
  //    NeoSWSerial, or NeoICSerial.
  //
  //  Prerequisites:
  //     1) You have completed the requirements for NMEA_isr.ino
  //     2) You have connected an SPI SD card and verified it is working
  //        with other SD utilities.
  //     3) For logging faster than the default 1Hz rate, you have
  //        identified the required commands for your GPS device.
  //
  //  'Serial' is for debug output to the Serial Monitor window.
  //
  //  License:
  //    Copyright (C) 2014-2017, SlashDevin
  //
  //    This file is part of NeoGPS
  //
  //    NeoGPS is free software: you can redistribute it and/or modify
  //    it under the terms of the GNU General Public License as published by
  //    the Free Software Foundation, either version 3 of the License, or
  //    (at your option) any later version.
  //
  //    NeoGPS is distributed in the hope that it will be useful,
  //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  //    GNU General Public License for more details.
  //
  //    You should have received a copy of the GNU General Public License
  //    along with NeoGPS.  If not, see <http://www.gnu.org/licenses/>.
  //
//======================================================================
// SDFat examples work
// NEMA and NEMAisr both work

  #include <GPSport.h>

  #include <Streamers.h>

//----------------------------------------------------------------
// Check configuration

  #if !defined( GPS_FIX_TIME ) || !defined( GPS_FIX_LOCATION )
    #error You must define TIME and LOCATION in GPSfix_cfg.h
  #endif

  #if !defined( NMEAGPS_PARSE_RMC )
    #error You must define NMEAGPS_PARSE_RMC in NMEAGPS_cfg.h!
  #endif

  #ifndef NMEAGPS_INTERRUPT_PROCESSING
    #error You must define NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h!
  #endif

  static const int LED = 13;

  static NMEAGPS  gps; 

//----------------------------------------------------------------
// SD card includes and declarations

  #include <SPI.h>
  #include <SdFat.h>

  SdFat SD;
  const int chipSelect = 10;

//----------------------------------------------------------------
// Simulate SD For testing, it may be more convenient to simply print the
  //   GPS fix fields to the Serial Monitor.  Simply uncomment
  //   this define to skip all SD operations.  An SD card module
  //   does not have to be connected.
//  #define SIMULATE_SD

  #ifdef SIMULATE_SD

    auto &logfile = DEBUG_PORT;

  #else

    File logfile;

  #endif

//----------------------------------------------------------------
//  Utility to print a long integer like it's a float 
//    with 9 significant digits.

void printL( Print & outs, int32_t degE7 )
{
  // Extract and print negative sign
  if (degE7 < 0) {
    degE7 = -degE7;
    outs.print( '-' );
  }

  // Whole degrees
  int32_t deg = degE7 / 10000000L;
  outs.print( deg );
  outs.print( '.' );

  // Get fractional degrees
  degE7 -= deg*10000000L;

  // Print leading zeroes, if needed
  if (degE7 < 10L)
    outs.print( F("000000") );
  else if (degE7 < 100L)
    outs.print( F("00000") );
  else if (degE7 < 1000L)
    outs.print( F("0000") );
  else if (degE7 < 10000L)
    outs.print( F("000") );
  else if (degE7 < 100000L)
    outs.print( F("00") );
  else if (degE7 < 1000000L)
    outs.print( F("0") );
  
  // Print fractional degrees
  outs.print( degE7 );
}

//----------------------------------------------------------------
  //  Because the SD write can take a long time, GPSisr will store 
  //  parsed data in the NMEAGPS 'buffer' array until GPSloop can get to it.
  //
  //  The number of elements you should have in the array depends on
  //  two things: the speed of your SD card, and the update rate you
  //  have chosen.
  //
  //  For an update rate of 10Hz (100ms period), two fixes are probably
  //  enough.  Most cards take ~100ms to complete a write of 512 bytes.
  //  With FIX_MAX=2, two complete fixes can be stored, which were
  //  received in 200ms.  A third fix can be started, giving a little
  //  more time before an overrun occurs, a total of about 250ms.
  //
  //  If your card is slower or your update rate is faster, you should
  //  first build and run this program to determine the speed of your
  //  card.  The time it takes to log one record is printed to the log
  //  file.
  //
  //  After the program has run for a minute or two, remove the
  //  card and examine the loggingTimes.  You may see that an interval
  //  was skipped, and you will also see an OVERRUN message on the
  //  DEBUG_PORT (usually Serial).
  //
  //  You should calculate a new FIX_MAX from the maximum loggingTime 
  //  you see in the log file:
  //
  //    FIX_MAX = (max loggingTime)/(update period) + 1;
  //
  //  For example, if the max loggingTime is 160ms, and the update period is
  //  100ms (10Hz), then FIX_MAX = 160/100 + 1 = 2.
  //
  //  Change the FIX_MAX value, build and run the program again.  The
  //  SD log file should now contain all records, and no OVERRUN
  //  messages should have been printed on the DEBUG_PORT.
  //
  //  If you do see an OVERRUN message, examine the loggingTime to see
  //  if it exceeded the maximum value from the previous build, and
  //  increase FIX_MAX.
  //
  //  If you are also printing data to a Serial device, you could be
  //  printing too much information.  In general, you must print less than
  //  (baudrate/10) characters per second.  For example, if your baudrate 
  //  is 9600, you must print less than 960 characters per second.  And if
  //  the update rate is 10Hz, you must print no more than 96 characters
  //  per update.
  //
  //  There are also restrictions on how much you should print to Serial in one 
  //  section of code.  If you print more than 64 characters (the output buffer 
  //  size), then some prints will block until all characters can be stored in the
  //  output buffer.
  //
  //  For example, if you try to print 80 characters, the first 64 characters
  //  will be immediately stored in the output buffer. However, the last 16 
  //  characters must wait until the output buffer has room for 16 more 
  //  characters.  That takes 16 * (10/baudrate) milliseconds.  At 9600 baud
  //  that will take 17ms.  The loggingTimes will show no less than 17ms per
  //  record, and will occasionally include the longer SD write time of ~100ms.

//----------------------------------------------------------------

static void GPSloop()
{
  if (gps.available()) {

    gps_fix fix = gps.read();

    // Log the fix information if we have a location and time
    
    if (fix.valid.location && fix.valid.time) {

      static uint16_t lastLoggingTime  = 0;
             uint16_t startLoggingTime = millis();

      // If you like the CSV format implemented in Streamers.h,
      //   you could replace all these prints with 
      // trace_all( logFile, fix ); // uncomment include Streamers.h

      printL( logfile, fix.latitudeL() );
      logfile.print( ',' );
      printL( logfile, fix.longitudeL() );
      logfile.print(',');

      if (fix.dateTime.hours < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.hours);
      logfile.print( ':' );
      if (fix.dateTime.minutes < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.minutes);
      logfile.print( ':' );
      if (fix.dateTime.seconds < 10)
        logfile.print( '0' );
      logfile.print(fix.dateTime.seconds);
      logfile.print( '.' );
      if (fix.dateTime_cs < 10)
         logfile.print( '0' ); // leading zero for .05, for example
      logfile.print(fix.dateTime_cs);
      logfile.print(',');

      logfile.print( lastLoggingTime ); // write how long the previous logging took
      logfile.println();

      // flush() is used to empty the contents of the SD buffer to the SD. 
      // If you don't call flush, the data will fill up the SdFat buffer 
      // of 512bytes and flush itself automatically.
      //
      // To avoid losing data or corrupting the SD card file system, you must 
      // call flush() at least once (or close()) before powering down or pulling 
      // the SD card.
      //
      // It is *strongly* recommended that you use some external event 
      // to close the file.  For example, staying within 50m of the moving
      // average location for 1 minute, or using a switch to start and stop 
      // logging.  It would also be good to provide a visual indication 
      // that it is safe to power down and/or remove the card,  perhaps via
      // the LED.
      //
      // To reduce the amount of data that may be lost by an abnormal shut down,
      // you can call flush() periodically.
      //
      // Depending on the amount of data you are printing, you can save 
      // *a lot* of CPU time by not flushing too frequently.  BTW, flushing
      // every time at 5Hz is too frequent.

      // This shows how to flush once a second.
      static uint16_t lastFlushTime = 0;

      if (startLoggingTime - lastFlushTime > 1000) {
        lastFlushTime = startLoggingTime; // close enough
        logfile.flush();
      }

      #ifdef SIMULATE_SD
        // Simulate the delay of writing to an SD card.  These times are
        //   very long.  This is intended to show (and test) the overrun detection.
        //
        // On a 1Hz GPS, delaying more than 2 seconds here, or more than
        //   2 seconds in two consecutive updates, will cause OVERRUN.
        //
        // Feel free to try different delays to simulate the actual behavior
        //   of your SD card.

        uint16_t t = random(0,5); // 0..4
        t += 3;                   // 3..7
        t = t*t*t*t;              // 81..2401ms
        delay( t ); // cause an OVERRUN
      #endif

      // All logging is finished, figure out how long that took.
      //   This will be written in the *next* record.    
      lastLoggingTime = (uint16_t) millis() - startLoggingTime;
    }
  }

} // GPSloop

//----------------------------------------------------------------

void GPSisr( uint8_t c )
{
  gps.handle( c );

} // GPSisr

//----------------------------------------------------------------
//  This routine waits for GPSisr to provide 
//  a fix that has a valid location.
//
//  The LED is slowly flashed while it's waiting.

static void waitForFix()
{
  DEBUG_PORT.print( F("Waiting for GPS fix...") );

  uint16_t lastToggle = millis();

  for (;;) {
    if (gps.available()) {
      if (gps.read().valid.location)
        break; // Got it!
    }

    // Slowly flash the LED until we get a fix
    if ((uint16_t) millis() - lastToggle > 500) {
      lastToggle += 500;
      digitalWrite( LED, !digitalRead(LED) );
      DEBUG_PORT.write( '.' );
    }
  }
  DEBUG_PORT.println();

  digitalWrite( LED, LOW );

  gps.overrun( false ); // we had to wait a while...

} // waitForFix

//----------------------------------------------------------------

void setup()
{
  DEBUG_PORT.begin(9600);
  while (!DEBUG_PORT)
    ; // wait for serial port to connect. 

  DEBUG_PORT.println( F("NMEASDlog_UnoAttempt.ino started!") );
  DEBUG_PORT.print( F("fix object size = ") );
  DEBUG_PORT.println( sizeof(gps_fix()) );
  DEBUG_PORT.println( NMEAGPS_FIX_MAX );
  DEBUG_PORT.print( F("NMEAGPS object size = ") );
  DEBUG_PORT.println( sizeof(gps) );
  DEBUG_PORT.println( F("Looking for GPS device on " GPS_PORT_NAME) );
  DEBUG_PORT.println( F(" GPS updates can be buffered. ") );
  
  trace_header( DEBUG_PORT );

  DEBUG_PORT.flush();
  
  if (gps.merging != NMEAGPS::EXPLICIT_MERGING)
    DEBUG_PORT.println( F("Warning: EXPLICIT_MERGING should be enabled for best results!") );

  gpsPort.attachInterrupt( GPSisr );
  gpsPort.begin( 9600 );

  //  Configure the GPS.  These are commands for MTK GPS devices.  Other
  //    brands will have different commands.
  gps.send_P( &gpsPort, F("PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") ); // RMC only for MTK GPS devices
  gps.send_P( &gpsPort, F("PMTK220,100") ); // 10Hz update rate for MTK GPS devices

  // Enable the LED for blinking feedback
  pinMode( LED, OUTPUT );

  initSD();

  waitForFix();

} // setup

//----------------------------------------------------------------

void loop()
{
  GPSloop();

  if (gps.overrun()) {
    gps.overrun( false );
    DEBUG_PORT.println( F("DATA OVERRUN: fix data lost!") );
  }
}

//----------------------------------------------------------------

void initSD()
{
  #ifdef SIMULATE_SD

    DEBUG_PORT.println( F("  Simulating SD.") );
    
  #else

    DEBUG_PORT.println( F("Initializing SD card...") );

    // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) {
      DEBUG_PORT.println( F("  SD card failed, or not present") );
      // don't do anything more:

      // Flicker the LED
      while (true) {
        digitalWrite(LED,HIGH);
        delay(75);
        digitalWrite(LED,LOW);
        delay(75);
      }
    }

    DEBUG_PORT.println( F("  SD card initialized.") );

    // Pick a numbered filename, 00 to 99.
    char filename[15] = "data_##.txt";

    for (uint8_t i=0; i<100; i++) {
      filename[5] = '0' + i/10;
      filename[6] = '0' + i%10;
      if (!SD.exists(filename)) {
        // Use this one!
        break;
      }
    }

    logfile = SD.open(filename, FILE_WRITE);
    if (!logfile) {
      DEBUG_PORT.print( F("Couldn't create ") ); 
      DEBUG_PORT.println(filename);

      // If the file can't be created for some reason this leaves the LED on
      //   so I know there is a problem
      digitalWrite(LED,HIGH);

      while (true) {}
    }

    DEBUG_PORT.print( F("Writing to ") ); 
    DEBUG_PORT.println(filename);

    // GPS Visualizer requires a header to identify the CSV fields.
    // If you are saving other data or don't need this, simply remove/change it
    logfile.println( F("latitude,longitude,time,loggingTime") ); 

    //trace_header( logfile ); // and uncomment #include Streamers.h
    
  #endif
} // initSD

and here is the serial monitor:

NMEASDlog_UnoAttempt.ino started!
13:24:48.813 -> fix object size = 1
13:24:48.845 -> 1
13:24:48.845 -> NMEAGPS object size = 84
13:24:48.877 -> Looking for GPS device on NeoSerial
13:24:48.912 -> GPS updates can be buffered.
13:24:48.944 -> Status,UTC Date/Time,Lat,Lon,Hdg,Spd,Alt,Sats,Rx ok,Rx err,Rx chars,
13:24:49.010 -> $PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,029
13:24:49.075 -> $PMTK220,100
2F
13:24:49.075 -> Initializing SD card...
13:24:49.108 -> SD card initialized.
13:24:49.141 -> Writing to data_03.txt
13:24:49.187 -> Waiting for GPS fix....................

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.