Saving files names to an array from files stored on an SD card

Hello,

I am working with the following SD card library:
https://code.google.com/p/sdfatlib/

I'm needing to obtain the names of the files on the card and store them into an array that will be used to display the names on a screen as to allow selection of the file that wanted to play.

There is an example code file that obtains the file names and prints them to the serial monitor. The code is as follows:
"OpenNext" Example

/*
 * Open all files in the root dir and print their filename and modify date/time
 */
#include <SdFat.h>

// SD chip select pin
const uint8_t chipSelect = SS;
 
// file system object
SdFat sd;
SdFile file;

// define a serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  while (!Serial) {} // wait for Leonardo
  delay(1000);
  
  // 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();
}
//------------------------------------------------------------------------------
int i = 0;

void loop() {

  // open next file in root.  The volume working directory, vwd, is root
  while (file.openNext(sd.vwd(), O_READ)) {  //openNext() in SDbaseFile.cpp
    file.printName(&Serial);
    cout << ' ';
    file.printModifyDateTime(&Serial); 
    cout << endl;
    file.close();
}
}

Even with some sleuthing, I am not understanding how the program is gathering the files. Here's my research:

I looked up the definition of the printName() function:

bool SdBaseFile::printName(Print* pr) {
  char name[13];
  if (!getFilename(name)) {
    DBG_FAIL_MACRO;
    goto fail;
  }
  return pr->print(name) > 0;
 fail:
  return false;
}

That leads me to the print() member function of the Print object "pr":
"print.cpp"
I discovered many print() functions indicating overloading. I found the one of interest:

size_t Print::print(const char str[])
{
  return write(str);
}

Which led me to the function write(), also located in print.cpp:

size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    n += write(*buffer++);
  }
  return n;
}

That's where the trail ends.

I'm not understanding two things:

  1. I don't see where the code is printing the file names to the serial monitor. If I can discover how it acquires the names and cycles through them, I can access and save them as it iterates through.
  2. In the write() function, there are two input parameters but when the print() function calls write(), it only provides one parameter (str).

I would appreciate insight from someone who mind lending their time.

-P

  1. I don't see where the code is printing the file names to the serial monitor.

The Serial class derives from Print. So, it's the Serial class' implementation of the virtual write method that ultimately gets called to write() name to the serial port.

If I can discover how it acquires the names

That's what this did:

getFilename(name)

The getFilename() method of the File class was called, for an instance of the class (a specific file), and populated the name array.

  1. In the write() function, there are two input parameters but when the print() function calls write(), it only provides one parameter (str).

There are 2 write() methods. One takes an array and a size.

Thank you PaulS,

Could you tell me where the Serial class is declared so that I may examine it further?

What seems strange to me is that the two write() functions form a cyclical relationship, they each call write().

 size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }
size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    n += write(*buffer++);
  }
  return n;
}

here is code i am using:

void ListFiles(EthernetClient client, uint8_t flags) {
  // This code is just copied from SdFile.cpp in the SDFat library
  // and tweaked to print to the client output in html!
  dir_t p;
  
  root.rewind();
  client.print("<table width=\"50%\" height=\"14%\" border=\"0\">");
  while (root.readDir(p) > 0) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
    
    //skip image files, as we do not want to display them. below code will allow the code to ignore any file names with png, jpg, jpeg, gif and so on. 
    if (p.name[0] == 'O' || p.name[0] == 'N' || p.name[0] == 'F' || p.name[0] == 'G' || p.name[0] == 'R' || p.name[0] == 'E' || p.name[0] == 'R' || p.name[0] == 'D' || p.name[0] == 'P') continue;

    // print any indent spaces
    client.print("<tr><td align=\"center\"><a href=\"");
    for (uint8_t i = 0; i < 11; i++) {//12 characters since we have 8 characters for the file name, one character for the "." and then three for the extension --> 8 + 1 + 3 = 12
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print("."); // if we are at the beginning of the extension, print the period
      }
      client.print((char)p.name[i]);
    }
   client.print("\" target=\"blank\">");
    
    // print file name with possible blank fill
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print(".");
      }
      client.print((char)p.name[i]);
    }
    
    client.print("</a> || File Size: ");
    client.print(p.fileSize / 1024);
    client.print("Kilobytes (KB)");
    client.print("</td></tr>");
  }
  client.print("</table>");
}

the code i use to initialize my sd card is the following:

#include <SD.h>
//SD card required variabled
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
byte SD_init_OK;    //did the SD card initilzie ok? 1 = yes, 0 = no
byte SDFAT_init_OK;  //does the SD have an active FAT file system? yes = 1, no = 0
byte FAT_Type;        //what kind of file system does the card have? FAT32? FAT16?
byte root_OK;        //cold we open the root of the SD card? yes = 1, no = 0
byte SD_Type;      //is the SD card a SDHC or something else?
double volumesize;  //how much space is on the card in bytes?
double SD_USED_SPACE = 0;  //how many bytes of space is used on the card?
pinMode(53, OUTPUT);//required for SD library! 
  //because of an issue with the SD library, if NO SD card is present, the SPI bus runs very slow because of changes made to two registers.
  //we are going to make a backup of these registers now before we nitilize the SD card. 
  //that way, if no SD card is fond, we can re-initilize those registers back to their proper values  
  uint8_t spcr = SPCR;
  uint8_t spsr = SPSR;
  if (!card.init(0, 4)){
    SD_init_OK = 0;//SD card not initilized
    //no SD card found, restore the SPI bus settings for full speed
    SPCR = spcr;
    SPSR = spsr;
    data_log_enabled =0;
  }else{
    SD_init_OK = 1;//SD card initilized OK
    // initialize a FAT volume
    if (!volume.init(&card)){
      SDFAT_init_OK = 0;//FAT volume not initilized
      data_log_enabled =0;
    }else{
      SDFAT_init_OK = 1;//FAT volume initilized OK
      FAT_Type = volume.fatType();//what kind of fat is the card formatted with?
      if (!root.openRoot(&volume)){
        root_OK = 0;//the root of the SD card could not be opened!
        data_log_enabled =0;
      }else{
        root_OK = 1;
        SD_Type = card.type();                    //what kind of card was inserted?
        volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
        volumesize *= volume.clusterCount();       // we'll have a lot of clusters
        volumesize *= 512;                            // SD card blocks are always 512 bytes
      }
    }
  }

all of this code can be found here
http://forum.arduino.cc/index.php?topic=140740.0

the code will print the file name several times in HTML.

because HTML is formatted

<a href=file_name>file_name</a>

the code will also tell you the physical size of the file. maybe this will help you.

What seems strange to me is that the two write() functions form a cyclical relationship, they each call write().

The two write() functions/methods you posted do not appear to be from the same class.

One of the write() methods should, indeed, call the other. One write() method moves ONE character/byte to the output device. The other deals with arrays of characters/bytes, and calls the one char/byte method for each element in the array.

Could you tell me where the Serial class is declared so that I may examine it further?

Can I assume that you found it?

here is code i am using:

Is it working, or do you still have a problem?

PaulS,

I've been seeking out local help from a classmate to understand the code. I still can't find where the Serial class is defined and declared. I'd appreciate if you could direct me to it.

Also, could someone provide clarification on the "size" entity in the following code? (taken from Print.cpp)

Thank you very much.

-P

I still can't find where the Serial class is defined and declared.

The Serial object is an instance of the HardwareSerial class.

Also, could someone provide clarification on the "size" entity in the following code? (taken from Print.cpp)

The write() method has two overloads - one that sends a single byte and that sends an array of bytes. The code you posted a picture of is the second one. Since the number of elements in the array can not be determined from just a pointer to the array, you also need to define how many elements in the array (that you want to send).

I managed to identify which method of the SDFile class stores the names of the files on the card to an array of 13 chars. I created a custom method to save all of these file names to an array. However, some of the names contain anomalies. It seems that starting at the 12th or 13th indices, a strange "N " character and space increments through the names.


You want a picture of an answer? Post code, not a damned picture of it.

The name array is a fixed location in memory. Assigning several pointers to that same memory location is pointless.
You MUST make a copy of the data in name.

names[i] = strdup(name);

How big is your names array? Exactly how many elements does it hold? If you come up with any number other than 0, you are flat out wrong.

The syntax highlighting is helpful for me, but you can't grab code from a picture, sorry.

The size of the names array varies depending on how many files the user puts on the SD card. There could be 10 or 1000 files. Each element in the array holds an 13 bit name ("12345678.MID") + "/n".

The size of the names array varies depending on how many files the user puts on the SD card.

What it needs to be, and what it IS are two completely different things. Anyway, I don't know what I was thinking when I wrote what I did. The array, names, contains 13 elements. The values are all NULL, because you didn't provide initializers. The empty curly braces contribute nothing. You should get rid of them.

You need a completely different approach if you are going to be able to store more than 13 names in the array. You need to giver serious consideration to how much memory the Arduino has, how much the SD class uses, etc., to determine whether you really can store 1000 names in memory, when each name has 13 characters. Even a Mega doesn't have that much SRAM.

boingaon
Can you provide the full code? Because this code print nothing.

void populateNames(){
while (file.openNext(sd.vwd(), O_READ)) {
if(file.getFilename(name)){
Serial.println("Error");
}
names = name;
_ Serial.println(names*);_
_
i++;_
_
file.close();_
_
}_
_
}*_