Compare commits

..

2 Commits

Author SHA1 Message Date
cottongin
be6ba1b62b
feat: add turnOffScreen parameter for sunlight fading fix
Add optional turnOffScreen parameter to displayBuffer() and displayWindow()
to allow turning off the screen after refresh, mitigating sunlight fading issues.
2026-01-30 23:02:20 -05:00
cottongin
dede09001c
feat: add SDCardManager rename method and EInkDisplay improvements
- Add rename() method to SDCardManager for file/directory moves
- Improve EInkDisplay grayscale handling
2026-01-28 09:57:45 -05:00
4 changed files with 83 additions and 76 deletions

View File

@ -42,9 +42,9 @@ class EInkDisplay {
void cleanupGrayscaleBuffers(const uint8_t* bwBuffer); void cleanupGrayscaleBuffers(const uint8_t* bwBuffer);
#endif #endif
void displayBuffer(RefreshMode mode = FAST_REFRESH); void displayBuffer(RefreshMode mode = FAST_REFRESH, bool turnOffScreen = false);
// EXPERIMENTAL: Windowed update - display only a rectangular region // EXPERIMENTAL: Windowed update - display only a rectangular region
void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h); void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool turnOffScreen = false);
void displayGrayBuffer(bool turnOffScreen = false); void displayGrayBuffer(bool turnOffScreen = false);
void refreshDisplay(RefreshMode mode = FAST_REFRESH, bool turnOffScreen = false); void refreshDisplay(RefreshMode mode = FAST_REFRESH, bool turnOffScreen = false);

View File

@ -119,12 +119,12 @@ EInkDisplay::EInkDisplay(int8_t sclk, int8_t mosi, int8_t cs, int8_t dc, int8_t
frameBufferActive(nullptr), frameBufferActive(nullptr),
#endif #endif
customLutActive(false) { customLutActive(false) {
if (Serial) Serial.printf("[%lu] EInkDisplay: Constructor called\n", millis()); Serial.printf("[%lu] EInkDisplay: Constructor called\n", millis());
if (Serial) Serial.printf("[%lu] SCLK=%d, MOSI=%d, CS=%d, DC=%d, RST=%d, BUSY=%d\n", millis(), sclk, mosi, cs, dc, rst, busy); Serial.printf("[%lu] SCLK=%d, MOSI=%d, CS=%d, DC=%d, RST=%d, BUSY=%d\n", millis(), sclk, mosi, cs, dc, rst, busy);
} }
void EInkDisplay::begin() { void EInkDisplay::begin() {
if (Serial) Serial.printf("[%lu] EInkDisplay: begin() called\n", millis()); Serial.printf("[%lu] EInkDisplay: begin() called\n", millis());
frameBuffer = frameBuffer0; frameBuffer = frameBuffer0;
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE #ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
@ -134,18 +134,18 @@ void EInkDisplay::begin() {
// Initialize to white // Initialize to white
memset(frameBuffer0, 0xFF, BUFFER_SIZE); memset(frameBuffer0, 0xFF, BUFFER_SIZE);
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE #ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
if (Serial) Serial.printf("[%lu] Static frame buffer (%lu bytes = 48KB)\n", millis(), BUFFER_SIZE); Serial.printf("[%lu] Static frame buffer (%lu bytes = 48KB)\n", millis(), BUFFER_SIZE);
#else #else
memset(frameBuffer1, 0xFF, BUFFER_SIZE); memset(frameBuffer1, 0xFF, BUFFER_SIZE);
if (Serial) Serial.printf("[%lu] Static frame buffers (2 x %lu bytes = 96KB)\n", millis(), BUFFER_SIZE); Serial.printf("[%lu] Static frame buffers (2 x %lu bytes = 96KB)\n", millis(), BUFFER_SIZE);
#endif #endif
if (Serial) Serial.printf("[%lu] Initializing e-ink display driver...\n", millis()); Serial.printf("[%lu] Initializing e-ink display driver...\n", millis());
// Initialize SPI with custom pins // Initialize SPI with custom pins
SPI.begin(_sclk, -1, _mosi, _cs); SPI.begin(_sclk, -1, _mosi, _cs);
spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0); // MODE0 is standard for SSD1677 spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0); // MODE0 is standard for SSD1677
if (Serial) Serial.printf("[%lu] SPI initialized at 40 MHz, Mode 0\n", millis()); Serial.printf("[%lu] SPI initialized at 40 MHz, Mode 0\n", millis());
// Setup GPIO pins // Setup GPIO pins
pinMode(_cs, OUTPUT); pinMode(_cs, OUTPUT);
@ -156,7 +156,7 @@ void EInkDisplay::begin() {
digitalWrite(_cs, HIGH); digitalWrite(_cs, HIGH);
digitalWrite(_dc, HIGH); digitalWrite(_dc, HIGH);
if (Serial) Serial.printf("[%lu] GPIO pins configured\n", millis()); Serial.printf("[%lu] GPIO pins configured\n", millis());
// Reset display // Reset display
resetDisplay(); resetDisplay();
@ -164,7 +164,7 @@ void EInkDisplay::begin() {
// Initialize display controller // Initialize display controller
initDisplayController(); initDisplayController();
if (Serial) Serial.printf("[%lu] E-ink display driver initialized\n", millis()); Serial.printf("[%lu] E-ink display driver initialized\n", millis());
} }
// ============================================================================ // ============================================================================
@ -172,14 +172,14 @@ void EInkDisplay::begin() {
// ============================================================================ // ============================================================================
void EInkDisplay::resetDisplay() { void EInkDisplay::resetDisplay() {
if (Serial) Serial.printf("[%lu] Resetting display...\n", millis()); Serial.printf("[%lu] Resetting display...\n", millis());
digitalWrite(_rst, HIGH); digitalWrite(_rst, HIGH);
delay(20); delay(20);
digitalWrite(_rst, LOW); digitalWrite(_rst, LOW);
delay(2); delay(2);
digitalWrite(_rst, HIGH); digitalWrite(_rst, HIGH);
delay(20); delay(20);
if (Serial) Serial.printf("[%lu] Display reset complete\n", millis()); Serial.printf("[%lu] Display reset complete\n", millis());
} }
void EInkDisplay::sendCommand(uint8_t command) { void EInkDisplay::sendCommand(uint8_t command) {
@ -214,17 +214,17 @@ void EInkDisplay::waitWhileBusy(const char* comment) {
while (digitalRead(_busy) == HIGH) { while (digitalRead(_busy) == HIGH) {
delay(1); delay(1);
if (millis() - start > 10000) { if (millis() - start > 10000) {
if (Serial) Serial.printf("[%lu] Timeout waiting for busy%s\n", millis(), comment ? comment : ""); Serial.printf("[%lu] Timeout waiting for busy%s\n", millis(), comment ? comment : "");
break; break;
} }
} }
if (comment) { if (comment) {
if (Serial) Serial.printf("[%lu] Wait complete: %s (%lu ms)\n", millis(), comment, millis() - start); Serial.printf("[%lu] Wait complete: %s (%lu ms)\n", millis(), comment, millis() - start);
} }
} }
void EInkDisplay::initDisplayController() { void EInkDisplay::initDisplayController() {
if (Serial) Serial.printf("[%lu] Initializing SSD1677 controller...\n", millis()); Serial.printf("[%lu] Initializing SSD1677 controller...\n", millis());
const uint8_t TEMP_SENSOR_INTERNAL = 0x80; const uint8_t TEMP_SENSOR_INTERNAL = 0x80;
@ -258,7 +258,7 @@ void EInkDisplay::initDisplayController() {
// Set up full screen RAM area // Set up full screen RAM area
setRamArea(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); setRamArea(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
if (Serial) Serial.printf("[%lu] Clearing RAM buffers...\n", millis()); Serial.printf("[%lu] Clearing RAM buffers...\n", millis());
sendCommand(CMD_AUTO_WRITE_BW_RAM); // Auto write BW RAM sendCommand(CMD_AUTO_WRITE_BW_RAM); // Auto write BW RAM
sendData(0xF7); sendData(0xF7);
waitWhileBusy(" CMD_AUTO_WRITE_BW_RAM"); waitWhileBusy(" CMD_AUTO_WRITE_BW_RAM");
@ -267,7 +267,7 @@ void EInkDisplay::initDisplayController() {
sendData(0xF7); // Fill with white pattern sendData(0xF7); // Fill with white pattern
waitWhileBusy(" CMD_AUTO_WRITE_RED_RAM"); waitWhileBusy(" CMD_AUTO_WRITE_RED_RAM");
if (Serial) Serial.printf("[%lu] SSD1677 controller initialized\n", millis()); Serial.printf("[%lu] SSD1677 controller initialized\n", millis());
} }
void EInkDisplay::setRamArea(const uint16_t x, uint16_t y, uint16_t w, uint16_t h) { void EInkDisplay::setRamArea(const uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
@ -312,7 +312,7 @@ void EInkDisplay::clearScreen(const uint8_t color) const {
void EInkDisplay::drawImage(const uint8_t* imageData, const uint16_t x, const uint16_t y, const uint16_t w, const uint16_t h, void EInkDisplay::drawImage(const uint8_t* imageData, const uint16_t x, const uint16_t y, const uint16_t w, const uint16_t h,
const bool fromProgmem) const { const bool fromProgmem) const {
if (!frameBuffer) { if (!frameBuffer) {
if (Serial) Serial.printf("[%lu] ERROR: Frame buffer not allocated!\n", millis()); Serial.printf("[%lu] ERROR: Frame buffer not allocated!\n", millis());
return; return;
} }
@ -340,19 +340,19 @@ void EInkDisplay::drawImage(const uint8_t* imageData, const uint16_t x, const ui
} }
} }
if (Serial) Serial.printf("[%lu] Image drawn to frame buffer\n", millis()); Serial.printf("[%lu] Image drawn to frame buffer\n", millis());
} }
void EInkDisplay::writeRamBuffer(uint8_t ramBuffer, const uint8_t* data, uint32_t size) { void EInkDisplay::writeRamBuffer(uint8_t ramBuffer, const uint8_t* data, uint32_t size) {
const char* bufferName = (ramBuffer == CMD_WRITE_RAM_BW) ? "BW" : "RED"; const char* bufferName = (ramBuffer == CMD_WRITE_RAM_BW) ? "BW" : "RED";
const unsigned long startTime = millis(); const unsigned long startTime = millis();
if (Serial) Serial.printf("[%lu] Writing frame buffer to %s RAM (%lu bytes)...\n", startTime, bufferName, size); Serial.printf("[%lu] Writing frame buffer to %s RAM (%lu bytes)...\n", startTime, bufferName, size);
sendCommand(ramBuffer); sendCommand(ramBuffer);
sendData(data, size); sendData(data, size);
const unsigned long duration = millis() - startTime; const unsigned long duration = millis() - startTime;
if (Serial) Serial.printf("[%lu] %s RAM write complete (%lu ms)\n", millis(), bufferName, duration); Serial.printf("[%lu] %s RAM write complete (%lu ms)\n", millis(), bufferName, duration);
} }
void EInkDisplay::setFramebuffer(const uint8_t* bwBuffer) const { void EInkDisplay::setFramebuffer(const uint8_t* bwBuffer) const {
@ -399,25 +399,34 @@ void EInkDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t*
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE #ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
/** /**
* In single buffer mode, this should be called with the previously written BW buffer * In single buffer mode, this should be called with the previously written BW buffer
* to reconstruct the RED buffer for proper differential fast refreshes following a * to restore proper BW state after a grayscale display.
* grayscale display. *
* The approach: Don't call grayscaleRevert() at all. Instead, just sync the RAMs with
* BW data and clear the grayscale mode flag. The physical pixels will stay in their
* current grayscale states, but subsequent refreshes will naturally transition them
* as the new BW content is displayed.
*/ */
void EInkDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) { void EInkDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) {
// Clear grayscale mode - we don't want grayscaleRevert to be called later
// because the RAMs won't have valid grayscale data anymore
inGrayscaleMode = false;
// Sync both RAMs with BW content for proper fast refresh behavior
setRamArea(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); setRamArea(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
writeRamBuffer(CMD_WRITE_RAM_BW, bwBuffer, BUFFER_SIZE);
writeRamBuffer(CMD_WRITE_RAM_RED, bwBuffer, BUFFER_SIZE); writeRamBuffer(CMD_WRITE_RAM_RED, bwBuffer, BUFFER_SIZE);
} }
#endif #endif
void EInkDisplay::displayBuffer(RefreshMode mode) { void EInkDisplay::displayBuffer(RefreshMode mode, const bool turnOffScreen) {
if (!isScreenOn) { if (!isScreenOn && !turnOffScreen) {
// Force half refresh if screen is off // Force half refresh if screen is off
mode = HALF_REFRESH; mode = HALF_REFRESH;
} }
// If currently in grayscale mode, revert first to black/white // If currently in grayscale mode, revert first to black/white
if (inGrayscaleMode) { if (inGrayscaleMode) {
inGrayscaleMode = false; grayscaleRevert(); // grayscaleRevert() sets inGrayscaleMode = false internally
grayscaleRevert();
} }
// Set up full screen RAM area // Set up full screen RAM area
@ -442,7 +451,7 @@ void EInkDisplay::displayBuffer(RefreshMode mode) {
#endif #endif
// Refresh the display // Refresh the display
refreshDisplay(mode); refreshDisplay(mode, turnOffScreen);
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE #ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
// In single buffer mode always sync RED RAM after refresh to prepare for next fast refresh // In single buffer mode always sync RED RAM after refresh to prepare for next fast refresh
@ -455,37 +464,36 @@ void EInkDisplay::displayBuffer(RefreshMode mode) {
// EXPERIMENTAL: Windowed update support // EXPERIMENTAL: Windowed update support
// Displays only a rectangular region of the frame buffer, preserving the rest of the screen. // Displays only a rectangular region of the frame buffer, preserving the rest of the screen.
// Requirements: x and w must be byte-aligned (multiples of 8 pixels) // Requirements: x and w must be byte-aligned (multiples of 8 pixels)
void EInkDisplay::displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { void EInkDisplay::displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const bool turnOffScreen) {
if (Serial) Serial.printf("[%lu] Displaying window at (%d,%d) size (%dx%d)\n", millis(), x, y, w, h); Serial.printf("[%lu] Displaying window at (%d,%d) size (%dx%d)\n", millis(), x, y, w, h);
// Validate bounds // Validate bounds
if (x + w > DISPLAY_WIDTH || y + h > DISPLAY_HEIGHT) { if (x + w > DISPLAY_WIDTH || y + h > DISPLAY_HEIGHT) {
if (Serial) Serial.printf("[%lu] ERROR: Window bounds exceed display dimensions!\n", millis()); Serial.printf("[%lu] ERROR: Window bounds exceed display dimensions!\n", millis());
return; return;
} }
// Validate byte alignment // Validate byte alignment
if (x % 8 != 0 || w % 8 != 0) { if (x % 8 != 0 || w % 8 != 0) {
if (Serial) Serial.printf("[%lu] ERROR: Window x and width must be byte-aligned (multiples of 8)!\n", millis()); Serial.printf("[%lu] ERROR: Window x and width must be byte-aligned (multiples of 8)!\n", millis());
return; return;
} }
if (!frameBuffer) { if (!frameBuffer) {
if (Serial) Serial.printf("[%lu] ERROR: Frame buffer not allocated!\n", millis()); Serial.printf("[%lu] ERROR: Frame buffer not allocated!\n", millis());
return; return;
} }
// displayWindow is not supported while the rest of the screen has grayscale content, revert it // displayWindow is not supported while the rest of the screen has grayscale content, revert it
if (inGrayscaleMode) { if (inGrayscaleMode) {
inGrayscaleMode = false; grayscaleRevert(); // grayscaleRevert() sets inGrayscaleMode = false internally
grayscaleRevert();
} }
// Calculate window buffer size // Calculate window buffer size
const uint16_t windowWidthBytes = w / 8; const uint16_t windowWidthBytes = w / 8;
const uint32_t windowBufferSize = windowWidthBytes * h; const uint32_t windowBufferSize = windowWidthBytes * h;
if (Serial) Serial.printf("[%lu] Window buffer size: %lu bytes (%d x %d pixels)\n", millis(), windowBufferSize, w, h); Serial.printf("[%lu] Window buffer size: %lu bytes (%d x %d pixels)\n", millis(), windowBufferSize, w, h);
// Allocate temporary buffer on stack // Allocate temporary buffer on stack
std::vector<uint8_t> windowBuffer(windowBufferSize); std::vector<uint8_t> windowBuffer(windowBufferSize);
@ -517,7 +525,7 @@ void EInkDisplay::displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
#endif #endif
// Perform fast refresh // Perform fast refresh
refreshDisplay(FAST_REFRESH); refreshDisplay(FAST_REFRESH, turnOffScreen);
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE #ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
// Post-refresh: Sync RED RAM with current window (for next fast refresh) // Post-refresh: Sync RED RAM with current window (for next fast refresh)
@ -525,7 +533,7 @@ void EInkDisplay::displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
writeRamBuffer(CMD_WRITE_RAM_RED, windowBuffer.data(), windowBufferSize); writeRamBuffer(CMD_WRITE_RAM_RED, windowBuffer.data(), windowBufferSize);
#endif #endif
if (Serial) Serial.printf("[%lu] Window display complete\n", millis()); Serial.printf("[%lu] Window display complete\n", millis());
} }
void EInkDisplay::displayGrayBuffer(const bool turnOffScreen) { void EInkDisplay::displayGrayBuffer(const bool turnOffScreen) {
@ -583,20 +591,20 @@ void EInkDisplay::refreshDisplay(const RefreshMode mode, const bool turnOffScree
// Power on and refresh display // Power on and refresh display
const char* refreshType = (mode == FULL_REFRESH) ? "full" : (mode == HALF_REFRESH) ? "half" : "fast"; const char* refreshType = (mode == FULL_REFRESH) ? "full" : (mode == HALF_REFRESH) ? "half" : "fast";
if (Serial) Serial.printf("[%lu] Powering on display 0x%02X (%s refresh)...\n", millis(), displayMode, refreshType); Serial.printf("[%lu] Powering on display 0x%02X (%s refresh)...\n", millis(), displayMode, refreshType);
sendCommand(CMD_DISPLAY_UPDATE_CTRL2); sendCommand(CMD_DISPLAY_UPDATE_CTRL2);
sendData(displayMode); sendData(displayMode);
sendCommand(CMD_MASTER_ACTIVATION); sendCommand(CMD_MASTER_ACTIVATION);
// Wait for display to finish updating // Wait for display to finish updating
if (Serial) Serial.printf("[%lu] Waiting for display refresh...\n", millis()); Serial.printf("[%lu] Waiting for display refresh...\n", millis());
waitWhileBusy(refreshType); waitWhileBusy(refreshType);
} }
void EInkDisplay::setCustomLUT(const bool enabled, const unsigned char* lutData) { void EInkDisplay::setCustomLUT(const bool enabled, const unsigned char* lutData) {
if (enabled) { if (enabled) {
if (Serial) Serial.printf("[%lu] Loading custom LUT...\n", millis()); Serial.printf("[%lu] Loading custom LUT...\n", millis());
// Load custom LUT (first 105 bytes: VS + TP/RP + frame rate) // Load custom LUT (first 105 bytes: VS + TP/RP + frame rate)
sendCommand(CMD_WRITE_LUT); sendCommand(CMD_WRITE_LUT);
@ -617,15 +625,15 @@ void EInkDisplay::setCustomLUT(const bool enabled, const unsigned char* lutData)
sendData(pgm_read_byte(&lutData[109])); sendData(pgm_read_byte(&lutData[109]));
customLutActive = true; customLutActive = true;
if (Serial) Serial.printf("[%lu] Custom LUT loaded\n", millis()); Serial.printf("[%lu] Custom LUT loaded\n", millis());
} else { } else {
customLutActive = false; customLutActive = false;
if (Serial) Serial.printf("[%lu] Custom LUT disabled\n", millis()); Serial.printf("[%lu] Custom LUT disabled\n", millis());
} }
} }
void EInkDisplay::deepSleep() { void EInkDisplay::deepSleep() {
if (Serial) Serial.printf("[%lu] Preparing display for deep sleep...\n", millis()); Serial.printf("[%lu] Preparing display for deep sleep...\n", millis());
// First, power down the display properly // First, power down the display properly
// This shuts down the analog power rails and clock // This shuts down the analog power rails and clock
@ -645,7 +653,7 @@ void EInkDisplay::deepSleep() {
} }
// Now enter deep sleep mode // Now enter deep sleep mode
if (Serial) Serial.printf("[%lu] Entering deep sleep mode...\n", millis()); Serial.printf("[%lu] Entering deep sleep mode...\n", millis());
sendCommand(CMD_DEEP_SLEEP); sendCommand(CMD_DEEP_SLEEP);
sendData(0x01); // Enter deep sleep sendData(0x01); // Enter deep sleep
} }
@ -656,7 +664,7 @@ void EInkDisplay::saveFrameBufferAsPBM(const char* filename) {
std::ofstream file(filename, std::ios::binary); std::ofstream file(filename, std::ios::binary);
if (!file) { if (!file) {
if (Serial) Serial.printf("Failed to open %s for writing\n", filename); Serial.printf("Failed to open %s for writing\n", filename);
return; return;
} }
@ -692,9 +700,9 @@ void EInkDisplay::saveFrameBufferAsPBM(const char* filename) {
file.write(reinterpret_cast<const char*>(rotatedBuffer.data()), rotatedBuffer.size()); file.write(reinterpret_cast<const char*>(rotatedBuffer.data()), rotatedBuffer.size());
file.close(); file.close();
if (Serial) Serial.printf("Saved framebuffer to %s\n", filename); Serial.printf("Saved framebuffer to %s\n", filename);
#else #else
(void)filename; (void)filename;
if (Serial) Serial.println("saveFrameBufferAsPBM is not supported on Arduino builds."); Serial.println("saveFrameBufferAsPBM is not supported on Arduino builds.");
#endif #endif
} }

View File

@ -2,7 +2,6 @@
#include <WString.h> #include <WString.h>
#include <vector> #include <vector>
#include <string>
#include <SdFat.h> #include <SdFat.h>
class SDCardManager { class SDCardManager {
@ -30,7 +29,7 @@ class SDCardManager {
bool exists(const char* path) { return sd.exists(path); } bool exists(const char* path) { return sd.exists(path); }
bool remove(const char* path) { return sd.remove(path); } bool remove(const char* path) { return sd.remove(path); }
bool rmdir(const char* path) { return sd.rmdir(path); } bool rmdir(const char* path) { return sd.rmdir(path); }
bool rename(const char* path, const char* newPath) { return sd.rename(path, newPath); } bool rename(const char* oldPath, const char* newPath) { return sd.rename(oldPath, newPath); }
bool openFileForRead(const char* moduleName, const char* path, FsFile& file); 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 std::string& path, FsFile& file);

View File

@ -11,10 +11,10 @@ SDCardManager::SDCardManager() : sd() {}
bool SDCardManager::begin() { bool SDCardManager::begin() {
if (!sd.begin(SD_CS, SPI_FQ)) { if (!sd.begin(SD_CS, SPI_FQ)) {
if (Serial) Serial.printf("[%lu] [SD] SD card not detected\n", millis()); Serial.printf("[%lu] [SD] SD card not detected\n", millis());
initialized = false; initialized = false;
} else { } else {
if (Serial) Serial.printf("[%lu] [SD] SD card detected\n", millis()); Serial.printf("[%lu] [SD] SD card detected\n", millis());
initialized = true; initialized = true;
} }
@ -28,17 +28,17 @@ bool SDCardManager::ready() const {
std::vector<String> SDCardManager::listFiles(const char* path, const 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) {
if (Serial) Serial.printf("[%lu] [SD] not initialized, returning empty list\n", millis()); Serial.printf("[%lu] [SD] not initialized, returning empty list\n", millis());
return ret; return ret;
} }
auto root = sd.open(path); auto root = sd.open(path);
if (!root) { if (!root) {
if (Serial) Serial.printf("[%lu] [SD] Failed to open directory\n", millis()); Serial.printf("[%lu] [SD] Failed to open directory\n", millis());
return ret; return ret;
} }
if (!root.isDirectory()) { if (!root.isDirectory()) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
root.close(); root.close();
return ret; return ret;
} }
@ -61,7 +61,7 @@ std::vector<String> SDCardManager::listFiles(const char* path, const int maxFile
String SDCardManager::readFile(const char* path) { String SDCardManager::readFile(const char* path) {
if (!initialized) { if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] not initialized; cannot read file\n", millis()); Serial.printf("[%lu] [SD] not initialized; cannot read file\n", millis());
return {""}; return {""};
} }
@ -84,8 +84,8 @@ String SDCardManager::readFile(const char* path) {
bool SDCardManager::readFileToStream(const char* path, Print& out, const size_t chunkSize) { bool SDCardManager::readFileToStream(const char* path, Print& out, const size_t chunkSize) {
if (!initialized) { if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.println("SDCardManager: not initialized; cannot read file"); Serial.println("SDCardManager: not initialized; cannot read file");
return false; return false;
} }
@ -115,8 +115,8 @@ size_t SDCardManager::readFileToBuffer(const char* path, char* buffer, const siz
if (!buffer || bufferSize == 0) if (!buffer || bufferSize == 0)
return 0; return 0;
if (!initialized) { if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) 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;
} }
@ -149,8 +149,8 @@ size_t SDCardManager::readFileToBuffer(const char* path, char* buffer, const siz
bool SDCardManager::writeFile(const char* path, const String& content) { bool SDCardManager::writeFile(const char* path, const String& content) {
if (!initialized) { if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.println("SDCardManager: not initialized; cannot write file"); Serial.println("SDCardManager: not initialized; cannot write file");
return false; return false;
} }
@ -161,8 +161,8 @@ bool SDCardManager::writeFile(const char* path, const String& content) {
FsFile f; FsFile f;
if (!openFileForWrite("SD", path, f)) { if (!openFileForWrite("SD", path, f)) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) 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;
} }
@ -173,8 +173,8 @@ bool SDCardManager::writeFile(const char* path, const String& content) {
bool SDCardManager::ensureDirectoryExists(const char* path) { bool SDCardManager::ensureDirectoryExists(const char* path) {
if (!initialized) { if (!initialized) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.println("SDCardManager: not initialized; cannot create directory"); Serial.println("SDCardManager: not initialized; cannot create directory");
return false; return false;
} }
@ -183,8 +183,8 @@ bool SDCardManager::ensureDirectoryExists(const char* path) {
FsFile dir = sd.open(path); FsFile dir = sd.open(path);
if (dir && dir.isDirectory()) { if (dir && dir.isDirectory()) {
dir.close(); dir.close();
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.printf("Directory already exists: %s\n", path); Serial.printf("Directory already exists: %s\n", path);
return true; return true;
} }
dir.close(); dir.close();
@ -192,25 +192,25 @@ bool SDCardManager::ensureDirectoryExists(const char* path) {
// Create the directory // Create the directory
if (sd.mkdir(path)) { if (sd.mkdir(path)) {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) Serial.printf("Created directory: %s\n", path); Serial.printf("Created directory: %s\n", path);
return true; return true;
} else { } else {
if (Serial) Serial.printf("[%lu] [SD] Path is not a directory\n", millis()); Serial.printf("[%lu] [SD] Path is not a directory\n", millis());
if (Serial) 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) { bool SDCardManager::openFileForRead(const char* moduleName, const char* path, FsFile& file) {
if (!sd.exists(path)) { if (!sd.exists(path)) {
if (Serial) Serial.printf("[%lu] [%s] File does not exist: %s\n", millis(), moduleName, path); Serial.printf("[%lu] [%s] File does not exist: %s\n", millis(), moduleName, path);
return false; return false;
} }
file = sd.open(path, O_RDONLY); file = sd.open(path, O_RDONLY);
if (!file) { if (!file) {
if (Serial) Serial.printf("[%lu] [%s] Failed to open file for reading: %s\n", millis(), moduleName, path); Serial.printf("[%lu] [%s] Failed to open file for reading: %s\n", millis(), moduleName, path);
return false; return false;
} }
return true; return true;
@ -227,7 +227,7 @@ bool SDCardManager::openFileForRead(const char* moduleName, const String& path,
bool SDCardManager::openFileForWrite(const char* moduleName, const char* path, FsFile& file) { bool SDCardManager::openFileForWrite(const char* moduleName, const char* path, FsFile& file) {
file = sd.open(path, O_RDWR | O_CREAT | O_TRUNC); file = sd.open(path, O_RDWR | O_CREAT | O_TRUNC);
if (!file) { if (!file) {
if (Serial) Serial.printf("[%lu] [%s] Failed to open file for writing: %s\n", millis(), moduleName, path); Serial.printf("[%lu] [%s] Failed to open file for writing: %s\n", millis(), moduleName, path);
return false; return false;
} }
return true; return true;