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
3 changed files with 22 additions and 13 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

@ -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,7 +464,7 @@ 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) {
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
@ -477,8 +486,7 @@ void EInkDisplay::displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
// 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
@ -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)

View File

@ -29,6 +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* 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);