Read SD card content to struct in memory

Hi,
I'm trying to prepare simple application on NodeMCU (ESP 8266) with microSD card reader.
The idea is very simple.
I want to read microSD card content structure (dir and files) and store it in the memory for later purposes.
I've prepare following code

#include <Arduino.h>
#include <SPI.h>
#include <SD.h>

struct FileItem {
  bool isDir;
  const char* name;
  FileItem* upDir;
  std::vector<FileItem> elements;
};

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ;  // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("Initializing SD card...");
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    while (1)
      ;
  }
  Serial.println("initialization done.");
  Serial.println("Creating structure of files");
  File root = SD.open("/");
  FileItem rootItem;
  rootItem.name = "/";
  rootItem.isDir = true;
  readSdCard(root, rootItem);
  displaySdCardContent(rootItem, 0);
}

void loop() {
  // put your main code here, to run repeatedly:
}

void readSdCard(File dir, FileItem& parentItem) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      break;  // no more files
    }
    FileItem item;
    item.isDir = entry.isDirectory();
    item.name = entry.fullName();
    parentItem.elements.push_back(item);
    Serial.printf("Add %s to %s\n", item.name, parentItem.name);
    if (entry.isDirectory()) {
      readSdCard(entry, item);
    }
  }
}

void displaySdCardContent(FileItem& item, int numTabs) {
  for (int i = 0; i < numTabs; i++) { Serial.print('\t'); }
  if (item.isDir) {
    Serial.printf("D %s (%s)\n", String(item.name), String(item.elements.size()));
    for (FileItem fileElement : item.elements) {
      displaySdCardContent(fileElement, numTabs + 1);
    }
  } else {
    Serial.printf("F %s", item.name);
  }
}

Where readSdCard reads all SD card content and store it in the struct FileItem.
Later displaySdCardContent displays information stored in the FileItem
What I can see in the logs is

  • readSdCard method reads data correctly from SD card (at least this is displayed in the Serial console)
  • method displaySdCardContent works weird. It displays incorrect name of the directory and elements in the FileItem looks like is empty.
    I've spend already couple of hours on it and have no idea why it behaves like that

The output in the console is

21:42:25.794 -> Creating structure of files
21:42:25.823 -> Add Directory01 to /
21:42:25.855 -> Add Directory01/Directory01-01 to Directory01
21:42:25.888 -> Add Directory01/Directory01-01/02.txt to Directory01/Directory01-01
21:42:25.954 -> Add Directory01/Directory01-01/01.txt to Directory01/Directory01-01
21:42:26.020 -> Add Directory01/Directory01-01/03.txt to Directory01/Directory01-01
21:42:26.088 -> Add Directory01/Directory01-02 to Directory01
21:42:26.154 -> Add Directory01/Directory01-02/03.txt to Directory01/Directory01-02
21:42:26.219 -> Add Directory01/Directory01-02/01.txt to Directory01/Directory01-02
21:42:26.286 -> Add Directory01/Directory01-02/02.txt to Directory01/Directory01-02
21:42:26.352 -> Add .Trashes to /
21:42:26.385 -> Add .Trashes/501 to .Trashes
21:42:26.417 -> Add .Trashes/._501 to .Trashes
21:42:26.449 -> D / (2)
21:42:26.449 -> 	D [<shes (0)
21:42:26.449 -> 	D [<shes (0)

you don't have memory allocated to store your file name

when you do this

you save a transient pointer to the data but it becomes stale

You can either dynamically allocate the right amount of RAM for the name, duplicate the name content into it and assign the pointer to the structure or have a fixed space (more costly in terms of memory) directly into the structure

something like

const uint8_t maxFileNameSize = 16;
struct FileItem {
  bool isDir;
  char name[maxFileNameSize+1]; // maxFileNameSize for the file name + trailing null char
  FileItem* upDir;
  std::vector<FileItem> elements;
};

and then instead of

you do

    strncpy(item.name,  entry.fullName(), maxFileNameSize); // https://cplusplus.com/reference/cstring/strncpy/
    item.name[maxFileNameSize] = '\0'; // just in case

That is working.
Thanks a lot :slight_smile:

Great
Have fun

@concir
Hi, i'm trying to use your code, but i can only print the first level with the
displaySdCardContent function.
How do you print subfolders content ?