278 lines
7.4 KiB
C++
Raw Normal View History

2025-12-04 19:20:21 +07:00
#include "SDCardManager.h"
namespace {
constexpr uint8_t SD_CS = 12;
constexpr uint32_t SPI_FQ = 40000000;
}
2025-12-04 19:20:21 +07:00
SDCardManager SDCardManager::instance;
2025-12-04 19:20:21 +07:00
SDCardManager::SDCardManager() : sd() {}
2025-12-04 19:20:21 +07:00
bool SDCardManager::begin() {
if (!sd.begin(SD_CS, SPI_FQ)) {
if (Serial) Serial.printf("[%lu] [SD] SD card not detected\n", millis());
2025-12-04 19:20:21 +07:00
initialized = false;
} else {
if (Serial) Serial.printf("[%lu] [SD] SD card detected\n", millis());
2025-12-04 19:20:21 +07:00
initialized = true;
}
return initialized;
}
bool SDCardManager::ready() const {
return initialized;
}
std::vector<String> SDCardManager::listFiles(const char* path, const int maxFiles) {
2025-12-04 19:20:21 +07:00
std::vector<String> ret;
if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] not initialized, returning empty list\n", millis());
2025-12-04 19:20:21 +07:00
return ret;
}
auto root = sd.open(path);
2025-12-04 19:20:21 +07:00
if (!root) {
if (Serial) Serial.printf("[%lu] [SD] Failed to open directory\n", millis());
2025-12-04 19:20:21 +07:00
return ret;
}
if (!root.isDirectory()) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
2025-12-04 19:20:21 +07:00
root.close();
return ret;
}
int count = 0;
char name[128];
for (auto f = root.openNextFile(); f && count < maxFiles; f = root.openNextFile()) {
2025-12-04 19:20:21 +07:00
if (f.isDirectory()) {
f.close();
continue;
}
f.getName(name, sizeof(name));
ret.emplace_back(name);
2025-12-04 19:20:21 +07:00
f.close();
count++;
}
root.close();
return ret;
}
String SDCardManager::readFile(const char* path) {
if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] not initialized; cannot read file\n", millis());
return {""};
2025-12-04 19:20:21 +07:00
}
FsFile f;
if (!openFileForRead("SD", path, f)) {
return {""};
2025-12-04 19:20:21 +07:00
}
String content = "";
constexpr size_t maxSize = 50000; // Limit to 50KB
2025-12-04 19:20:21 +07:00
size_t readSize = 0;
while (f.available() && readSize < maxSize) {
const char c = static_cast<char>(f.read());
2025-12-04 19:20:21 +07:00
content += c;
readSize++;
}
f.close();
return content;
}
bool SDCardManager::readFileToStream(const char* path, Print& out, const size_t chunkSize) {
2025-12-04 19:20:21 +07:00
if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.println("SDCardManager: not initialized; cannot read file");
2025-12-04 19:20:21 +07:00
return false;
}
FsFile f;
if (!openFileForRead("SD", path, f)) {
2025-12-04 19:20:21 +07:00
return false;
}
constexpr size_t localBufSize = 256;
2025-12-04 19:20:21 +07:00
uint8_t buf[localBufSize];
const size_t toRead = (chunkSize == 0) ? localBufSize : (chunkSize < localBufSize ? chunkSize : localBufSize);
2025-12-04 19:20:21 +07:00
while (f.available()) {
const int r = f.read(buf, toRead);
2025-12-04 19:20:21 +07:00
if (r > 0) {
out.write(buf, static_cast<size_t>(r));
2025-12-04 19:20:21 +07:00
} else {
break;
}
}
f.close();
return true;
}
size_t SDCardManager::readFileToBuffer(const char* path, char* buffer, const size_t bufferSize, const size_t maxBytes) {
2025-12-04 19:20:21 +07:00
if (!buffer || bufferSize == 0)
return 0;
if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.println("SDCardManager: not initialized; cannot read file");
2025-12-04 19:20:21 +07:00
buffer[0] = '\0';
return 0;
}
FsFile f;
if (!openFileForRead("SD", path, f)) {
2025-12-04 19:20:21 +07:00
buffer[0] = '\0';
return 0;
}
const size_t maxToRead = (maxBytes == 0) ? (bufferSize - 1) : min(maxBytes, bufferSize - 1);
2025-12-04 19:20:21 +07:00
size_t total = 0;
while (f.available() && total < maxToRead) {
constexpr size_t chunk = 64;
const size_t want = maxToRead - total;
const size_t readLen = (want < chunk) ? want : chunk;
const int r = f.read(buffer + total, readLen);
2025-12-04 19:20:21 +07:00
if (r > 0) {
total += static_cast<size_t>(r);
2025-12-04 19:20:21 +07:00
} else {
break;
}
}
buffer[total] = '\0';
f.close();
return total;
}
bool SDCardManager::writeFile(const char* path, const String& content) {
if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.println("SDCardManager: not initialized; cannot write file");
2025-12-04 19:20:21 +07:00
return false;
}
// Remove existing file so we perform an overwrite rather than append
if (sd.exists(path)) {
sd.remove(path);
2025-12-04 19:20:21 +07:00
}
FsFile f;
if (!openFileForWrite("SD", path, f)) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.printf("Failed to open file for write: %s\n", path);
2025-12-04 19:20:21 +07:00
return false;
}
const size_t written = f.print(content);
2025-12-04 19:20:21 +07:00
f.close();
return written == content.length();
2025-12-04 19:20:21 +07:00
}
bool SDCardManager::ensureDirectoryExists(const char* path) {
if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.println("SDCardManager: not initialized; cannot create directory");
2025-12-04 19:20:21 +07:00
return false;
}
// Check if directory already exists
if (sd.exists(path)) {
FsFile dir = sd.open(path);
2025-12-04 19:20:21 +07:00
if (dir && dir.isDirectory()) {
dir.close();
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.printf("Directory already exists: %s\n", path);
2025-12-04 19:20:21 +07:00
return true;
}
dir.close();
}
// Create the directory
if (sd.mkdir(path)) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.printf("Created directory: %s\n", path);
2025-12-04 19:20:21 +07:00
return true;
} else {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.printf("Failed to create directory: %s\n", path);
2025-12-04 19:20:21 +07:00
return false;
}
}
bool SDCardManager::openFileForRead(const char* moduleName, const char* path, FsFile& file) {
if (!sd.exists(path)) {
if (Serial) Serial.printf("[%lu] [%s] File does not exist: %s\n", millis(), moduleName, path);
return false;
}
file = sd.open(path, O_RDONLY);
if (!file) {
if (Serial) 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, O_RDWR | O_CREAT | O_TRUNC);
if (!file) {
if (Serial) 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);
}