/* NicheGraphics parallel E-Ink driver for the LilyGo T5-S3-ePaper-Pro (ED047TC1). InkHUD buffer format : 1bpp, horizontal bytes, MSB = leftmost pixel, 1 = white FastEPD buffer format: 1bpp, horizontal bytes, MSB = leftmost pixel, 1 = white Both formats share the same pixel layout and polarity (1 = white, 0 = black). The InkHUD safe-area buffer (944×523) is copied into the centre of the physical 960×540 FastEPD buffer so content clears the panel's inactive edge border. See ED047TC1.h for the H_OFFSET_BYTES / V_OFFSET_TOP / V_OFFSET_BOTTOM constants. */ // Ruler diagnostic — uncomment to draw calibration lines at each physical edge. // #define EINK_EDGE_LINES #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS #ifdef T5_S3_EPAPER_PRO #include "./ED047TC1.h" #include "FastEPD.h" #include "configuration.h" using namespace NicheGraphics::Drivers; void ED047TC1::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst) { // Parallel display — SPI parameters are not used (void)spi; (void)pin_dc; (void)pin_cs; (void)pin_busy; (void)pin_rst; epaper = new FASTEPD; #if defined(T5_S3_EPAPER_PRO_V1) epaper->initPanel(BB_PANEL_LILYGO_T5PRO, 28000000); #elif defined(T5_S3_EPAPER_PRO_V2) epaper->initPanel(BB_PANEL_LILYGO_T5PRO_V2, 28000000); // Initialize all PCA9535 port-0 pins as outputs / HIGH for (int i = 0; i < 8; i++) { epaper->ioPinMode(i, OUTPUT); epaper->ioWrite(i, HIGH); } #else #error "ED047TC1 driver: unsupported variant — define T5_S3_EPAPER_PRO_V1 or T5_S3_EPAPER_PRO_V2" #endif epaper->setMode(BB_MODE_1BPP); epaper->clearWhite(); epaper->fullUpdate(true); // Blocking initial clear } void ED047TC1::update(uint8_t *imageData, UpdateTypes type) { if (!epaper) return; // InkHUD renders into a DISPLAY_WIDTH × DISPLAY_HEIGHT safe-area buffer. // We need to place that into the centre of the physical 960×540 FastEPD buffer, // leaving blank margins at every edge to avoid the panel's inactive border. const uint32_t srcRowBytes = (DISPLAY_WIDTH + 7) / 8; // bytes per row in InkHUD buffer (118) const uint32_t dstRowBytes = (960 + 7) / 8; // bytes per row in physical buffer (120) const uint32_t dstTotalRows = 540; uint8_t *cur = epaper->currentBuffer(); // Fill physical buffer with white (0xFF = white in FastEPD 1bpp) memset(cur, 0xFF, dstRowBytes * dstTotalRows); // Copy each InkHUD row into the physical buffer with horizontal + vertical offsets for (uint32_t row = 0; row < DISPLAY_HEIGHT; row++) { const uint8_t *srcRow = imageData + row * srcRowBytes; uint8_t *dstRow = cur + (row + V_OFFSET_TOP) * dstRowBytes + H_OFFSET_BYTES; memcpy(dstRow, srcRow, srcRowBytes); } #ifdef EINK_EDGE_LINES // Draw a 1px black box at the exact boundary of the safe area within the // physical buffer. If the margins are correct, all 4 lines should be // fully visible and right at the edge of the usable display area. auto setPixelBlack = [&](uint32_t col, uint32_t row) { cur[row * dstRowBytes + col / 8] &= ~(0x80 >> (col % 8)); }; const uint32_t safeX = H_OFFSET_BYTES * 8; const uint32_t safeY = V_OFFSET_TOP; const uint32_t safeW = DISPLAY_WIDTH; const uint32_t safeH = DISPLAY_HEIGHT; // Top edge: horizontal line at safeY for (uint32_t col = safeX; col < safeX + safeW; col++) setPixelBlack(col, safeY); // Bottom edge: horizontal line at safeY + safeH - 1 for (uint32_t col = safeX; col < safeX + safeW; col++) setPixelBlack(col, safeY + safeH - 1); // Left edge: vertical line at safeX for (uint32_t row = safeY; row < safeY + safeH; row++) setPixelBlack(safeX, row); // Right edge: vertical line at safeX + safeW - 1 for (uint32_t row = safeY; row < safeY + safeH; row++) setPixelBlack(safeX + safeW - 1, row); #endif if (type == FULL) { epaper->fullUpdate(CLEAR_SLOW, false); epaper->backupPlane(); // Sync pPrevious so next partialUpdate has a correct baseline } else { // FAST: true partial update — compares pCurrent vs pPrevious and only applies // the update waveform to rows that actually changed. Unchanged rows get a neutral // signal (no visible effect). partialUpdate() updates pPrevious internally. epaper->partialUpdate(false, 0, dstTotalRows - 1); } } #endif // T5_S3_EPAPER_PRO #endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS