Arduino GPS SD-Card Logger

Hi guys,

I’m quite new to C++ and Arduino programming, so please be kind to me :slight_smile:

I’m trying to write a GPS logger with writing data to a SD-Card. This is the code I’ve got so far. Unfortunately at the line: “return(sz);” in the “print_int” function I do get the error "invalid conversion from ‘char*’ tp ‘char’.

Any help is appreciated…

#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <SD.h>                        // Standard Arduino SD card library

File myFile;

SoftwareSerial gpsSerial(3, 4);  
TinyGPS gps;

unsigned long currentTime;
unsigned long loopTime;

void setup() {
  Serial.begin(115200);
  gpsSerial.begin(9600);
  Serial.println("Reading GPS");

// Initialize SD card

 Serial.print("Initializing SD card...");
 
 pinMode(10, OUTPUT);
 
 currentTime = millis();
 loopTime = currentTime;
   
 if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    //return;
 }
 Serial.println("initialization done.");
  
// End initialize SD Card
}

void loop() {
  currentTime = millis();
  while(gpsSerial.available()) { //check for gps data
    if(gps.encode(gpsSerial.read())) { //encode gps data
      if(currentTime >= (loopTime + 5000)) {  // Update every 5 seconds
      
        gpsdump(gps);
      }
    }
  }
}

// Get and process GPS data
void gpsdump(TinyGPS &gps) {
  float lat, lng, altitude, courseTrue, speedKmph;
  unsigned long hdop, dateAge, fixAge, date, time, chars = 0;
  unsigned short sentences = 0, failed = 0;
  static char courseTrueCard, sats;
  static const float HOME_LAT = 51.508131, HOME_LNG = -0.128002;
  
  sats = print_int(gps.satellites(), TinyGPS::GPS_INVALID_SATELLITES, 5);
  hdop = print_int(gps.hdop(), TinyGPS::GPS_INVALID_HDOP, 5);
  //set the position
  gps.f_get_position(&lat, &lng, &dateAge);
  lat = print_float(lat, TinyGPS::GPS_INVALID_F_ANGLE, 9, 5);
  lng = print_float(lng, TinyGPS::GPS_INVALID_F_ANGLE, 10, 5);
  print_int(fixAge, TinyGPS::GPS_INVALID_AGE, 5);
  print_date(gps);
  altitude = print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 8, 2);
  courseTrue  = print_float(gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  speedKmph = print_float(gps.f_speed_kmph(), TinyGPS::GPS_INVALID_F_SPEED, 6, 2);
  courseTrueCard = print_str(gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6);
  print_int(lat == TinyGPS::GPS_INVALID_F_ANGLE ? 0UL : (unsigned long)TinyGPS::distance_between(lat, lng, HOME_LAT, HOME_LNG) / 1000, 0xFFFFFFFF, 9);
  print_float(lat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : TinyGPS::course_to(lat, lng, HOME_LAT, HOME_LNG), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_str(lat == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(TinyGPS::course_to(lat, lng, HOME_LAT, HOME_LNG)), 6);
  
  gps.stats(&chars, &sentences, &failed);
  print_int(chars, 0xFFFFFFFF, 6);
  print_int(sentences, 0xFFFFFFFF, 10);
  print_int(failed, 0xFFFFFFFF, 9);

  /// And write it to SD card
  myFile = SD.open("gpsData.csv", FILE_WRITE);
  
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to gpsData.csv...");
    // display position
    Serial.println("Position: ");
    Serial.print("sats: ");Serial.print(sats); // print number of satellites
    Serial.print(", hdop: ");Serial.print(hdop); // print hdop
    Serial.print(", lat: ");Serial.print(lat);Serial.print(" ");// print latitude
    Serial.print(", lng: ");Serial.print(lng); // print longitude
    Serial.print(", fixAge: ");Serial.print(fixAge); // print fixAge
    Serial.print(", date: ");Serial.print(date); // print date
    Serial.print(", time: ");Serial.print(time); // print time
    Serial.print(", dateAge: ");Serial.print(dateAge); // print dateAge
    Serial.print(", altitude: ");Serial.print(altitude); // print altitude
    Serial.print(", courseTrue: ");Serial.print(courseTrue); // print courseTrue
    Serial.print(", speedKmph: ");Serial.println(speedKmph); // print speedKmph
    myFile.print("1234567891231"); //devideID - IMEI - todo: read the IMEI out of the GSM/GPRS Shield
    myFile.print(", ");
    myFile.print(sats);
    myFile.print(", ");
    myFile.print(hdop);
    myFile.print(", ");
    myFile.print(lat);
    myFile.print(", ");
    myFile.println(lng);
    myFile.print(", ");
    myFile.print(fixAge);
    myFile.print(", ");
    myFile.print(date);
    myFile.print(", ");
    myFile.print(time);
    myFile.print(", ");
    myFile.print(dateAge);
    myFile.print(", ");
    myFile.print(altitude);
    myFile.print(", ");
    myFile.print(courseTrue);
    myFile.print(", ");
    myFile.print(speedKmph);
    myFile.print(", ");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening gpsData.csv");
  }
}

static char print_int(unsigned long val, unsigned long invalid, int len)
{
  char sz[32];
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  return(sz);
}

static char print_float(float val, float invalid, int len, int prec)
{
  char sz[32];
  if (val == invalid)
  {
    strcpy(sz, "*******");
    sz[len] = 0;
        if (len > 0) 
          sz[len-1] = ' ';
    for (int i=7; i<len; ++i)
        sz[i] = ' ';
    Serial.print(sz);
    return(sz);
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1);
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(" ");
  }
}

static char print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE) {
    Serial.print("*******    *******    ");
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d   ",
        month, day, year, hour, minute, second);
    Serial.print(sz);
    return(sz);
  }
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);
}

static char print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i) {
    Serial.print(i<slen ? str[i] : ' ');
    return(i<slen ? str[i] : ' ');
  }
}

print_int is declared as returning a char, but you're trying to return an array of char, which could be declared using char * instead. However, since the array you're trying to return is local to print_int, you can't rely on its content once the function ends.

You could pass the buffer you want filled to print_int as another parameter and make the function return nothing (void) instead. Note also that sats and others like it would need to be declared as an appropriately sized char array in this case.

Thank you wildbill!
could you help me with a short code snippet?
furthermore... I kind of get what you're saying, but why does "Serial.print(sz);" in function "print_int" return only one value (for example: "7" satellites) at the serial monitor?

void setup()
{
}

void loop()
{
char sats[8];
print_int(sats,66,99,8);
Serial.print("sats: ");Serial.print(sats); // print number of satellites
}

static void print_int(char * sz,unsigned long val, unsigned long invalid, int len)
{
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
}

why does “Serial.print(sz);” in function “print_int” return only one value (for example: “7” satellites) at the serial monitor?

Not sure what you’re asking - what were you expecting?

That helped me a lot wildbill, thank you for that snipped.

My current code looks like this:

#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <SD.h>                        // Standard Arduino SD card library

File myFile;

SoftwareSerial gpsSerial(3, 4);  
TinyGPS gps;

unsigned long currentTime;
unsigned long loopTime;

void setup() {
  Serial.begin(115200);
  gpsSerial.begin(9600);
  Serial.println("Reading GPS");

// Initialize SD card

 Serial.print("Initializing SD card...");
 
 pinMode(10, OUTPUT);
 
 currentTime = millis();
 loopTime = currentTime;
   
 if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    //return;
 }
 Serial.println("initialization done.");
  
// End initialize SD Card
}

void loop() {
  currentTime = millis();
  while(gpsSerial.available()) { //check for gps data
    if(gps.encode(gpsSerial.read())) { //encode gps data
      if(currentTime >= (loopTime + 5000)) {  // Update every 5 seconds
      
        gpsdump(gps);
      }
    }
  }
}

// Get and process GPS data
void gpsdump(TinyGPS &gps) {
  float fgetlat, fgetlng;
  unsigned long fgetfixAge, dateAge, date, time, fgetchars = 0;
  unsigned short fgetsentences = 0, fgetfailed = 0;
  char sats[5], hdop[5], fixAge[3], lat[5], lng[5], altitude[5], courseTrue[7], speedKmph[6], courseTrueCard[6], distance[9], courseTo[7], courseToCard[6], chars[6], sentences[10], failed[9];
  static const float HOME_LAT = 50.17385, HOME_LNG = 8.46914;
  
  print_int(sats, gps.satellites(), TinyGPS::GPS_INVALID_SATELLITES, 5);
  print_int(hdop, gps.hdop(), TinyGPS::GPS_INVALID_HDOP, 5);
  //set the position
  gps.f_get_position(&fgetlat, &fgetlng, &fgetfixAge);
  print_float(lat, fgetlat, TinyGPS::GPS_INVALID_F_ANGLE, 9, 5);
  print_float(lng, fgetlng, TinyGPS::GPS_INVALID_F_ANGLE, 10, 5);
  print_int(fixAge, fgetfixAge, TinyGPS::GPS_INVALID_AGE, 5);
  print_date(gps);
  print_float(altitude, gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 8, 2);
  print_float(courseTrue, gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_float(speedKmph, gps.f_speed_kmph(), TinyGPS::GPS_INVALID_F_SPEED, 6, 2);
  print_str(courseTrueCard, gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6);
  print_int(distance, lat == TinyGPS::GPS_INVALID_F_ANGLE ? 0UL : (unsigned long)TinyGPS::distance_between(lat, lng, HOME_LAT, HOME_LNG) / 1000, 0xFFFFFFFF, 9);
  print_float(courseTo, lat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : TinyGPS::course_to(lat, lng, HOME_LAT, HOME_LNG), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_str(courseToCard, lat == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(TinyGPS::course_to(lat, lng, HOME_LAT, HOME_LNG)), 6);
  
  gps.stats(&fgetchars, &fgetsentences, &fgetfailed);
  print_int(chars, fgetchars, 0xFFFFFFFF, 6);
  print_int(sentences, fgetsentences, 0xFFFFFFFF, 10);
  print_int(failed, fgetfailed, 0xFFFFFFFF, 9);

  /// And write it to SD card
  myFile = SD.open("gpsData.csv", FILE_WRITE);

  Serial.print("File content: ");  
  Serial.print(myFile);
  
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to gpsData.csv...");
    // display position
    Serial.println("Position: ");
    Serial.print("sats: ");Serial.print(sats); // print number of satellites
    Serial.print(", hdop: ");Serial.print(hdop); // print hdop
    Serial.print(", lat: ");Serial.print(lat);Serial.print(" ");// print latitude
    Serial.print(", lng: ");Serial.print(lng); // print longitude
    Serial.print(", fixAge: ");Serial.print(fixAge); // print fixAge
    Serial.print(", date: ");Serial.print(date); // print date
    Serial.print(", time: ");Serial.print(time); // print time
    Serial.print(", dateAge: ");Serial.print(dateAge); // print dateAge
    Serial.print(", altitude: ");Serial.print(altitude); // print altitude
    Serial.print(", courseTrue: ");Serial.print(courseTrue); // print courseTrue
    Serial.print(", speedKmph: ");Serial.println(speedKmph); // print speedKmph
    myFile.print("1234567891231"); //devideID - IMEI - todo: read the IMEI out of the GSM/GPRS Shield
    myFile.print(", ");
    myFile.print(sats);
    myFile.print(", ");
    myFile.print(hdop);
    myFile.print(", ");
    myFile.print(lat);
    myFile.print(", ");
    myFile.println(lng);
    myFile.print(", ");
    myFile.print(fixAge);
    myFile.print(", ");
    myFile.print(date);
    myFile.print(", ");
    myFile.print(time);
    myFile.print(", ");
    myFile.print(dateAge);
    myFile.print(", ");
    myFile.print(altitude);
    myFile.print(", ");
    myFile.print(courseTrue);
    myFile.print(", ");
    myFile.print(speedKmph);
    myFile.print(", ");
    myFile.print(courseTrueCard);
    myFile.print(", ");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening gpsData.csv");
  }
}

static void print_int(char *sz, unsigned long val, unsigned long invalid, int len)
{
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
}

static void print_float(char *sz, float val, float invalid, int len, int prec)
{
  if (val == invalid)
  {
    strcpy(sz, "*******");
    sz[len] = 0;
        if (len > 0) 
          sz[len-1] = ' ';
    for (int i=7; i<len; ++i)
        sz[i] = ' ';
    Serial.print(sz);
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1);
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(" ");
  }
}

static void print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE) {
    Serial.print("*******    *******    ");
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d   ",
        month, day, year, hour, minute, second);
    Serial.print(sz);
  }
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);
}

static void print_str(char *sz, const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i) {
    sz[i] = i<slen ? str[i] : ' ';
    Serial.print(sz);
  }
}

The problematic lines are:

  print_int(distance, lat == TinyGPS::GPS_INVALID_F_ANGLE ? 0UL : (unsigned long)TinyGPS::distance_between(lat, lng, HOME_LAT, HOME_LNG) / 1000, 0xFFFFFFFF, 9);
  print_float(courseTo, lat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : TinyGPS::course_to(lat, lng, HOME_LAT, HOME_LNG), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_str(courseToCard, lat == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(TinyGPS::course_to(lat, lng, HOME_LAT, HOME_LNG)), 6);

…as for all the three of them the error message is:

error: invalid operands of types ‘char [5]’ and ‘const float’ to binary ‘operator==’

Another question would be, how do I get date/time/age out of the following line:

print_date(gps);

Coming to my previous question:

why does “Serial.print(sz);” in function “print_int” return only one value (for example: “7” satellites) at the serial monitor?

What I meant was: As the “sz” was initalized as an array and “return(sz);” would give problems, then why does “Serial.print(sz);” not give any problems as its outputting the sz as an array as well?