Go Down

Topic: trying to understand example GPS logging code? (Read 70 times) previous topic - next topic

mysteriousmonkey29

Hello, I'm using an adafruit ultimate GPS logger shield with the Arduino Uno. I'm in the process of modifying an example sketch (shield_sdlog) to fit my exact purpose. I just want to log latitude and longitude to a log file in a format that I can control. Originally, I was trying to parse through the default NMEA sentences for the latitude and longitude, but then I realized that it's easier if I just access them directly, so I switched the code to do that. However, then I began to wonder if the commands in the setup code to tell the GPS to send out specific sentences were unnecessary, if I was accessing the data directly, and not through these sentences, so I tried commenting out this section and one other section in the loop that parsed each sentence. However, now whenever I run the code I get no fix from the GPS no matter where I am (including places that had a fix before I commented out this code). So I assume that this code is important to what I want to do somehow, but I don't understand how. I was wondering if anyone could explain how it was related, and/or what the built in "interrupt" option does, and whether or not I want it. (I'm going to be pushing the memory capacity for this project, so I want to remove anything that's unnecessary)

Here is the code of the modified sketch:

Code: [Select]
// 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.

#include <SPI.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <SD.h>
#include <avr/sleep.h>

SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);

// 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!
boolean usingInterrupt = false;
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy

//pins

//adafruit GPS shield uses pins 8, 10-13
//pin 8 is for serial communication, 10 is the chip select pin (whatever that means), and 11-13 are for the SPI bus
//not sure why we only have to declare 8, 10, and 13, but this is how it was originally in the tutorial and it works
//apparently the GPS uses pin 9 also because I realized it was glitching when I didn't connect it
#define chipSelect 10
#define ledPin 13

File logFile;


//maybe use a different class for these functions if I ever get MSVCPP and visual micro working with Arduino

// 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)
{
  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()
{
  // 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");

  //initialize switch pins
  pinMode(laneUpPin, INPUT);
  pinMode(laneDownPin, INPUT);
  pinMode(leftShoulderPin, INPUT);
  pinMode(rightShoulderPin, INPUT);
  pinMode(onRampPin, INPUT);
  pinMode(offRampPin, INPUT);

  //initialize LCD pins
  //pinMode(greenPin, OUTPUT);
  //pinMode(bluePin, OUTPUT);
 
  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);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect))
  {
    Serial.println("Card init. failed!");
    error(2);
  }
  //increment name by adding a number if there are already GPS logs on the SD card
  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);

  //this is the first section I want to be able to comment out but apparently can't
/*
  //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
  //the other two possible NMEA sentences I might want to include: GPHDT/GPHDG (heading), and PGRME (estimated position error)
 
  //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);
 
  // 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!
  useInterrupt(true);
*/
}

// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(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(boolean 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;
  }
}

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 (GPS.newNMEAreceived())
  {
    //get GPS location

    //this is the second section
/*
    // 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
*/

    //log NMEA sentence along with highway state variables to SD card

    if (GPS.fix)
    {
      //logFile.print("Fix: ");
    } else
    {
      logFile.print("no fix: ");
    }
   
    logFile.print(GPS.month); logFile.print("/"); logFile.print(GPS.day); logFile.print("/"); logFile.print(GPS.year); logFile.print(";");
    logFile.print(GPS.hour); logFile.print(":"); logFile.print(GPS.minute); logFile.print(":"); logFile.print(GPS.seconds); logFile.print(";");
    logFile.print(GPS.lat); logFile.print(";"); //N or S to represent a positive or negative sign for the latitude
    logFile.print(GPS.latitude); logFile.print(";");
    logFile.print(GPS.lon); logFile.print(";"); //E or W to represent a positive or negative sign for the longitude
    logFile.print(GPS.longitude); logFile.print(";");
   
    logFile.println();
    logFile.flush(); //need this to make sure that each individual line is actually saved to the file without closing it officially
   
  }
}


Thanks

/dev

So, when I replied here, did you go look at the program that does what you want?  Did you have any questions?

That program logs complete NMEA sentences to an SD card, and does it very reliably.  Since you're not really using lat/long to calculate something in the Arduino, you might as well avoid parsing it: just save the bytes you receive, don't waste time converting them to a floating-point number (or long int).

The Adafruit example usually works until you modify it.  The "useInterrupt" feature will save one character from the input stream, once per millisecond.  It's a way to avoid input buffer overflow.  When an entire line has been saved, it gets parsed in loop.  You say you don't need to do that, but that's what it does.

I have been asked about writing a "more modifiable" GPS logging example, as your kind of question comes up frequently.  It's pretty far down my list of things to do, as my interest is really in parsing solutions, like NeoGPS.  The link above is closest to what you want to do.

Cheers,
/dev

mysteriousmonkey29

Okay, yeah my bad. I wasn't able to get that sketch to work, but I never responded with any questions/problems with it on the other thread, which I will do now.

In the meantime, I decided to try to pursue getting the adafruit example to work if I could, with the modification that instead of trying to work with whole sentences, I would make my own sentences by accessing specific values like latitude and longitude. This works, but I am close to running out of memory, and thus am trying to determine what parts of the program are unnecessary for what I'm trying to do. Based on just trial and error, it seems that the GPS commands that turn on the basic sentences and/or the command that receives each specific sentence is necessary to directly access values like latitude and longitude, but I don't understand why, so that is what I was wondering.

As for the interrupt part, thanks. That makes sense

I'll go post the problems I was having with the other sketch on the other thread now

mysteriousmonkey29

Also, I have another question about the adafruit example sketch. After some testing, it seems like the program creates a new log file every time the GPS loses and regains its fix. I would like this to not happen, but I'm not sure what is actually causing it (I don't see any code like this in the example program itself, so I assume it must be somewhere in the included libraries, but really have no idea where to start).

Thanks

Go Up