Вопросы по библиотеке SdFat (Bill Greiman)

У меня проблема с пониманием работы этой библиотеки, после применения встроенной библиотеки SD.h
Я пытаюсь подсчитать количество файлов и папок на карте памяти. В примерах библиотеки SD есть пример “listfiles”. На основе этого примера я написал код, который вел такой подсчет числа папок и файлов. Но работал этот код жутко медленно. В итоге я принял решение переходить на работу с библиотекой SdFat. В границах первого этапа я объявил структуру “SdFat SD;” и в рамках версии библиотеки SdFat [2.2.2] код отлично работал и очень шустро. Но с выходом обновления библиотеки SdFat [2.2.3] рекурсия, которая содержалась в функции

void printDirectory(File dir, int numTabs); стала вызывать ошибку.

Текст listfiles из примеров библиотеки SD я приводить не буду.
Что касается примеров из библиотеки SdFat, то есть только OpenNext, но он выводит имена файлов и папок корневого каталога.
Вот попытка написать свой код на основе данного примера.

#include "SdFat.h"
#include <SPI.h>

#define SD_CS           10

// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
#define SD_FAT_TYPE 3

#if SD_FAT_TYPE == 0
SdFat sd;
File entry, currentDir;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 entry, currentDir;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile entry, currentDir;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile entry, currentDir;
#else  // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif  // SD_FAT_TYPE

uint16_t fileCnt;
uint16_t dirCnt;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    // wait for serial port to connect. Needed for native USB port only
  }
  Serial.print(F("\nInitializing SD card..."));
  while (!sd.begin(SD_CS, SD_SCK_MHZ(4))) {
    delay(1000);
    Serial.println(F("initialization failed. Things to check:"));
    Serial.println(F("* is a card inserted?"));
  }  
  currentDir.close();
  currentDir.open("/", O_RDONLY); //set file to root directory
  scanSD(); // подсчитываем число папок и файлов  
  Serial.print(F("DIRs: \t")); Serial.println(dirCnt);
  Serial.print(F("FILEs: \t")); Serial.println(fileCnt);
}

void loop() {}

void scanSD() {
  Serial.println(F("Debug ScanSD"));
  while(entry.openNext(&currentDir, O_RDONLY)) {
    if (entry.isDir() && !entry.isHidden()) {
    Serial.print(F("/")); Serial.print(dirCnt);
    Serial.println(F(" DIR Found..."));    
    dirCnt++;
//    scanSD(); 
  } 
  if (entry.isFile()) {
        Serial.println(F("FILE Found..."));        
        fileCnt++;
    }
    entry.close();
  }
}

проблема в том, что этим кодом я не попадаю в внутрь папки и подсчета файлов не происходит.
рекурсия должна быть по закомментированному вызову функции

// scanSD();

Только в примере библиотеки SD у аналогичной функции должен быть аргумент в виде

scanSD(File dir);

Но как раз на это и ругается версия 2.2.3 библиотеки SdFat

Растолкуйте, пожалуйста, как получить мне искомый вариант! Может есть пример такой функции?

Почему бы не пользоваться той версией библиотеки, в которой все работает?
И, кстати, именно в коде этой библиотеки, вероятно, и можно найти искомый Вами пример функции.

Да не вопрос! Но хотелось бы уйти от костылей с обратной совместимостью библиотек SdFat и SD и полностью использовать только функционал SdFat.

покажите тот код, что работал. И сообщение об ошибке

#include "SdFat.h"
#include <SPI.h>

#define SD_CS           10
SdFat SD;
File root;

#define filenameLength  64

uint16_t fileCnt;
uint16_t dirCnt;
char fName[filenameLength + 1];

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    // wait for serial port to connect. Needed for native USB port only
  }
  Serial.print(F("\nInitializing SD card..."));
  while (!SD.begin(SD_CS)) {
    delay(1000);
    Serial.println(F("initialization failed. Things to check:"));
    Serial.println(F("* is a card inserted?"));
  }
  Serial.println(F("Wiring is correct and a card is present."));  
  fileCnt = dirCnt = 0;
  root = SD.open("/", O_READ);
  if (!root) root.rewindDirectory(); //возвращаемся к первому файлу, если файл не существует
  scanSD(root); // подсчитываем число папок и файлов
  root.close();
  Serial.print(F("DIRs: \t")); Serial.println(dirCnt);
  Serial.print(F("FILEs: \t")); Serial.println(fileCnt);
}

void loop() {}

void scanSD(File dir) {
  while (true) {
    File entry =  dir.openNextFile();
    if (!entry) break;  // no more files
    if (entry.isDirectory()) {
      entry.getName(fName, filenameLength);
      Serial.print("/"); Serial.println(fName);
      dirCnt++;
      scanSD(entry);
    } 
    else {
      fileCnt++;
    }
    entry.close();
  }
}

Вот этот код подсчитывает число папок и файлов с библиотекой SdFat [2.2.2].
С библиотекой [2.2.3] компилятор выдаёт ошибку

G:\Arduino\SD_SdFat_scanSD\SD_SdFat_scanSD.ino: In function 'void setup()':
SD_SdFat_scanSD:29:14: error: use of deleted function 'File32::File32(const File32&)'
   scanSD(root); // подсчитываем число папок и файлов
              ^
In file included from C:\Users\Documents\Arduino\libraries\SdFat\src/FatLib/FatVolume.h:27:0,
                 from C:\Users\Documents\Arduino\libraries\SdFat\src/FatLib/FatLib.h:28,
                 from C:\Users\Documents\Arduino\libraries\SdFat\src/SdFat.h:32,
                 from G:\Arduino\SD_SdFat_scanSD\SD_SdFat_scanSD.ino:1:
C:\Users\Documents\Arduino\libraries\SdFat\src/FatLib/FatFile.h:1088:7: note: 'File32::File32(const File32&)' is implicitly deleted because the default definition would be ill-formed:
 class File32 : public StreamFile<FatFile, uint32_t> {
       ^~~~~~
C:\Users\Documents\Arduino\libraries\SdFat\src/FatLib/FatFile.h:1088:7: error: use of deleted function 'StreamFile<FatFile, long unsigned int>::StreamFile(const StreamFile<FatFile, long unsigned int>&)'
In file included from C:\Users\Documents\Arduino\libraries\SdFat\src/ExFatLib/ExFatFile.h:883:0,
                 from C:\Users\Documents\Arduino\libraries\SdFat\src/ExFatLib/ExFatVolume.h:27,
                 from C:\Users\Documents\Arduino\libraries\SdFat\src/ExFatLib/ExFatLib.h:28,
                 from C:\Users\Documents\Arduino\libraries\SdFat\src/SdFat.h:31,
                 from G:\Arduino\SD_SdFat_scanSD\SD_SdFat_scanSD.ino:1:
c:\users\documents\arduino\libraries\sdfat\src\common\arduinofiles.h:61:7: note: 'StreamFile<FatFile, long unsigned int>::StreamFile(const StreamFile<FatFile, long unsigned int>&)' is implicitly deleted because the default definition would be ill-formed:
 class StreamFile : public stream_t, public BaseFile {
       ^~~~~~~~~~
c:\users\documents\arduino\libraries\sdfat\src\common\arduinofiles.h:61:7: error: 'constexpr FatFile::FatFile(const FatFile&)' is private within this context
In file included from C:\Users\Documents\Arduino\libraries\SdFat\src/FatLib/FatVolume.h:27:0,
                 from C:\Users\Documents\Arduino\libraries\SdFat\src/FatLib/FatLib.h:28,
                 from C:\Users\Documents\Arduino\libraries\SdFat\src/SdFat.h:32,
                 from G:\Arduino\SD_SdFat_scanSD\SD_SdFat_scanSD.ino:1:
C:\Users\Documents\Arduino\libraries\SdFat\src/FatLib/FatFile.h:157:3: note: declared private here
   FatFile(const FatFile& from) = default;
   ^~~~~~~
G:\Arduino\SD_SdFat_scanSD\SD_SdFat_scanSD.ino:37:6: note:   initializing argument 1 of 'void scanSD(File)'
 void scanSD(File dir) {
      ^~~~~~
G:\Arduino\SD_SdFat_scanSD\SD_SdFat_scanSD.ino: In function 'void scanSD(File)':
SD_SdFat_scanSD:45:19: error: use of deleted function 'File32::File32(const File32&)'
       scanSD(entry);
                   ^
G:\Arduino\SD_SdFat_scanSD\SD_SdFat_scanSD.ino:37:6: note:   initializing argument 1 of 'void scanSD(File)'
 void scanSD(File dir) {
      ^~~~~~
exit status 1
use of deleted function 'File32::File32(const File32&)'

Правильнее исправить ваш код. В той строке, где ошибка - вы передаете внутрь своей функции scanSD обьект каталога по значению. Так не надо делать, это плохой стиль.
Передавайте его по ссылке - и никаких ошибок не будет.

Заголовок вашей функции

исправьте на
void scanSD(File &dir) {

и все

Да, Вы правы! С указателем на адрес данная функция заработала без проблем с библиотекой [2.2.3]. Огромное спасибо за подсказку!
Но тем не менее, мне бы в рамках данной темы все-таки хотелось бы понять, как подобную функцию можно написать для библиотеки SdFat? Сделал функцию с указателем, но все равно функция openNext() не заходит в папку и нет подсчета числа файлов.
Функцию void scanSD() из первого сообщения нужно заменить на

void scanSD(FsFile &dir) { 
  while(entry.openNext(&dir, O_RDONLY)) {
    if (entry.isDir() && !entry.isHidden()) {
    Serial.print(F("/")); Serial.println(dirCnt);
//    Serial.println(F(" DIR Found..."));    
    dirCnt++;
    scanSD(entry); 
  } 
  else if (entry.isFile()) {
        Serial.println(F("FILE Found..."));        
        fileCnt++;
    }
    entry.close();
  }
}

вы изьясняетесь непонятно. Сначала пишете

а потом спрашиваете

Библиотека 2.2.3 это и есть библиотека SdFat или я что-то не понял.

Тот код, что я исправил - он заработал или нет?

Ну что ж, давайте разбираться! Я сам не люблю путаницу.
С Вашей подсказкой у меня заработал код из сообщения #5. Но это код, который написан в режиме совместимости двух библиотек. Для этого объявлена структура

SdFat SD;

И все функции в данном листинге принадлежат встроенной библиотеке SD.h за исключением функции чтения имени файла

entry.getName(fName, filenameLength); //для SdFat

Но моё желание полностью уйти от от функций библиотеки SD.h и переписать код для библиотеки SdFat.h.
Сейчас он выглядит так:

#include "SdFat.h"
#include <SPI.h>

#define SD_CS           10
SdFat SD;
File root;

#define filenameLength  64

uint16_t fileCnt;
uint16_t dirCnt;
char fName[filenameLength + 1];

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    // wait for serial port to connect. Needed for native USB port only
  }
  Serial.print(F("\nInitializing SD card..."));
  while (!SD.begin(SD_CS)) {
    delay(1000);
    Serial.println(F("initialization failed. Things to check:"));
    Serial.println(F("* is a card inserted?"));
  }
  Serial.println(F("Wiring is correct and a card is present."));  
  fileCnt = dirCnt = 0;
  root = SD.open("/", O_READ);
  if (!root) root.rewindDirectory(); //возвращаемся к первому файлу, если файл не существует
  scanSD(root); // подсчитываем число папок и файлов
  root.close();
  Serial.print(F("DIRs: \t")); Serial.println(dirCnt);
  Serial.print(F("FILEs: \t")); Serial.println(fileCnt);
}

void loop() {}

void scanSD(FsFile &dir) { 
  while(entry.openNext(&dir, O_RDONLY)) {
    if (entry.isDir() && !entry.isHidden()) {
    Serial.print(F("/")); Serial.println(dirCnt);
//    Serial.println(F(" DIR Found..."));    
    dirCnt++;
    scanSD(entry); 
  } 
  else if (entry.isFile()) {
        Serial.println(F("FILE Found..."));        
        fileCnt++;
    }
    entry.close();
  }
}

Но при работе этого кода я читаю только корневой каталог файлов без подсчета общего числа файлов. Это вывод с монитора:

Initializing SD card...Wiring is correct and a card is present.

Debug ScanSD
/0
/1
/2
/3
/4
/5
/6
/7
/8
/9
/10
DIRs: 	11
FILEs: 	0

сейчас на подопытной карте памяти содержится именно 11 папок и 534 файла. Но подсчет файлов не происходит

Вопрос - у вас есть хоть один регулярный файл(не каталог) в корне?

Если убрать ветку if (entry.isDir() и оставить только ветку для файлов - будет ли число файлов в корне подсчитано верно?

В корне нет файлов, только папки. Даже папку “System volume…” я удалил, чтобы она не мешалась.

Сделайте тест - положите в корень несколько файлов и уберите ветку if (entry.isDir() из кода.
Больше ничего не меняйте

сделал тест. Добавил 3 файла в корень. Из кода ничего не убирал.
Вот данные с монитора:

Initializing SD card...Wiring is correct and a card is present.

Debug ScanSD
/0
/1
/2
/3
/4
/5
/6
/7
/8
/9
/10
FILE Found...
FILE Found...
FILE Found...
DIRs: 	11
FILEs: 	3

в корне находит файлы без проблем без переписывания кода

Каталоги 0 - 10 - они все в корне или есть вложенные в другие?

папки только в корне. Вложенных папок нет. Я пока простой пример пытаюсь освоить. Подсчитывать вложенные папки - это уже усложнение задачи

Добавьте хоть один вложенный и напишите, находит его или нет. Это нужно для локализации ошибки

Добавил по одной подпапке в папки 0 и 1. Ничего не изменилось.

Ну вот видите, дело-то не в файлах. Ваш метод scanSD работает только в корне.
Скорее всего обьект entry получаемый в строке

  • не является полноценным каталогом, в котором можно вести поиск.

Может быть, вам нужно сначала перейти в каталог чем-то типа chdir(), а уже потом искать в нем файлы.

Дальше оставляю эту задачку вам

Да в том и дело, что остаётся только гадать, ибо примера на эту тему автор библиотеки не предоставил. А я не супер программист, чтобы разобраться в чем причина. Я же не первый день вожусь прежде чем решился создать тему и задать вопрос

Я заглянул в исходники библиотеки - и это сотни отдельных файлов. К сожалению, я не имею сейчас возможности искать в них готовое решение. Организация кода, прямо скажем, не располагает…

не прибедняйтесь. Насколько я могу судить, ваш уровень понимания на голову выше большинства любителей. Я думаю, вы найдете ответ. Побольше тестов, чтобы понять что как работает - и решение отыщется.

Я бы с удовольствием в этом поучаствовал, но это явно не на 20 минут, сорри
Удачи