Would like help with SD.h library

I would like to know what functions are available in SD.h (any library, for that matter) Yes, I am new to Arduino, though I have been programming since before c was a.

My current project is to log the data from a GPS to an SD card. I am to the point where I have to consider that the SD card will get full and it will be necessary to overwrite existing data, which would be the oldest.

I am in the process of gleaning the technique to write the file creation date. I have been unable to find out how to read the file creation date. In the reference is "openNextFile()" which "Reports the next file or folder in a directory" Forum topic < SD Card Directory Listing Fails within Large Application - Storage - Arduino Forum > uses this and then uses file.name() to get the name of the file. Is there a file.createDate()?

I would like to read the documentation on SD.h so I don't have to go gleaning. Or any other library. I am rather annoyed with comments like, "just search google." I have. The Arduino SD.h library page is woefully incomplete as it does not have any reference to file.name().

Good documentation is hard to write, I know this. I would allocate 20% of my project time to writing documentation. But without it, your work is useless to anyone else and often, even to you.

So help me out here, where do I find the complete documentation for SD.h?

I usually glance over the documentation the first time I use a lib/code, this usually gives me a broader understanding of how to do the particular task I'm after, then I just go straight to the .h file and glance through that.

Like you said, documentation has a tendency to lag behind in content. But the header file never lies ;). A quick peruse of the public functions is usually enough, if they've been named well.

So with that said, here's your SD.h file for you to look through :wink:

sd.h

Missed one of your questions. No, it would appear file.createDate() does not exist.

But, as your limited with FAT filesystem to 8 char name, why not name them in numerical sequential order, then once your card is full, go back to the first file:

f0000001.LOG -> done logging this session, next file ...
f0000002.LOG -> done logging this session, next file ...
f0000003.LOG -> done logging this session, next file
...
f0000123.LOG -> done logging this session, next file ...
f0000001.LOG -> Card is full, does f0000124.LOG exist? No, so overwrite "f0000001.LOG" ...
f0000002.LOG -> done logging this session, next file ...

Not all log sessions will be the same size hence the check for the next sequential file.

Also this function does exist in SD.h, and will help:

// Methods to determine if the requested file path exists.
  boolean exists(char *filepath);
[\code]

I have been unable to find out how to read the file creation date.

Since the Arduino hasn't a clue what the date is, HOW is it going to assign a file creation date?

Regarding a meaningful time for the creation date.

I would say that I could use a creation date of about 5000 years ago, but then I'd be being sarcastic. So the straight forward response is that I am getting the current date and time from the GPS from which I am trying to capture the data.

It's really not too hard. The GPS I'm using is a Garmin 10x (Bluetooth). I set up an HC-05 module to automatically pair and connect. (The earlier eTrex Garmins connect straight to the serial port). I use the SoftwareSerial library for the serial connection. Once the GPS has acquired a satellite or two, it starts streaming data, some of which is the UTC date and time. With a little string manipulation, viola, I have the creation date when I open a file that was not previously present for write and subsequently write the creation date.

My point here is, we're all in this together and we need to help each other. Granted, I write archaic code, but if anyone is interested, I'd be glad to share.

I have found some code that will write the create date in a sketch called timestamp. This uses the SD.h library,

I have also found some code that will read the creation date using the SDfat.h library. (unfortunately, I lost the link to the code but I have the code. It appears that SD.h and SDfat.h do not like to play in the same sandbox

#include <SdFat.h>
// SD chip select pin
const uint8_t chipSelect = SS;

SdFat sd;
SdFile file;
ArduinoOutStream cout(Serial);
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();

// open next file in root. The volume working directory, vwd, is root
while (file.openNext(sd.vwd(), O_READ)) {
file.getFilename(name); // original
file.dirEntry(&dir); // original
cout << name << ' ';
cout << FAT_YEAR(dir.lastWriteDate) << '-' << setfill('0');
cout << setw(2) << (int)FAT_MONTH(dir.lastWriteDate) << '-';
cout << setw(2) << (int)FAT_DAY(dir.lastWriteDate) << ' ';
cout << setw(2) << (int)FAT_HOUR(dir.lastWriteTime) << ':';
cout << setw(2) << (int)FAT_MINUTE(dir.lastWriteTime) << ':';
cout << setw(2) << (int)FAT_SECOND(dir.lastWriteTime) << endl;
file.close();
}
cout << "Done" << endl;
}
void loop() { }

First unfortunate thing is that the compiler does not like the line
file.getFilename(name);
Secondly, I am not 100% sure what the other lines do other than send out the time and dates. Where is the documentation for the likes of FAT_YEAR, etc.?

So, while I'm getting the timestamp, I'm not getting the file name.

I considered the serial naming of files and now, after thinking about what you wrote, I realize how easy this would be. First I find the file with the largest file name and increment by one. If the card is full, I remove the file with the lowest name. This is good. I wouldn't expect to use more than 99999999 files, but I'll write code, just in case. ;).

I would like to learn the more elegant method of reading and writing timestamps.

Thanks for your help.

In other computers, when you attempt to create a file, the operating system ( and not the application ) puts the creation date on the file.

The Arduino has no idea what the date is - unless you have an RTC or a warmed-up GPS. It would be impractical for the code of the SD library to search for all of the possibilities there, including waiting some arbitrarily long time for the GPS to warm up.

You get a default date of Jan-1-1970, don't you ?

There is nothing stopping you from writing your own function to modify the file's date on the sd card.

First unfortunate thing is that the compiler does not like the line
file.getFilename(name);

Most files do not have the file's own name inside them. If you want to know the file name, you need to look inside the file allocation table which is stored on the sd card.

I suggest you go back to the link at the end of reply #1, and look at the method for getting the file name of any of the files on the sd card.

/examples/listfiles/listfiles.ino

Thanks for all the help folks! I did, indeed find what I needed when I found this code. I added some documentation to as I understand it.

It is the blessing/curse of Arduino that it is not all things to all people. The blessing is that we don't have to endure bloatware, but the curse is the one thing we want is not there and we have to write it ourselves. I'm good with it. As a note, the windows executable to output "hello, world" is well over a megabyte.

Thanks again!

OSD

/*

  • This program tests the dateTimeCallback() function
  • and the timestamp() function.
    */
    #include <SPI.h>
    #include <SdFat.h>

SdFat sd;

SdFile file;

// Default SD chip select is SS pin
const uint8_t chipSelect = SS;

// create Serial stream (in my code, I used Serial.print)
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s)) // I assume that the compiler has bunch of
// error messages
//------------------------------------------------------------------------------
/*

  • date/time values for debug
  • normally supplied by a real-time clock or GPS
    */
    // date 1-Oct-14 (I used them as global variables and stuffed them with data
    // extracted from my GPS)
    uint16_t year = 2014;
    uint8_t month = 10;
    uint8_t day = 1;

// time 20:30:40
uint8_t hour = 20;
uint8_t minute = 30;
uint8_t second = 40;
//------------------------------------------------------------------------------
/*

  • User provided date time callback function.
  • See SdFile::dateTimeCallback() for usage.

This routine takes the information from the global time/date variables above,
formats into something the file.open (function of SDfat) can use and
puts the formated information in a place that the file.open can find it.
/
void dateTime(uint16_t
date, uint16_t* time) {
// User gets date and time from GPS or real-time
// clock in real callback function

// return date using FAT_DATE macro to format fields
*date = FAT_DATE(year, month, day);

// return time using FAT_TIME macro to format fields
time = FAT_TIME(hour, minute, second);
}
//------------------------------------------------------------------------------
/

  • Function to print all timestamps.
    */
    void printTimestamps(SdFile& f) {
    dir_t d;
    if (!f.dirEntry(&d)) {
    error("f.dirEntry failed");
    }

cout << F("Creation: ");
f.printFatDate(d.creationDate);
cout << ' ';
f.printFatTime(d.creationTime);
cout << endl;

cout << F("Modify: ");
f.printFatDate(d.lastWriteDate);
cout <<' ';
f.printFatTime(d.lastWriteTime);
cout << endl;

cout << F("Access: ");
f.printFatDate(d.lastAccessDate);
cout << endl;
}
//------------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
while (!Serial) {} // wait for Leonardo

cout << F("Type any character to start\n");
while (!Serial.available());
delay(400); // catch Due reset problem

// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}

// remove files if they exist
sd.remove("callback.txt");
sd.remove("default.txt");
sd.remove("stamp.txt");

// create a new file with default timestamps
if (!file.open("default.txt", O_CREAT | O_WRITE)) {
error("open default.txt failed");
}
cout << F("\nOpen with default times\n");
printTimestamps(file);

// close file
file.close();
/*

  • Test the date time callback function.
  • dateTimeCallback() sets the function
  • that is called when a file is created
  • or when a file's directory entry is
  • modified by sync().
  • The callback can be disabled by the call
  • SdFile::dateTimeCallbackCancel()
    */
    // set date time callback function
    SdFile::dateTimeCallback(dateTime); // This call tells file.open to call the
    // dateTime() function coded above.
    //
    // This call only needs to happen once, thus,
    // it can be in the setup() section.
    //
    // There seems to be a bug in that the
    // last access time does not get written.
    // The date does get written.
    // create a new file with callback timestamps
    if (!file.open("callback.txt", O_CREAT | O_WRITE)) { // the timedate stamp are
    // automatically written.)
    error("open callback.txt failed");
    }
    cout << ("\nOpen with callback times\n");
    printTimestamps(file);

// change call back date
day += 1;

// must add two to see change since FAT second field is 5-bits
second += 2;

// modify file by writing a byte // Set up the data to be written on the card
file.write('t');

// force dir update // cause the data to be written.
file.sync();

cout << F("\nTimes after write\n");
printTimestamps(file);

// close file
file.close();
/*

  • Test timestamp() function
  • Cancel callback so sync will not
  • change access/modify timestamp
    */
    SdFile::dateTimeCallbackCancel(); // this call cancels the call to dateTime
    // thus reverting to the default timestamp.

// create a new file with default timestamps
if (!file.open("stamp.txt", O_CREAT | O_WRITE)) {
error("open stamp.txt failed");
}
// set creation date time
if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) {
error("set create time failed");
}
// set write/modification date time
if (!file.timestamp(T_WRITE, 2014, 11, 11, 4, 5, 6)) {
error("set write time failed");
}
// set access date
if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) {
error("set access time failed");
}
cout << F("\nTimes after timestamp() calls\n");
printTimestamps(file);

file.close();
cout << F("\nDone\n");
}

void loop(void) {}