#include "low_level_display_epaper.h" #include #include #include // Waveshare 4.2" e-Paper monochrome driver (supports partial refresh) extern "C" { #include "EPD_4in2_V2.h" #include "DEV_Config.h" } // Note: EPD_4IN2_V2 is monochrome (black/white) and supports fast partial refresh LowLevelDisplayEPaper::LowLevelDisplayEPaper(const epaper_config* cfg, int w, int h) : config(cfg), width(w), height(h), initialized(false), framebuffer(nullptr), dirty(false) { // Allocate framebuffer (1 bit per pixel for monochrome e-paper) int buffer_size = (width * height + 7) / 8; // Round up to nearest byte framebuffer = (uint8_t*)malloc(buffer_size); if (framebuffer) { memset(framebuffer, 0xFF, buffer_size); // White } } LowLevelDisplayEPaper::~LowLevelDisplayEPaper() { if (framebuffer) { free(framebuffer); } } bool LowLevelDisplayEPaper::init() { if (initialized) { return true; } if (!framebuffer) { printf("Failed to allocate framebuffer for e-paper display\n"); return false; } printf("Initializing Waveshare 4.2\" e-Paper display (%dx%d)...\n", width, height); // Initialize Waveshare driver (handles GPIO and SPI setup) DEV_Module_Init(); EPD_4IN2_V2_Init(); printf("E-paper display initialized successfully (partial refresh enabled)\n"); initialized = true; return true; } void LowLevelDisplayEPaper::clear(bool white) { if (!framebuffer) return; int buffer_size = (width * height + 7) / 8; memset(framebuffer, white ? 0xFF : 0x00, buffer_size); dirty = true; } void LowLevelDisplayEPaper::draw_pixel(int x, int y, bool white) { if (!framebuffer || x < 0 || x >= width || y < 0 || y >= height) { return; } int byte_index = (y * width + x) / 8; int bit_index = 7 - ((y * width + x) % 8); if (white) { framebuffer[byte_index] |= (1 << bit_index); } else { framebuffer[byte_index] &= ~(1 << bit_index); } dirty = true; } void LowLevelDisplayEPaper::draw_buffer(const uint8_t* bit_buffer) { if (!bit_buffer || !framebuffer) return; // Direct copy of 1-bit buffer int buffer_size = (width * height + 7) / 8; memcpy(framebuffer, bit_buffer, buffer_size); dirty = true; } void LowLevelDisplayEPaper::refresh() { if (!dirty || !framebuffer) { return; } if (!initialized) { printf("E-paper not initialized, cannot refresh\n"); return; } // printf("Refreshing e-paper display (partial refresh)...\n"); // Use partial refresh for fast updates (~1 second instead of ~15 seconds) // Partial refresh updates only the changed pixels EPD_4IN2_V2_PartialDisplay(framebuffer, 0, 0, EPD_4IN2_V2_WIDTH, EPD_4IN2_V2_HEIGHT); // printf("E-paper partial refresh complete\n"); dirty = false; } void LowLevelDisplayEPaper::clear_display() { if (!initialized) { printf("E-paper not initialized, cannot clear\n"); return; } printf("Clearing e-paper display...\n"); EPD_4IN2_V2_Clear(); // Also clear framebuffer clear(true); dirty = false; // Already refreshed by EPD_4IN2_V2_Clear } void LowLevelDisplayEPaper::full_refresh() { if (!framebuffer || !initialized) { printf("E-paper not initialized or no framebuffer\n"); return; } printf("Performing full refresh (removes ghosting, ~15 seconds)...\n"); // Full refresh uses EPD_4IN2_V2_Display for complete screen update // This is slower but removes any ghosting from partial refreshes EPD_4IN2_V2_Display(framebuffer); printf("Full refresh complete\n"); dirty = false; } void LowLevelDisplayEPaper::sleep() { if (!initialized) { return; } printf("Putting e-paper display to sleep...\n"); EPD_4IN2_V2_Sleep(); } void LowLevelDisplayEPaper::on_idle_2min() { // E-paper doesn't dim } void LowLevelDisplayEPaper::on_idle_10min() { if (!is_sleeping) { sleep(); is_sleeping = true; printf("E-Paper: Entered sleep mode\n"); } } void LowLevelDisplayEPaper::on_user_interaction() { if (is_sleeping) { printf("E-Paper: Waking from sleep...\n"); init(); // Re-initialize to wake up is_sleeping = false; printf("E-Paper: Ready\n"); } }