Using the sprintf function

I am making a GPS SD card data logger. To write the GPS data to the SD card I have to sprintf it, unless there is another way. I looked at a tutorial for the GPS which showed me how to sprintf the elevation data, but nothing else. Would someone explain to me how to write a sprintf function and what the %02d part means. I know it replaces that with the GPS data, but I don’t understand why it has to be %02d.

Hardware: Arduino Uno with Seeed SD card shield and Adafruit Ultimate GPS.

Code:

// SD card location logger
// Hardware: Seeed SD Card Shield v3.0, Sparkfun Ultimate GPS Breakout, and Arduino Uno

#include <SD.h>
#include <SoftwareSerial.h> 
#include <TinyGPS++.h>

// RX and TX pins for Software Serial library
#define RXPin 2
#define TXPin 3

// The serial connections to the GPS device.
SoftwareSerial ss(RXPin, TXPin);

// GPS Baud Rate
#define GPSBaud 4800

// The TinyGPS++ object
TinyGPSPlus gps;

// On the Seeed SD Card Shield, the CS is pin 10. No matter what pin is used as CS on your shield, 
// the hardware CS pin (10 on most Arduino board's, 53 on Arduino Mega) must be left as an output
const int chipSelect = 10;

// Prepare SD card file.
File GPSlog;

// Warning light (LED)
const int fail = 13;

void setup() {
  Serial.begin(GPSBaud);
  
  // IMPORTANT: Set the hardware CS pin to output mode
  pinMode(10, OUTPUT);
  
  // Warning LED is an output
  pinMode(fail, OUTPUT);
  // Turn warning light off
  digitalWrite(fail, LOW);
  
  // Make sure the card is present and can be initialized.
  if(!SD.begin(chipSelect)) 
  {
    // Turn on the SD warning light.
    digitalWrite(fail, HIGH);
    // Don't do anything more.
    return;
  }
  
  // Make and open a new plain text (.txt) file called "GPSdata.txt"
  File GPSlog = SD.open("GPSdata.txt", FILE_WRITE);
  // If the file opened, write to it.
  if(GPSlog)
  {
    GPSlog.println("GPS Data Log File");
    GPSlog.println("lattitude, longitude, altitude, time");
    // Close the file.
    GPSlog.close();
  }
  // If the file was not able to be opened
  else
  {
    // Turn on the SD warning light.
    digitalWrite(fail, HIGH);
    // Don't do anything more.
    return;
  }
}

void loop() {
  // If any characters have arrived from the GPS,
  // send them to the TinyGPS++ object.
  while(Serial.available() > 0)
  {
    gps.encode(Serial.read());
  }
  
  // Record our new locaation and altitude when 
  // they have been updated.
  if(gps.location.isUpdated() || gps.altitude.isUpdated())
  {
    
  }
}

void recordGPS() {
  // Open the GPSdata.txt file in write mode.
  GPSlog = SD.open("GPSdata.txt", FILE_WRITE);
  
  // If the file opened, write to it.
  if(GPSlog)
  {
    // Make a char called emit and sprintf it.
    char emit[80];  //Time backwards since naming stuff time causes compiler issues
    sprintf(emit, "%02d:%02d:%02d ~ ", gps.time.hour(), gps.time.minute(), gps.time.second());
    
    // Make a char called here and sprintf it.
    char here[80];  
    sprintf(here, "?????????", gps.location.lat(), gps.location.long(), gps.altitude.meters());
    // Print the time to GPSdata.txt.
    GPSlog.print(emit);
    // Print the lattitude and longitude to GPSdata.txt.
    GPSlog.print(here);
  }
}

Here you go...

http://www.cplusplus.com/reference/cstdio/sprintf/

Here is a convenient list of the format specifiers C library function - sprintf() - Tutorialspoint

Thanks, but do you know if you have to specify where to put the decimal on floats like you have to for Serial.print()ing floats. ex. myFloat = 3.14; Serial.print(myFloat, 2); //Decimal 2 points in from right

sprintf() on the arduino does not handle floats, so I guess the answer to your question is 42.

What do you mean by 42???

Ohh. The answer to life the universe and everything.

Use dtostrf() to convert the float to a string. Then use the %s format specifier to include the newly created float-representing string.

This worked:

// SD card location logger
// Hardware: Seeed SD Card Shield v3.0, Sparkfun Ultimate GPS Breakout, and Arduino Uno

#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
#include <TinyGPS++.h>

// RX and TX pins for Software Serial library
#define RXPin 2
#define TXPin 3

// The serial connections to the GPS device.
SoftwareSerial ss(RXPin, TXPin);

// GPS Baud Rate
#define GPSBaud 4800

// The TinyGPS++ object
TinyGPSPlus gps;

// On the Seeed SD Card Shield, the CS is pin 10. No matter what pin is used as CS on your shield,
// the hardware CS pin (10 on most Arduino board's, 53 on Arduino Mega) must be left as an output
const int chipSelect = 10;

// Prepare SD card file.
File GPSlog;

// Warning light (LED)
const int fail = 13;

void setup() {
  Serial.begin(GPSBaud);

  // IMPORTANT: Set the hardware CS pin to output mode
  pinMode(10, OUTPUT);

  // Warning LED is an output
  pinMode(fail, OUTPUT);
  // Turn warning light off
  digitalWrite(fail, LOW);

  // Make sure the card is present and can be initialized.
  if (!SD.begin(chipSelect))
  {
    // Turn on the SD warning light.
    digitalWrite(fail, HIGH);
    // Don't do anything more.
    return;
  }

  // Make and open a new plain text (.txt) file called "GPSdata.txt"
  File GPSlog = SD.open("GPSdata.txt", FILE_WRITE);
  // If the file opened, write to it.
  if (GPSlog)
  {
    GPSlog.println("GPS Data Log File");
    GPSlog.println("lattitude, longitude, altitude, time");
    // Close the file.
    GPSlog.close();
  }
  // If the file was not able to be opened
  else
  {
    // Turn on the SD warning light.
    digitalWrite(fail, HIGH);
    // Don't do anything more.
    return;
  }
}

void loop() {
  // If any characters have arrived from the GPS,
  // send them to the TinyGPS++ object.
  while (Serial.available() > 0)
  {
    gps.encode(Serial.read());
  }

  // Record our new locaation and altitude when
  // they have been updated.
  if (gps.location.isUpdated() || gps.altitude.isUpdated())
  {

  }
}

void recordGPS() {
  // Open the GPSdata.txt file in write mode.
  GPSlog = SD.open("GPSdata.txt", FILE_WRITE);

  // If the file opened, write to it.
  if (GPSlog)
  {
    // Make a char called emit and sprintf it.
    char emit[80];  //Time backwards since naming stuff time causes compiler issues
    sprintf(emit, "%02d:%02d:%02d ~ ", gps.time.hour(), gps.time.minute(), gps.time.second());

    // Print the time to GPSdata.txt.
    GPSlog.print(emit);

    // Print the latitude and longitude to GPSdata.txt.
    GPSlog.print(gps.location.lat());
    GPSlog.print(", ");
    GPSlog.print(gps.location.lng());

    //Print the altitude to GPSdata.txt.
    GPSlog.print(gps.altitude.meters());
  }
  else
  {
    // Turn on the SD warning light.
    digitalWrite(fail, HIGH);
    // Don't do anything more.
    return;
  }
}

KeithRB: sprintf() on the arduino does not handle floats, so I guess the answer to your question is 42.

Yes it does. To save space, avr libC has a library without floating point sprintf() support that is used by default. The IDE uses this default library so the sprintf() linked in doesn't have it. With Arduino 1.5x you can go in and modify the linker options down in the avr platform.txt file to use the "normal" full version of sprintf() library instead. You change this:

compiler.c.elf.flags=-Os -Wl,--gc-sections

to this:

compiler.c.elf.flags=-Os -Wl,--gc-sections -Wl,-u,vfprintf -lprintf_flt

This is a one time "fix". It does come with the cost of using additional code in flash - about 1.8k or so that you use even if not using floating point.

--- bill

Woa, last time I messed with stuff like that I had to reinstall my whole arduino application and libraries.

2007jingz: Woa, last time I messed with stuff like that I had to reinstall my whole arduino application and libraries.

I can't imagine what you did that would require a full re-install.

This is a simple one line change and is completely undo-able. just go edit {installdir}/hardware/arduino/avr/platform.txt

Add a # to the old line to comment it out, and insert the new line I gave you. Easy peasy.

--- bill

KeithRB:
sprintf() on the arduino does not handle floats, so I guess the answer to your question is 42.

Actually, it does - if you compile your code with the proper libraries linked.

See the attached ZIP file - it contains a new “pde.jar” file (which goes in the “arduino/lib” directory).

It adds an option in “File / Preferences” of the IDE to enable or disable floating point support for printf() and sscanf().

All you do is rename your original pde.jar to pde.bak and unzip the attached file into the “arduino/lib” directory.

Now you have the option to use %f if desired (or to turn it off to save a little code space).

This option will make a sketch about 1.5K larger if enabled.

By the way, for Arduino 1.0.5. Not tested on 1.5.x.

floating_point_option.zip (772 KB)

bperrybap: This is a one time "fix". It does come with the cost of using additional code in flash - about 1.8k or so that you use even if not using floating point. --- bill

I added that as an option to the IDE 1.0.5 (I don't use 1.5.x).

Krupski:

bperrybap: This is a one time "fix". It does come with the cost of using additional code in flash - about 1.8k or so that you use even if not using floating point. --- bill

I added that as an option to the IDE 1.0.5 (I don't use 1.5.x).

That sounds really great. However, It didn't work for me. On my linux Mint 13 machine I get:

Exception in thread "main" java.lang.UnsupportedClassVersionError: processing/app/Base : Unsupported major.minor version 51.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:643)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
Could not find the main class: processing.app.Base. Program will exit.

What am I missing? Note that 1.0.5-r2 was only released for Windows as it supposedly only fixed issues that were windows specific.

--- bill

Krupski, apparently it needs jdk7 and that isn't there on Linux mint 13. Seems a bit odd and restrictive to require a newer version than the jar file it is replacing. Does it really need jdk7?

--- bill