GPS logger

Yes this is another GPS Logger project
Track is recorded on the SD card as a .CVS file which is then converted as a Google Earth .kml file
Just download and double click on the attached sample file (you should be logged to the forum to see the file)
It will directly open in Google Earth and display the track.

This logger design is focused on low power consumption by combining several technologies that can be reused for other, non related, projects:
A stripped down Arduino (plain AtMega 328P, $2.99 shipped) removes Led’s and FTDI overheads. Processor works @ 8Mhz, 3V.
The board is directly powered by a single 3.2V lithium iron phosphate battery; no need for a power hungry DC/DC converter.
Finally, the processor is put asleep below 3V to avoid battery deep discharge.
By combining all those, I was able to improve battery life from a couple of hours to 20+ hours

This project is rather modular and educational, it will let you master several techniques

  • Arduino on a board (thanks Super Nick ;))
  • LiFePO4 battery technology
  • battery monitoring w/o voltage regulator (not trivial…)
  • Arduino sleeping modes (thanks again Nick)
  • GPS NMEA 0183 serial protocol
  • SD Card SPI communication
  • Python scripting
  • KML format
  • soldering and packaging in a nice waterproof box

Disclaimer: I invented nothing, just putting stuff together :wink:

Should you need additional info’s and links on specific points, let me know

Track20131113.kml (3.27 KB)

If you don't actually need the .CSV file itself, you could record the data directly to the SD card as a GPX file which can be read by Google Earth and Garmin software (amongst others). Saves having to translate the file but makes the file somewhat larger - but that's not usually an issue.

I made essentially the same kind of logger using an Adafruit Ultimate GPS, Teensy 3 (Correction: It's a Teensy 2) and the PJRC uSD card/adapter. It is nowhere near as good looking as your installation though :-)

Pete

Hi Pete,

If you don’t actually need the .CSV file itself, you could record the data directly to the SD card

You are right, no real need for the cvs file
I choose this road for three reasons:

  1. Terminate the file with the relevant ending Tags
      </coordinates>
    </LineString>
  </Placemark>
</Document>
</kml>

Could be done on the fly:

  • open SD card file
  • rewind 56 Bytes (erase previous ending Tags)
  • Write Long + Lat
  • write ending Tags
  • close file
  1. compute average speed, cumulative uphill and downhill…

  2. Get a final file with date stamp, which is not possible within Arduino ecosystem

Thanks for your interest

Nice!

And I like the box. Is it a waterproof cigarette case?

And a pleasure to see an exhibition post with some good pictures and a text , not just an external "generate some traffic on my blog or youtube"-link.

I write a record every 5 seconds so there’s plenty of time to open the file, seek to near the end, write the new stuff and close it again. Like this:

  SD_open_file();
  unsigned long filesize = myFile.fileSize();
  // back up the file pointer to just before the closing tags
  filesize -= 27;
  //Serial.println(filesize);
  if(!myFile.seekSet(filesize)) {
#ifndef BATTERY
    // Can't win 'em all. Print error message, but can't do anything
    // other than keep going and hope for the best!
    Serial.println("Seek failed");
#endif
  }
  // print the GPS data to the file
  myFile.print(trk_buf);
  // This will be overwritten by the next write to the file, if there is one,
  // and if there isn't, the file is complete and will read as a valid GPX
  myFile.print(F("</trkseg>\r\n</trk>\r\n</gpx>\r\n"));
  myFile.close();

Pete

@Peter_I

And I like the box. Is it a waterproof cigarette case?

Just a general purpose box Search for "waterproof" "box" "plastic" on eBay example: ($2.85 shipped) http://www.ebay.com/itm/Outdoor-Waterproof-Plastic-Container-Key-Money-Storage-Box-Case-Holder-/180917961914?pt=LH_DefaultDomain_0&hash=item2a1f8d04ba

Thanks for the comments

@el_supremo

Yeah, that the way to go Closing Tags should always be added after each Long/Lat recording, as the logger may be switch off at any time. Out of curiosity, what's about ???

#ifndef BATTERY
.....

If it is running on a battery, which it does when I'm driving and it's recording the track, there's no point trying to talk to the Serial port. I suppose it doesn't really matter - the bits won't go anywhere :-). If I'm testing it at home, then I do want to see the failure message on the Serial monitor. Basically, it's a bit of old debugging.

Pete

kas:
@Peter_I

And I like the box.
Is it a waterproof cigarette case?

Just a general purpose box, $2.85 shipped
http://www.ebay.com/itm/Outdoor-Waterproof-Plastic-Container-Key-Money-Storage-Box-Case-Holder-/180917961914?pt=LH_DefaultDomain_0&hash=item2a1f8d04ba

Thanks for the comments

Thanks.
I can see quite a few uses for those!

Nice logger.... kas are you going to release your code and stripboard layout?

thanks

If it is running on a battery, which it does when I’m driving and it’s recording the track, there’s no point trying to talk to the Serial port. I suppose it doesn’t really matter - the bits won’t go anywhere :-).
If I’m testing it at home, then I do want to see the failure message on the Serial monitor. Basically, it’s a bit of old debugging

In this project, error Messages are “Bip coded” and transmitted both through the built in buzzer
and the status LED

If everything is OK, I expect 2 short Bips at startup
If I forget to insert the SD card, I am warned with 4 long Bips :blush:

void setup()    {
....
  if (!sd.begin(CS) || !getConfig())  {                    // Initialize the SD and get configuration
    bip(bipPin, 250, 3);                                   // error
    blinkLED(logLED, 10, 3);
    while(1);
  }
  if (!file.open("LOG.CSV", O_CREAT | O_WRITE | O_APPEND))  {// create or open the data file for append.
    bip(bipPin, 250, 4);                                     // error
    blinkLED(logLED, 10, 4);
    while(1);
  }
.... 
  blinkLED(logLED, 10, 2);
  bip(bipPin, 10, 2);                                       // ready
}


void bip(int pin, int duration, int n)    {                 // ms and number of bips 
  for(int i=0; i<n; i++)  {  
     digitalWrite(bipPin, HIGH);        
     delay(duration);
     digitalWrite(bipPin, LOW);         
     delay(75);
  }
}

void blinkLED(int pin, int duration, int n)    {             // ms and number of blinks
  for(int i=0; i<n; i++)  {  
   digitalWrite(pin, HIGH);        
   delay(duration);
   digitalWrite(pin, LOW);
   if(n > 1)    delay(200);  // ex500
  }

Nice logger.... kas are you going to release your code and stripboard layout?

Thanks rbright

I will clean my code and post it within a few days

For the layout ... :blush: :blush: would the breadboard prototype photo help ??

kas the protoboard image is really helpful, so would be a schematic & eventually the code. What you have produced is an excellent example of a minimum component data logger which by its nature has a low current drain especially with what appears to be no UART implimented. People could remove the GPS module and add a RTC (to provide accurate time/date) and then have a logger suitable for say temperatures, or that what I'd expect from what I've seen to date.

Regards

Here is the kasGPS V3.0 source code:

// GPS Logger kasGPS V3.0        Kas 2013

// SD wiring: 
// MOSI: D11, MISO: D12, CLK: D13, CS: D10 (D53 for Mega)

// V30: Use sdfat (http://arduino.cc/forum/index.php/topic,149504.0.html)
// .....
// V1.0 Initial release

#include <SdFat.h>
#include <avr/sleep.h>
#include <TinyGPS.h>

#define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"     // only RMC data

TinyGPS gps;                              // TinyGPS object
SdFat sd;                                 // SD file system.
SdFile file;                              // Log file.
int CS = 10;                              // SPI chip select
int logLED = 8;                 
int bipPin = 9;  
int minDist = 30;                         // Minimum distance in meters between logs
long logRate = 3000;                      // Minimun time in ms between logs
int voltMin = 300;                        // Minimum battery voltage
static char dtostrfbuffer[20];
float flat, flon;
float flat_ant=0, flon_ant=0;
unsigned long age;
long distance;
long startLog = 0;
String SD_lat = "invalid";
String SD_lon = "invalid";
String dataString ="";
int maxSkip = 5;                          // do not log initial inaccurate values
int skip = 1;

static bool feedgps();

void setup()    {
  pinMode(CS, OUTPUT);                                     // Chip Select Pin for SD Card
  pinMode(bipPin, OUTPUT);                                 // Buzzer
  pinMode(logLED, OUTPUT);                                 // LED Indicator
  Serial.begin(9600);
  Serial.println(PMTK_SET_NMEA_OUTPUT_RMCONLY);            // turn on only RMC
  if (!sd.begin(CS) || !getConfig())  {                    // Initialize SD card and get configuration
    bip(bipPin, 250, 3);                                   // error
    blinkLED(logLED, 10, 3);
    while(1);
  }
  if (!file.open("LOG.CSV", O_CREAT | O_WRITE | O_APPEND)) {// Initialize the SD and create or open the CVS data file for append.
    bip(bipPin, 250, 4);                                    // error
    blinkLED(logLED, 10, 4);
    while(1);
  }
  blinkLED(logLED, 10, 2);
  bip(bipPin, 10, 2);                                      // ready
}

void loop()    {
  bool newdata = false;

  if(getBatVolt() < voltMin)  {                           // battery monitoring
    bip(bipPin, 200, 5);
    GoToSleep();                                          // ATMega low hibernation mode
  }

  unsigned long start = millis();
  while (millis() - start < 1500)   {                    // data acquisition from GPS module
    if (feedgps())  newdata = true; 
  }
  if(newdata)  {  
    gps.f_get_position(&flat, &flon, &age); 
    if(flat == TinyGPS::GPS_INVALID_F_ANGLE)    {
      SD_lat = "***";
      SD_lon = "***";
    }  else  {
      SD_lat = dtostrf(flat,8,5,dtostrfbuffer);    
      SD_lon = dtostrf(flon,8,5,dtostrfbuffer);
    }
    dataString = SD_lat + "," + SD_lon;  
    feedgps();
    
    if(dataString != "***,***")  {  
      if(skip > maxSkip)  {                                  // discard first values
        if((millis() - startLog > logRate))  {               // time log conditions
          startLog = millis();  
          distance = TinyGPS::distance_between(flat, flon, flat_ant, flon_ant);
          if(distance > minDist)      {                      // distance log conditions
            if(abs(distance > 1000))    distance = 0;        // max speed 1200Km/h
            file.println(dataString);                        // write to file
            file.sync();                                     // Use sync instead of close.
            flat_ant = flat;    
            flon_ant = flon;
            bip(bipPin, 3, 1);                               
            blinkLED(logLED, 10, 1);
         }  
        }  
      }  else {
        skip++;
        blinkLED(logLED, 10, 1);
      }
    }  else    { blinkLED(logLED, 200, 1);  blinkLED(logLED, 10, 1); }
  }  else   digitalWrite(logLED, HIGH);
}

static bool feedgps()  {
  while(Serial.available())  {
    if(gps.encode(Serial.read()))    return true;
  }
  return false;
}

void bip(int pin, int duration, int n)    {                 // Bip piezo: duree en ms et repetition
  for(int i=0; i<n; i++)  {  
     digitalWrite(bipPin, HIGH);        
     delay(duration);
     digitalWrite(bipPin, LOW);         
     delay(75);
  }
}

void blinkLED(int pin, int duration, int n)    {             
  for(int i=0; i<n; i++)  {  
   digitalWrite(pin, HIGH);        
   delay(duration);
   digitalWrite(pin, LOW);
   if(n > 1)    delay(200);  // ex500
  } 
}   

int getBatVolt()      {
int results;
const long InternalReferenceVoltage = 1098L; //1050L;  // Adjust value to your boards specific internal BG voltage x1000
   // REFS1 REFS0	    --> 0 1, AVcc internal ref.   	  // MUX3 MUX2 MUX1 MUX0  --> 1110 1.1V (VBG)
  for (int i=0; i <= 3; i++)     {  //4 readings required for best stable value?
    ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
    ADCSRA |= _BV( ADSC );                                                    // Start a conversion
    while( ( (ADCSRA & (1<<ADSC)) != 0 ) );                                   // Wait for it to complete
    results = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L;        // Scale the value
 }
 return results;
}

void GoToSleep()    {
  Serial.end();
  ADCSRA = 0;                                        // disable ADC
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();
  MCUCR = _BV (BODS) | _BV (BODSE);                  // turn on brown-out enable select
  MCUCR = _BV (BODS);                                // this must be done within 4 clock cycles of above
  sleep_cpu ();                                      // sleep within 3 clock cycles of above
}  

boolean getConfig()  {                              // Read the Configuration information (Config.txt)
boolean result = true;                              // file content: X;Y
  if (file.open("Config.txt", O_READ) )    {        // X=seconds Y=metersX10 (between 2 records)
    if(file.available() >= 3)  {    
      logRate = (file.read() - '0') * 1000;
      file.read();                                  // drops ";"
      minDist = (file.read() - '0') * 10;
    }    else  result = false;
    file.close();
  }    else    result = false;
  return result;
}

I use two nice libraries for this project

Both libraries come with sample code I happily reused
Kudos to these two gentlemen for their hard works

Hi Kas

First of all, congratulations for your project. I have liked it a lot, specifically the consumption matter, it's very interesting to have 20 hours of atonomy, I can track my hikings with my phone, but 20 hours of atonomy is a complete weekend log !!!

I was thinking in something similar and your experiences are very helpful to me.

I have some questions:

  • Have you thought about include another battery and measure the impact in the autonomy ? It must duplicate it, doesn't it? I'm thinking too in a temperature logger. I guess a temperature sensor will consume less power than a gps so the autonomy with two of those batteries could increase to a few days.

  • What's the point of the CR1220 battery? are there a realtime clock? or it's used for another reason?

  • Which GPS module has been used for your project?

Thanks in advance and congratulations one more time

Gabi

Hi Gabi,

Thanks for your interest, welcome to this forum

  • Have you thought about include another battery and measure the impact in the autonomy ? It must duplicate it, doesn't it?

You can install a second battery and get twice the time Alternatively, replace the 1000mAH 14500 battery by the larger 1500mAH 18650 model

I'm thinking too in a temperature logger. I guess a temperature sensor will consume less power than a gps so the autonomy with two of those batteries could increase to a few days

For temperature, just hibernate the processor between two readings, with 2mn log time, your battery should last well over one month

What's the point of the CR1220 battery? are there a realtime clock? or it's used for another reason?

This is the GPS backup battery for storing ephemeris data and allow "warm boot" in a couple of seconds (cold boot takes one minute) Since my last post, I replaced the battery by a .22 Farad supercap

Which GPS module has been used for your project?

Fastrax UP501, $27 shipped http://www.ebay.com/itm/High-performance-and-Quality-UP501-GPS-Antenna-Module-For-Arduino-/170979281074?pt=LH_DefaultDomain_0&hash=item27cf28c8b2

Than you very much. Nowadays I'm waiting for some components in order to start with my temperature logger. I have taken a lot of ideas from your project, and I will keep you posted. Best regards

Could you let me know which GPS you used? I can't seem to find it in the thread.

Thanks

Did you miss the 2nd page ? The answer is 2 posts above your question. It includes a link to an E-bay listing, but after 2.5 years they have been sold out from that seller a long time ago.

bonjour désolé pour le réveille tardif

le sketch fonctionnerait avec nano ou uno? merci