Skip to content

E-Ink Display Integration Best Practices

E-ink (e-paper) displays are the right choice for battery-powered devices that update infrequently. They retain their image with zero power. But they are slow, fragile, and easy to misuse.


Why E-Ink for Battery Projects

Property E-Ink OLED TFT LCD
Power at rest 0 Β΅A 1–10 mA 5–50 mA
Update power 20–40 mA peak ~20 mA ~50 mA
Update time 1–3 s (full) <50 ms <50 ms
Sunlight readable Excellent Poor Poor–fair
Image retention Permanent (no power) Fade on power-off Off = black
Viewing angle 180Β° Varies Limited

Best fit: displays that update once per minute or slower. Poor fit: fast-changing data, animations, gauges.


Partial vs Full Refresh

Mode Speed Ghosting Use for
Full refresh 1–3 s None First boot, periodic full updates
Partial refresh 100–500 ms Moderate Small area updates (clocks, counters)
Fast refresh ~500 ms High If library supports; use with caution

Full refresh clears the display through several flicker cycles before settling β€” it's slow but leaves no artifacts.

Partial refresh updates only the changed pixels. Faster, but ghosting (shadow of previous content) accumulates. Perform a full refresh every 5–10 partial updates to clear ghosts.

Ghosting mitigation: - Track update count in RTC memory across deep sleep cycles - Trigger full refresh when updateCount % 10 == 0

RTC_DATA_ATTR uint8_t partialUpdateCount = 0;

void refreshDisplay(GxEPD2_BW& display, bool forceFullUpdate = false) {
    if (forceFullUpdate || partialUpdateCount == 0) {
        display.setFullWindow();
        display.firstPage();
        // draw...
        display.nextPage();
        partialUpdateCount = 10;  // reset counter
    } else {
        display.setPartialWindow(x, y, w, h);
        // draw partial area...
        partialUpdateCount--;
    }
}

SPI Wiring

E-ink panels connect via SPI plus 3 control lines:

ESP32             E-Ink Panel
─────             ──────────────────
GPIO18 (SCK)  ──► CLK
GPIO23 (MOSI) ──► DIN / SDA
GPIO 5 (CS)   ──► CS
GPIO17 (DC)   ──► DC (Data/Command)
GPIO16 (RST)  ──► RST
GPIO 4 (BUSY) ◄── BUSY
              VCC ◄── 3.3 V only
              GND ◄── GND

BUSY is critical: the display drives BUSY LOW while processing. Never send commands while BUSY is LOW. Always poll or wait for BUSY HIGH before the next operation. Ignoring BUSY causes garbled display or display damage.

void waitBusy() {
    while (digitalRead(EINK_BUSY_PIN) == LOW) {
        delay(1);
    }
}

Over-Refresh and Display Longevity

E-ink panels have a rated refresh count β€” typically 1 million full refresh cycles for quality panels. At 1 full refresh per 5 minutes, that's ~10 years. That's fine.

Risks from over-refresh: - Driving partial refresh continuously without periodic full refresh β†’ permanent ghosting - Not waiting for BUSY signal β†’ incomplete waveform sequences β†’ pixel state corruption - Displaying the same static content without any refresh for years β†’ image sticking (though less common than LCD burn-in)

Safe update loop:

void updateDisplay(GxEPD2_BW<GxEPD2_154, GxEPD2_154::HEIGHT>& display) {
    display.init(115200, true, 2, false);  // reset on init
    display.setRotation(1);
    display.setTextColor(GxEPD_BLACK);

    display.setFullWindow();
    display.firstPage();
    do {
        display.fillScreen(GxEPD_WHITE);
        drawContent(display);
    } while (display.nextPage());

    display.hibernate();  // power down display driver β€” essential
}

display.hibernate() is mandatory after every update for low-power designs. It powers down the display driver IC; without it, the driver draws 1–3 mA continuously.


Frame Buffer Handling

E-ink libraries (GxEPD2, EPD_Libraries) manage an in-memory frame buffer. The buffer is sent to the panel on nextPage().

  • Buffer size: a 1.54" 200Γ—200 1-bit display = 200Γ—200Γ·8 = 5000 bytes in RAM
  • Larger panels (4.2" 400Γ—300) = 15,000 bytes β€” significant on ESP32 with 320 KB SRAM
  • For very large displays, use paged drawing (setFullWindow + firstPage/nextPage loop) instead of full-frame buffer

Paged drawing pattern (GxEPD2):

display.firstPage();
do {
    // This callback is called once per page (horizontal band)
    // Only draw what's in the current window β€” use display.getCursorY() to determine page
    display.fillScreen(GxEPD_WHITE);
    display.setCursor(10, 20);
    display.print("Hello");
} while (display.nextPage());

Sleep and Wake

E-ink displays draw current during refresh only. After refreshing:

  1. Call display.hibernate() β€” powers down the driver IC
  2. De-assert CS, DC, RST pins (set LOW or high-impedance) β€” prevents leakage through GPIO
  3. Power-gate the display's VCC rail if sleep current matters (<1 Β΅A target)

On wake: 1. Re-initialize SPI (SPI.begin(...)) 2. Re-initialize the display library (display.init(...)) 3. Draw and refresh 4. Hibernate again


Common Display Libraries

Library Displays supported Notes
GxEPD2 Waveshare, GDEY, Pimoroni Most actively maintained; partial refresh support
Adafruit EPD Adafruit-branded panels Good Adafruit-ecosystem integration
EPaper ESP32 LilyGo T5/T5S Vendor-specific for LilyGo boards

See Also

See also