Use SdFat as SDCardManager base and bump to 2.0.0

This commit is contained in:
Dave Allie 2025-12-29 21:23:05 +11:00
parent 98a5aa1f89
commit 0d269feed2
No known key found for this signature in database
GPG Key ID: F2FDDB3AD8D0276F
3 changed files with 165 additions and 69 deletions

View File

@ -1,13 +1,12 @@
#ifndef SDCARD_MANAGER_H #pragma once
#define SDCARD_MANAGER_H
#include <Arduino.h>
#include <WString.h>
#include <vector> #include <vector>
#include <SdFat.h>
class SDCardManager { class SDCardManager {
public: public:
SDCardManager(uint8_t epd_sclk, uint8_t sd_miso, uint8_t epd_mosi, uint8_t sd_cs, uint8_t eink_cs); SDCardManager();
bool begin(); bool begin();
bool ready() const; bool ready() const;
std::vector<String> listFiles(const char* path = "/", int maxFiles = 200); std::vector<String> listFiles(const char* path = "/", int maxFiles = 200);
@ -25,13 +24,27 @@ class SDCardManager {
// Ensure a directory exists, creating it if necessary. Returns true on success. // Ensure a directory exists, creating it if necessary. Returns true on success.
bool ensureDirectoryExists(const char* path); bool ensureDirectoryExists(const char* path);
FsFile open(const char* path, const oflag_t oflag = O_RDONLY) { return sd.open(path, oflag); }
bool mkdir(const char* path, const bool pFlag = true) { return sd.mkdir(path, pFlag); }
bool exists(const char* path) { return sd.exists(path); }
bool remove(const char* path) { return sd.remove(path); }
bool rmdir(const char* path) { return sd.rmdir(path); }
bool openFileForRead(const char* moduleName, const char* path, FsFile& file);
bool openFileForRead(const char* moduleName, const std::string& path, FsFile& file);
bool openFileForRead(const char* moduleName, const String& path, FsFile& file);
bool openFileForWrite(const char* moduleName, const char* path, FsFile& file);
bool openFileForWrite(const char* moduleName, const std::string& path, FsFile& file);
bool openFileForWrite(const char* moduleName, const String& path, FsFile& file);
bool removeDir(const char* path);
static SDCardManager& getInstance() { return instance; }
private: private:
uint8_t epd_sclk; static SDCardManager instance;
uint8_t sd_miso;
uint8_t epd_mosi;
uint8_t sd_cs;
uint8_t eink_cs;
bool initialized = false; bool initialized = false;
SdFat sd;
}; };
#endif #define SdMan SDCardManager::getInstance()

View File

@ -1,14 +1,20 @@
{ {
"name": "SDCardManager", "name": "SDCardManager",
"version": "1.0.0", "version": "2.0.0",
"description": "SD card file system utilities", "description": "SD card file system utilities",
"authors": [ "authors": [
{ {
"name": "CidVonHighwind", "name": "CidVonHighwind",
"url": "https://github.com/CidVonHighwind" "url": "https://github.com/CidVonHighwind"
},
{
"name": "Dave Allie",
"url": "https://github.com/daveallie"
} }
], ],
"dependencies": {}, "dependencies": {
"greiman/SdFat": "^2.3.1"
},
"platforms": "espressif32", "platforms": "espressif32",
"frameworks": ["arduino", "espidf"] "frameworks": ["arduino", "espidf"]
} }

View File

@ -1,24 +1,20 @@
#include "SDCardManager.h" #include "SDCardManager.h"
#include <SD.h> namespace {
#include <SPI.h> constexpr uint8_t SD_CS = 12;
constexpr uint32_t SPI_FQ = 40000000;
}
SDCardManager::SDCardManager(uint8_t epd_sclk, uint8_t sd_miso, uint8_t epd_mosi, uint8_t sd_cs, uint8_t eink_cs) SDCardManager SDCardManager::instance;
: epd_sclk(epd_sclk), sd_miso(sd_miso), epd_mosi(epd_mosi), sd_cs(sd_cs), eink_cs(eink_cs), initialized(false) {}
SDCardManager::SDCardManager() : sd() {}
bool SDCardManager::begin() { bool SDCardManager::begin() {
pinMode(eink_cs, OUTPUT); if (!sd.begin(SD_CS, SPI_FQ)) {
digitalWrite(eink_cs, HIGH); Serial.printf("[%lu] [SD] SD card not detected\n", millis());
pinMode(sd_cs, OUTPUT);
digitalWrite(sd_cs, HIGH);
SPI.begin(epd_sclk, sd_miso, epd_mosi, sd_cs);
if (!SD.begin(sd_cs, SPI, 40000000)) {
Serial.print("\n SD card not detected\n");
initialized = false; initialized = false;
} else { } else {
Serial.print("\n SD card detected\n"); Serial.printf("[%lu] [SD] SD card detected\n", millis());
initialized = true; initialized = true;
} }
@ -29,31 +25,33 @@ bool SDCardManager::ready() const {
return initialized; return initialized;
} }
std::vector<String> SDCardManager::listFiles(const char* path, int maxFiles) { std::vector<String> SDCardManager::listFiles(const char* path, const int maxFiles) {
std::vector<String> ret; std::vector<String> ret;
if (!initialized) { if (!initialized) {
Serial.println("SDCardManager: not initialized, returning empty list"); Serial.printf("[%lu] [SD] not initialized, returning empty list\n", millis());
return ret; return ret;
} }
File root = SD.open(path); auto root = sd.open(path);
if (!root) { if (!root) {
Serial.println("Failed to open directory."); Serial.printf("[%lu] [SD] Failed to open directory\n", millis());
return ret; return ret;
} }
if (!root.isDirectory()) { if (!root.isDirectory()) {
Serial.println("Path is not a directory."); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
root.close(); root.close();
return ret; return ret;
} }
int count = 0; int count = 0;
for (File f = root.openNextFile(); f && count < maxFiles; f = root.openNextFile()) { char name[128];
for (auto f = root.openNextFile(); f && count < maxFiles; f = root.openNextFile()) {
if (f.isDirectory()) { if (f.isDirectory()) {
f.close(); f.close();
continue; continue;
} }
ret.push_back(String(f.name())); f.getName(name, sizeof(name));
ret.emplace_back(name);
f.close(); f.close();
count++; count++;
} }
@ -63,21 +61,20 @@ std::vector<String> SDCardManager::listFiles(const char* path, int maxFiles) {
String SDCardManager::readFile(const char* path) { String SDCardManager::readFile(const char* path) {
if (!initialized) { if (!initialized) {
Serial.println("SDCardManager: not initialized; cannot read file"); Serial.printf("[%lu] [SD] not initialized; cannot read file\n", millis());
return String(""); return {""};
} }
File f = SD.open(path); FsFile f;
if (!f) { if (!openFileForRead("SD", path, f)) {
Serial.printf("Failed to open file: %s\n", path); return {""};
return String("");
} }
String content = ""; String content = "";
size_t maxSize = 50000; // Limit to 50KB constexpr size_t maxSize = 50000; // Limit to 50KB
size_t readSize = 0; size_t readSize = 0;
while (f.available() && readSize < maxSize) { while (f.available() && readSize < maxSize) {
char c = (char)f.read(); const char c = static_cast<char>(f.read());
content += c; content += c;
readSize++; readSize++;
} }
@ -85,26 +82,26 @@ String SDCardManager::readFile(const char* path) {
return content; return content;
} }
bool SDCardManager::readFileToStream(const char* path, Print& out, size_t chunkSize) { bool SDCardManager::readFileToStream(const char* path, Print& out, const size_t chunkSize) {
if (!initialized) { if (!initialized) {
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.println("SDCardManager: not initialized; cannot read file"); Serial.println("SDCardManager: not initialized; cannot read file");
return false; return false;
} }
File f = SD.open(path); FsFile f;
if (!f) { if (!openFileForRead("SD", path, f)) {
Serial.printf("Failed to open file: %s\n", path);
return false; return false;
} }
const size_t localBufSize = 256; constexpr size_t localBufSize = 256;
uint8_t buf[localBufSize]; uint8_t buf[localBufSize];
size_t toRead = (chunkSize == 0) ? localBufSize : (chunkSize < localBufSize ? chunkSize : localBufSize); const size_t toRead = (chunkSize == 0) ? localBufSize : (chunkSize < localBufSize ? chunkSize : localBufSize);
while (f.available()) { while (f.available()) {
int r = f.read(buf, toRead); const int r = f.read(buf, toRead);
if (r > 0) { if (r > 0) {
out.write(buf, (size_t)r); out.write(buf, static_cast<size_t>(r));
} else { } else {
break; break;
} }
@ -114,32 +111,32 @@ bool SDCardManager::readFileToStream(const char* path, Print& out, size_t chunkS
return true; return true;
} }
size_t SDCardManager::readFileToBuffer(const char* path, char* buffer, size_t bufferSize, size_t maxBytes) { size_t SDCardManager::readFileToBuffer(const char* path, char* buffer, const size_t bufferSize, const size_t maxBytes) {
if (!buffer || bufferSize == 0) if (!buffer || bufferSize == 0)
return 0; return 0;
if (!initialized) { if (!initialized) {
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.println("SDCardManager: not initialized; cannot read file"); Serial.println("SDCardManager: not initialized; cannot read file");
buffer[0] = '\0'; buffer[0] = '\0';
return 0; return 0;
} }
File f = SD.open(path); FsFile f;
if (!f) { if (!openFileForRead("SD", path, f)) {
Serial.printf("Failed to open file: %s\n", path);
buffer[0] = '\0'; buffer[0] = '\0';
return 0; return 0;
} }
size_t maxToRead = (maxBytes == 0) ? (bufferSize - 1) : min(maxBytes, bufferSize - 1); const size_t maxToRead = (maxBytes == 0) ? (bufferSize - 1) : min(maxBytes, bufferSize - 1);
size_t total = 0; size_t total = 0;
const size_t chunk = 64;
while (f.available() && total < maxToRead) { while (f.available() && total < maxToRead) {
size_t want = maxToRead - total; constexpr size_t chunk = 64;
size_t readLen = (want < chunk) ? want : chunk; const size_t want = maxToRead - total;
int r = f.read((uint8_t*)(buffer + total), readLen); const size_t readLen = (want < chunk) ? want : chunk;
const int r = f.read(buffer + total, readLen);
if (r > 0) { if (r > 0) {
total += (size_t)r; total += static_cast<size_t>(r);
} else { } else {
break; break;
} }
@ -152,37 +149,41 @@ size_t SDCardManager::readFileToBuffer(const char* path, char* buffer, size_t bu
bool SDCardManager::writeFile(const char* path, const String& content) { bool SDCardManager::writeFile(const char* path, const String& content) {
if (!initialized) { if (!initialized) {
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.println("SDCardManager: not initialized; cannot write file"); Serial.println("SDCardManager: not initialized; cannot write file");
return false; return false;
} }
// Remove existing file so we perform an overwrite rather than append // Remove existing file so we perform an overwrite rather than append
if (SD.exists(path)) { if (sd.exists(path)) {
SD.remove(path); sd.remove(path);
} }
File f = SD.open(path, FILE_WRITE); FsFile f;
if (!f) { if (!openFileForWrite("SD", path, f)) {
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.printf("Failed to open file for write: %s\n", path); Serial.printf("Failed to open file for write: %s\n", path);
return false; return false;
} }
size_t written = f.print(content); const size_t written = f.print(content);
f.close(); f.close();
return (written == content.length()); return written == content.length();
} }
bool SDCardManager::ensureDirectoryExists(const char* path) { bool SDCardManager::ensureDirectoryExists(const char* path) {
if (!initialized) { if (!initialized) {
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.println("SDCardManager: not initialized; cannot create directory"); Serial.println("SDCardManager: not initialized; cannot create directory");
return false; return false;
} }
// Check if directory already exists // Check if directory already exists
if (SD.exists(path)) { if (sd.exists(path)) {
File dir = SD.open(path); FsFile dir = sd.open(path);
if (dir && dir.isDirectory()) { if (dir && dir.isDirectory()) {
dir.close(); dir.close();
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.printf("Directory already exists: %s\n", path); Serial.printf("Directory already exists: %s\n", path);
return true; return true;
} }
@ -190,11 +191,87 @@ bool SDCardManager::ensureDirectoryExists(const char* path) {
} }
// Create the directory // Create the directory
if (SD.mkdir(path)) { if (sd.mkdir(path)) {
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.printf("Created directory: %s\n", path); Serial.printf("Created directory: %s\n", path);
return true; return true;
} else { } else {
Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
Serial.printf("Failed to create directory: %s\n", path); Serial.printf("Failed to create directory: %s\n", path);
return false; return false;
} }
} }
bool SDCardManager::openFileForRead(const char* moduleName, const char* path, FsFile& file) {
if (!sd.exists(path)) {
Serial.printf("[%lu] [%s] File does not exist: %s\n", millis(), moduleName, path);
return false;
}
file = sd.open(path, FILE_READ);
if (!file) {
Serial.printf("[%lu] [%s] Failed to open file for reading: %s\n", millis(), moduleName, path);
return false;
}
return true;
}
bool SDCardManager::openFileForRead(const char* moduleName, const std::string& path, FsFile& file) {
return openFileForRead(moduleName, path.c_str(), file);
}
bool SDCardManager::openFileForRead(const char* moduleName, const String& path, FsFile& file) {
return openFileForRead(moduleName, path.c_str(), file);
}
bool SDCardManager::openFileForWrite(const char* moduleName, const char* path, FsFile& file) {
file = sd.open(path, FILE_WRITE);
if (!file) {
Serial.printf("[%lu] [%s] Failed to open file for writing: %s\n", millis(), moduleName, path);
return false;
}
return true;
}
bool SDCardManager::openFileForWrite(const char* moduleName, const std::string& path, FsFile& file) {
return openFileForWrite(moduleName, path.c_str(), file);
}
bool SDCardManager::openFileForWrite(const char* moduleName, const String& path, FsFile& file) {
return openFileForWrite(moduleName, path.c_str(), file);
}
bool SDCardManager::removeDir(const char* path) {
// 1. Open the directory
auto dir = sd.open(path);
if (!dir) {
return false;
}
if (!dir.isDirectory()) {
return false;
}
auto file = dir.openNextFile();
char name[128];
while (file) {
String filePath = path;
if (!filePath.endsWith("/")) {
filePath += "/";
}
file.getName(name, sizeof(name));
filePath += name;
if (file.isDirectory()) {
if (!removeDir(filePath.c_str())) {
return false;
}
} else {
if (!sd.remove(filePath.c_str())) {
return false;
}
}
file = dir.openNextFile();
}
return sd.rmdir(path);
}