SDCardManager.cpp
1 #include "SDCardManager.h" 2 3 namespace { 4 constexpr uint8_t SD_CS = 12; 5 constexpr uint32_t SPI_FQ = 40000000; 6 } // namespace 7 8 SDCardManager SDCardManager::instance; 9 10 SDCardManager::SDCardManager() : sd() {} 11 12 bool SDCardManager::begin() { 13 if (!sd.begin(SD_CS, SPI_FQ)) { 14 if (Serial) Serial.printf("[%lu] [SD] SD card not detected\n", millis()); 15 initialized = false; 16 } else { 17 if (Serial) Serial.printf("[%lu] [SD] SD card detected\n", millis()); 18 initialized = true; 19 } 20 21 return initialized; 22 } 23 24 bool SDCardManager::ready() const { return initialized; } 25 26 std::vector<String> SDCardManager::listFiles(const char* path, const int maxFiles) { 27 std::vector<String> ret; 28 if (!initialized) { 29 if (Serial) Serial.printf("[%lu] [SD] not initialized, returning empty list\n", millis()); 30 return ret; 31 } 32 33 auto root = sd.open(path); 34 if (!root) { 35 if (Serial) Serial.printf("[%lu] [SD] Failed to open directory\n", millis()); 36 return ret; 37 } 38 if (!root.isDirectory()) { 39 if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); 40 root.close(); 41 return ret; 42 } 43 44 int count = 0; 45 char name[128]; 46 for (auto f = root.openNextFile(); f && count < maxFiles; f = root.openNextFile()) { 47 if (f.isDirectory()) { 48 f.close(); 49 continue; 50 } 51 f.getName(name, sizeof(name)); 52 ret.emplace_back(name); 53 f.close(); 54 count++; 55 } 56 root.close(); 57 return ret; 58 } 59 60 String SDCardManager::readFile(const char* path) { 61 if (!initialized) { 62 if (Serial) Serial.printf("[%lu] [SD] not initialized; cannot read file\n", millis()); 63 return {""}; 64 } 65 66 FsFile f; 67 if (!openFileForRead("SD", path, f)) { 68 return {""}; 69 } 70 71 constexpr size_t maxSize = 50000; // Limit to 50KB 72 const size_t fileSize = f.size(); 73 const size_t toRead = (fileSize < maxSize) ? fileSize : maxSize; 74 75 String content; 76 content.reserve(toRead); 77 78 uint8_t buf[256]; 79 size_t readSize = 0; 80 while (f.available() && readSize < toRead) { 81 const size_t chunkSize = min(sizeof(buf), toRead - readSize); 82 const int n = f.read(buf, chunkSize); 83 if (n <= 0) break; 84 content.concat(reinterpret_cast<char*>(buf), static_cast<size_t>(n)); 85 readSize += static_cast<size_t>(n); 86 } 87 f.close(); 88 return content; 89 } 90 91 bool SDCardManager::readFileToStream(const char* path, Print& out, const size_t chunkSize) { 92 if (!initialized) { 93 if (Serial) Serial.printf("[%lu] [SD] SD card not initialized\n", millis()); 94 return false; 95 } 96 97 FsFile f; 98 if (!openFileForRead("SD", path, f)) { 99 return false; 100 } 101 102 constexpr size_t localBufSize = 256; 103 uint8_t buf[localBufSize]; 104 const size_t toRead = (chunkSize == 0) ? localBufSize : (chunkSize < localBufSize ? chunkSize : localBufSize); 105 106 while (f.available()) { 107 const int r = f.read(buf, toRead); 108 if (r > 0) { 109 out.write(buf, static_cast<size_t>(r)); 110 } else { 111 break; 112 } 113 } 114 115 f.close(); 116 return true; 117 } 118 119 size_t SDCardManager::readFileToBuffer(const char* path, char* buffer, const size_t bufferSize, const size_t maxBytes) { 120 if (!buffer || bufferSize == 0) return 0; 121 if (!initialized) { 122 if (Serial) Serial.printf("[%lu] [SD] SD card not initialized\n", millis()); 123 buffer[0] = '\0'; 124 return 0; 125 } 126 127 FsFile f; 128 if (!openFileForRead("SD", path, f)) { 129 buffer[0] = '\0'; 130 return 0; 131 } 132 133 const size_t maxToRead = (maxBytes == 0) ? (bufferSize - 1) : min(maxBytes, bufferSize - 1); 134 size_t total = 0; 135 136 while (f.available() && total < maxToRead) { 137 constexpr size_t chunk = 64; 138 const size_t want = maxToRead - total; 139 const size_t readLen = (want < chunk) ? want : chunk; 140 const int r = f.read(buffer + total, readLen); 141 if (r > 0) { 142 total += static_cast<size_t>(r); 143 } else { 144 break; 145 } 146 } 147 148 buffer[total] = '\0'; 149 f.close(); 150 return total; 151 } 152 153 bool SDCardManager::writeFile(const char* path, const String& content) { 154 if (!initialized) { 155 if (Serial) Serial.printf("[%lu] [SD] SD card not initialized\n", millis()); 156 return false; 157 } 158 159 // Remove existing file so we perform an overwrite rather than append 160 if (sd.exists(path)) { 161 sd.remove(path); 162 } 163 164 FsFile f; 165 if (!openFileForWrite("SD", path, f)) { 166 return false; 167 } 168 169 const size_t written = f.print(content); 170 f.close(); 171 return written == content.length(); 172 } 173 174 bool SDCardManager::ensureDirectoryExists(const char* path) { 175 if (!initialized) { 176 if (Serial) Serial.printf("[%lu] [SD] SD card not initialized\n", millis()); 177 return false; 178 } 179 180 // Check if directory already exists 181 if (sd.exists(path)) { 182 FsFile dir = sd.open(path); 183 if (dir && dir.isDirectory()) { 184 dir.close(); 185 return true; 186 } 187 dir.close(); 188 } 189 190 // Create the directory 191 if (sd.mkdir(path)) { 192 if (Serial) Serial.printf("[%lu] [SD] Created directory: %s\n", millis(), path); 193 return true; 194 } else { 195 if (Serial) Serial.printf("[%lu] [SD] Failed to create directory: %s\n", millis(), path); 196 return false; 197 } 198 } 199 200 bool SDCardManager::openFileForRead(const char* moduleName, const char* path, FsFile& file) { 201 if (!sd.exists(path)) { 202 if (Serial) Serial.printf("[%lu] [%s] File does not exist: %s\n", millis(), moduleName, path); 203 return false; 204 } 205 206 file = sd.open(path, O_RDONLY); 207 if (!file) { 208 if (Serial) Serial.printf("[%lu] [%s] Failed to open file for reading: %s\n", millis(), moduleName, path); 209 return false; 210 } 211 return true; 212 } 213 214 bool SDCardManager::openFileForRead(const char* moduleName, const std::string& path, FsFile& file) { 215 return openFileForRead(moduleName, path.c_str(), file); 216 } 217 218 bool SDCardManager::openFileForRead(const char* moduleName, const String& path, FsFile& file) { 219 return openFileForRead(moduleName, path.c_str(), file); 220 } 221 222 bool SDCardManager::openFileForWrite(const char* moduleName, const char* path, FsFile& file) { 223 file = sd.open(path, O_RDWR | O_CREAT | O_TRUNC); 224 if (!file) { 225 if (Serial) Serial.printf("[%lu] [%s] Failed to open file for writing: %s\n", millis(), moduleName, path); 226 return false; 227 } 228 return true; 229 } 230 231 bool SDCardManager::openFileForWrite(const char* moduleName, const std::string& path, FsFile& file) { 232 return openFileForWrite(moduleName, path.c_str(), file); 233 } 234 235 bool SDCardManager::openFileForWrite(const char* moduleName, const String& path, FsFile& file) { 236 return openFileForWrite(moduleName, path.c_str(), file); 237 } 238 239 bool SDCardManager::removeDir(const char* path) { 240 // 1. Open the directory 241 auto dir = sd.open(path); 242 if (!dir) { 243 return false; 244 } 245 if (!dir.isDirectory()) { 246 return false; 247 } 248 249 auto file = dir.openNextFile(); 250 char name[128]; 251 while (file) { 252 String filePath = path; 253 if (!filePath.endsWith("/")) { 254 filePath += "/"; 255 } 256 file.getName(name, sizeof(name)); 257 filePath += name; 258 259 if (file.isDirectory()) { 260 if (!removeDir(filePath.c_str())) { 261 return false; 262 } 263 } else { 264 if (!sd.remove(filePath.c_str())) { 265 return false; 266 } 267 } 268 file = dir.openNextFile(); 269 } 270 271 return sd.rmdir(path); 272 }