How To: Ultimate GPS Data Logging on a Mega

Breakout boards, sensors, other Adafruit kits, etc.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Autonomous_Sailboat
 
Posts: 31
Joined: Thu Dec 22, 2022 10:26 pm

How To: Ultimate GPS Data Logging on a Mega

Post by Autonomous_Sailboat »

Like many others in this Forum, I have spent quite a lot of time trying to figure out how to get a Adafruit Ultimate GPS shield to store data on it's data logger when it is mounted on an Arduino Mega 2560. While there are many troubleshooting posts on this subject there really isn't one that is more of a how-to post. After much frustration I finally figured it out and want to share my findings so that -hopefully- it is of use to someone else trying to do the same thing. As always, corrections to my below are welcome.

The shield_sdlog.ino sketch is/was originally included as an example provided in the Adafruit_GPS_Library distribution that supports the Adafruit Ultimate GPS Logger Shield (Product ID: 1272). I assume that the intent is to demonstrate how to capture GPS data and write that data to the associated logger SD card. A very cool capability, indeed.

While this sketch works as-is when an Arduino Uno is paired with the GPS shield, it will fail when used on an Arduino Mega. Several modifications to the sketch, the wiring, AND the hardware itself in order to perform successfully on a Mega.

The two modifications to the sketch are copied and annotated below. Specifically:
* I2C communication on TX1/RX1 (i.e., pins 18 and 19) via Serial1 is required.
* Designating Chip Select to be pin 53.

Code: Select all

// The shield_sdlog.ino sketch is/was originally included as an example provided in
// the Adafruit_GPS_Library distribution that supports the Adafruit Ultimate GPS Logger
// Shield (Product ID: 1272).  I assume that the intent is to demonstrate how to
// capture GPS data and write that data to the associated logger SD card.
//
// While this sketch works as-is when an Arduino Uno is paired with the GPS shield,
// it will fail when used on an Arduino Mega.  Several modifications to the sketch,
// the wiring, AND the hardware itself in order to perform successfully on a Mega.
//
// The two modifications to the sketch are annotated below.  Specifically:
//   * I2C communication on TX1/RX1 (i.e., pins 18 and 19) via Serial1 is required.
//   * Designating Chip Select to be pin 53.
//
// Two sets of wire connections must be made, one to enable Serial1 communication to
// the GPS board and the other to enable SPI communication to the SD card.
//
// For Serial1 communications make the following connections:
//   * GPS digital pin 7 (RX) to Mega communications pin 18 (TX1)
//   * GPS digital pin 8 (TX) to Mega communications pin 19 (RX1)
//
// For SPI communications make the following connections:
//   * GPS digital I/O pin 10 to Mega pin 53 (SS / CS)
//   * GPS digital I/O pin 11 to Mega pin 51 (MOSI / COPI)
//   * GPS digital I/O pin 12 to Mega pin 50 (MISO / CIPO)
//   * GPS digital I/O pin 13 to Mega pin 52 (CLK / SLK)
//
// One set of hardware modification must be made to electrically isolate the GPS shield
// from the Mega.  Specifically:
//   * Cut short the GPS shield's header pins 10-13.
//
// See pages 11-13 of the Arduino Mega datasheet (available from the Arduino website)
// for information regarding the Mega's pinouts.
//
// The above has been tested and seems to work.  I wish you great success in your effort.
//
#include <SPI.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <SD.h>
#include <avr/sleep.h>

// Ladyada's logger modified by Bill Greiman to use the SdFat library
//
// This code shows how to listen to the GPS module in an interrupt
// which allows the program to have more 'freedom' - just parse
// when a new NMEA sentence is available! Then access data when
// desired.
//
// Tested and works great with the Adafruit Ultimate GPS Shield
// using MTK33x9 chipset
//    ------> http://www.adafruit.com/products/
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
// Fllybob added 10 sec logging option



//--------------------MODIFICATION--------------------//
// SoftwareSerial mySerial(8, 7);   // This is as published.

#define mySerial Serial1            // This works for the Mega.
//----------------END OF MODIFICATION--------------------//
Adafruit_GPS GPS(&mySerial);        // This works for the Mega.



// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO  true
/* set to true to only log to SD when GPS has a fix, for debugging, keep it false */
#define LOG_FIXONLY false

// this keeps track of whether we're using the interrupt
// off by default!
#ifndef ESP8266 // Sadly not on ESP8266
bool usingInterrupt = false;
#endif



// Set the pins used
//--------------------MODIFICATION--------------------//
// #define chipSelect 10  // This is as published.

#define chipSelect 53     // This works for the Mega.
//----------------END OF MODIFICATION--------------------//
#define ledPin 13



File logfile;

// read a Hex value and return the decimal equivalent
uint8_t parseHex(char c) {
  if (c < '0')
    return 0;
  if (c <= '9')
    return c - '0';
  if (c < 'A')
    return 0;
  if (c <= 'F')
    return (c - 'A')+10;
}

// blink out an error code
void error(uint8_t errno) {
  /*
  if (SD.errorCode()) {
   putstring("SD error: ");
   Serial.print(card.errorCode(), HEX);
   Serial.print(',');
   Serial.println(card.errorData(), HEX);
   }
   */
  while(1) {
    uint8_t i;
    for (i=0; i<errno; i++) {
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
    }
    for (i=errno; i<10; i++) {
      delay(200);
    }
  }
}

void setup() {
  // for Leonardos, if you want to debug SD issues, uncomment this line
  // to see serial output
  //while (!Serial);

  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  Serial.begin(115200);
  Serial.println("\r\nUltimate GPSlogger Shield");
  pinMode(ledPin, OUTPUT);

  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
 
  if (!SD.begin(chipSelect)) {
    Serial.println("Card init. failed!");
    error(2);
  }
  char filename[15];
  strcpy(filename, "GPSLOG00.TXT");
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = '0' + i/10;
    filename[7] = '0' + i%10;
    // create if does not exist, do not open existing, write, sync after write
    if (! SD.exists(filename)) {
      break;
    }
  }

  logfile = SD.open(filename, FILE_WRITE);
  if( ! logfile ) {
    Serial.print("Couldnt create ");
    Serial.println(filename);
    error(3);
  }
  Serial.print("Writing to ");
  Serial.println(filename);

  // connect to the GPS at the desired rate
  GPS.begin(9600);

  // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  // uncomment this line to turn on only the "minimum recommended" data
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
  // For logging data, we don't suggest using anything but either RMC only or RMC+GGA
  // to keep the log files at a reasonable size
  // Set the update rate
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 100 millihertz (once every 10 seconds), 1Hz or 5Hz update rate

  // Turn off updates on antenna status, if the firmware permits it
  GPS.sendCommand(PGCMD_NOANTENNA);

  // the nice thing about this code is you can have a timer0 interrupt go off
  // every 1 millisecond, and read data from the GPS for you. that makes the
  // loop code a heck of a lot easier!
#ifndef ESP8266 // Not on ESP8266
  useInterrupt(true);
#endif

  Serial.println("Ready!");
}


// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
#ifndef ESP8266 // Not on ESP8266
ISR(TIMER0_COMPA_vect) {
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
  #ifdef UDR0
      if (GPSECHO)
        if (c) UDR0 = c;
      // writing direct to UDR0 is much much faster than Serial.print
      // but only one character can be written at a time.
  #endif
}

void useInterrupt(bool v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  }
  else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}
#endif // ESP8266

void loop() {
  if (! usingInterrupt) {
    // read data from the GPS in the 'main loop'
    char c = GPS.read();
    // if you want to debug, this is a good time to do it!
    if (GPSECHO)
      if (c) Serial.print(c);
  }

  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences!
    // so be very wary if using OUTPUT_ALLDATA and trying to print out data

    // Don't call lastNMEA more than once between parse calls!  Calling lastNMEA
    // will clear the received flag and can cause very subtle race conditions if
    // new data comes in before parse is called again.
    char *stringptr = GPS.lastNMEA();

    if (!GPS.parse(stringptr))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another

    // Sentence parsed!
    Serial.println("OK");
    if (LOG_FIXONLY && !GPS.fix) {
      Serial.print("No Fix");
      return;
    }

    // Rad. lets log it!
    Serial.println("Log");

    uint8_t stringsize = strlen(stringptr);
    if (stringsize != logfile.write((uint8_t *)stringptr, stringsize))    //write the string to the SD file
        error(4);
    if (strstr(stringptr, "RMC") || strstr(stringptr, "GGA"))   logfile.flush();
    Serial.println();
  }
}

Additionally, two sets of wire connections must be made, one to enable Serial1 communication to the GPS board and the other to enable SPI communication to the SD card.

For Serial1 communications make the following connections:
* GPS digital pin 7 (RX) to Mega communications pin 18 (TX1)
* GPS digital pin 8 (TX) to Mega communications pin 19 (RX1)

For SPI communications make the following connections:
* GPS digital I/O pin 10 to Mega pin 53 (SS / CS)
* GPS digital I/O pin 11 to Mega pin 51 (MOSI / COPI)
* GPS digital I/O pin 12 to Mega pin 50 (MISO / CIPO)
* GPS digital I/O pin 13 to Mega pin 52 (CLK / SLK)

One set of hardware modifications must be made to electrically isolate the GPS shield from the Mega. Specifically:
* Cut short the GPS shield's header pins 10-13.

Saving GPS data on SD card.jpg
Saving GPS data on SD card.jpg (945.49 KiB) Viewed 120 times

See pages 11-13 of the Arduino Mega datasheet (available from the Arduino website) for information regarding the Mega's pinouts. That helps to understand why the additional wiring is required.

The above has been tested and seems to work. I wish you great success in your effort.

User avatar
Franklin97355
 
Posts: 23939
Joined: Mon Apr 21, 2008 2:33 pm

Re: How To: Ultimate GPS Data Logging on a Mega

Post by Franklin97355 »

Looks interesting. I'll let the others know, thanks.

User avatar
adafruit2
 
Posts: 22193
Joined: Fri Mar 11, 2005 7:36 pm

Re: How To: Ultimate GPS Data Logging on a Mega

Post by adafruit2 »

yay thanks, can we use this image and text in the product guide as a user contributed suggestion?

Locked
Please be positive and constructive with your questions and comments.

Return to “Other Products from Adafruit”