LabWired The easy way to build hardware
An ESP32 drives a display in the LabWired playground.

Render your Arduino e-paper firmware without the board or the panel

Category: Tutorial

You build a stock GxEPD2 sketch with PlatformIO. The sketch boots in the LabWired simulator. The sketch draws a page in the simulator. The page is the same as the page on a physical Waveshare 2.9″ panel.

Why e-paper development is slow

If you write e-paper firmware, you know this loop. Move a label two pixels. Rebuild the firmware. Flash the board. Wait for the multi-second refresh of the panel. Look closely at the glass. If the driver class is wrong, the panel reports success. The panel renders a blank page. Each iteration needs the display wired to the pins. You can flash only one board at a time.

The code that draws the page is not the slow part. The work around the code is the slow part. This post sets up a loop. The same binary runs in a window on your laptop. The binary paints the same pixels.

The sketch

The sketch has no porting layer. The sketch has no mock. The sketch is a stock Arduino-ESP32 program. The program drives GxEPD2 on an ESP32-WROOM-32. GxEPD2 is an e-paper display library. The board has a Waveshare 2.9″ tri-color panel.

#include <Arduino.h>
#include <GxEPD2_3C.h>
#include <Fonts/FreeSerifBold12pt7b.h>

// Waveshare 2.9" tri-color (C90c driver). CS=5 DC=17 RST=16 BUSY=4.
GxEPD2_3C<GxEPD2_290_C90c, GxEPD2_290_C90c::HEIGHT> display(
    GxEPD2_290_C90c(/*CS=*/5, /*DC=*/17, /*RST=*/16, /*BUSY=*/4));

void drawPage() {
  display.setFullWindow();
  display.firstPage();
  do {
    display.fillScreen(GxEPD_WHITE);
    display.setTextColor(GxEPD_BLACK);
    display.setFont(&FreeSerifBold12pt7b);
    display.setCursor(8, 24);
    display.print("LabWired Reader");
    display.setTextColor(GxEPD_RED);
    display.setCursor(230, 122);
    display.print("Page 1");
  } while (display.nextPage());
}

void setup() {
  Serial.begin(115200);
  display.init(115200);
  display.setRotation(1);
  drawPage();
  display.hibernate();
}

The program is the full Arduino framework. FreeRTOS is under the framework. The program talks to the driver IC of the panel through SPI. No code in the sketch knows if the other end of the bus is glass or a model.

Build with PlatformIO

The build uses the standard espressif32 / Arduino setup. The build pulls GxEPD2 from the registry.

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino

lib_deps =
    zinggjm/GxEPD2@^1.6.0

pio run produces firmware.elf. This image is the same image that you would flash to the board. The runnable example has the full sketch. The example has platformio.ini. The example has a one-command headless run.

Run on the digital twin

Give that ELF to the LabWired simulator. The simulator boots the Arduino-ESP32 image on an instruction-level Xtensa core model. The simulator starts FreeRTOS. The simulator runs your setup(). Your firmware clocks out SPI bytes. The SPI bytes go to a simulated e-paper panel. The panel decodes the bytes. The panel shows the page. The page is the same “LabWired Reader” page that you would see on glass.

flowchart LR
  A["GxEPD2 sketch"] -- "pio run" --> B["firmware.elf"]
  B --> C["LabWired sim<br/>Xtensa core + FreeRTOS"]
  C -- "SPI bytes" --> D["e-paper panel model"]
  D --> E(("rendered<br/>page"))
  classDef ok fill:#27c93f,stroke:#1a1a1a,stroke-width:2px,color:#0d2b12;
  class E ok;

Caption: This is the same ELF that you would flash to the board. Your browser renders the ELF deterministically.

Try it right here

The embed below is the real lab. The lab is an ESP32-WROOM-32 e-reader. The e-reader runs the Arduino-ESP32 + GxEPD2 + FreeRTOS stack. The e-reader runs from the same .elf that flashes to hardware. Press Run. The simulated Waveshare 2.9″ tri-color panel paints the page during the cold boot. The cold boot takes about two minutes. This procedure involves no board. This procedure involves no panel. This procedure involves no flashing.

Do you want a full window? Open the e-reader lab in the Playground →

From the lab, the work is like web development. Change the sketch. Run the firmware. Look at the rendered page. A full cycle takes about a second. A flash-and-refresh round trip on the bench takes a minute or more.

Simulator operation

A note on scope. This demo boots a full framework stack in the simulator. The stack includes Arduino, FreeRTOS, and the GxEPD2 driver. This capability is newer than a run of the small bare-metal test programs. This capability is more demanding than that run. Those programs come from the PlatformIO unit tests guide. The demo does not work on every board yet. We enable boards one at a time. The supported boards page lists the boards that can boot framework firmware today.

See an e-reader render in the Playground →

If you build an embedded UI and you want this loop for your panel, write to contact@labwired.com. Or book 30 minutes.

Andrii Shylenko
Andrii Shylenko

Founder, LabWired.