Overview
E-paper is the display technology that keeps showing your content with the power off. This 2.9" module packs 296x128 black-and-white pixels of it: microcapsules of charged pigment that the controller physically rearranges, then leaves in place — zero current to hold an image, readable in direct sunlight, with the soft paper-like look that makes badges, labels, and dashboards feel finished rather than blinking.
The interface is standard 4-line SPI plus three control signals: CS, CLK (SCK), DIN (MOSI), DC (data/command), RST (reset), and BUSY (the display's "still refreshing, hold on" flag). The BS jumper on the back selects 3-line vs 4-line SPI — leave it at 0 (4-line), which is what every mainstream library expects. The board runs from 3.3V or 5V and level-shifts appropriately, so it wires directly to everything from an Uno to a Pico.
The mental model that makes e-paper pleasant: it's a poster, not a video screen. Full refreshes take ~2 seconds with the characteristic black/white flash; partial refresh (supported on this panel) updates a region in a fraction of that for clocks and counters. Perfect fits: weather stations and calendar dashboards updated every few minutes, shelf labels and name badges, battery devices that sleep for hours between updates — anywhere "always readable, almost never powered" wins.
At a Glance
Specifications
| Parameter | Value |
| Panel | 2.9 inch e-ink, 296 x 128 pixels, black/white |
| Viewing | Reflective — no backlight, sunlight readable, ~180° angle |
| Interface | SPI up to 4 MHz + DC, RST, BUSY handshake |
| BS Jumper | 0 = 4-line SPI (default, use this), 1 = 3-line SPI |
| Supply Voltage | 3.3 - 5V (onboard regulation and level shifting) |
| Refresh Current | ~8 mA during refresh; ~0 idle (deep sleep ~1 uA) |
| Full Refresh | ~2 s with black/white flashing (normal and required periodically) |
| Partial Refresh | Supported — fast region updates without full flash |
| Operating Conditions | 0 - 50°C, 35 - 65% RH (indoor-class panel) |
| Pins | BUSY, RST, DC, CS, CLK, DIN, GND, VCC |
| Libraries | GxEPD2 (Arduino/ESP32), Waveshare epd Python (Pi), Waveshare Pico examples |
Pinout Diagram
The rear view shows everything you touch: the 8-pin cable connector (BUSY, RST, DC, CS, CLK, DIN, GND, VCC), the BS interface-select jumper with its table (keep it on 0), and the FPC that runs to the glass. The module ships with a color-coded 8-wire cable matching these labels.
Wiring Guide
Arduino Wiring
| e-Paper Pin | Arduino Pin |
|---|---|
| VCC / GND | 5V / GND |
| DIN | D11 (MOSI) |
| CLK | D13 (SCK) |
| CS | D10 |
| DC | D9 |
| RST | D8 |
| BUSY | D7 |
ESP32 Wiring
| e-Paper Pin | ESP32 Pin | Details |
|---|---|---|
| VCC / GND | 3V3 / GND | |
| DIN | GPIO 23 | VSPI MOSI |
| CLK | GPIO 18 | VSPI SCK |
| CS | GPIO 5 | |
| DC | GPIO 17 | |
| RST | GPIO 16 | |
| BUSY | GPIO 4 | Input |
Raspberry Pi Wiring (standard Waveshare mapping)
| e-Paper Pin | Pi Pin | Details |
|---|---|---|
| VCC / GND | Pin 1 (3.3V) / Pin 6 | |
| DIN | Pin 19 (GPIO 10, MOSI) | |
| CLK | Pin 23 (GPIO 11, SCLK) | |
| CS | Pin 24 (GPIO 8, CE0) | |
| DC | Pin 22 (GPIO 25) | |
| RST | Pin 11 (GPIO 17) | |
| BUSY | Pin 18 (GPIO 24) |
Raspberry Pi Pico Wiring
| e-Paper Pin | Pico Pin | Details |
|---|---|---|
| VCC / GND | 3V3(OUT) / GND | |
| DIN | GP11 (SPI1 TX) | |
| CLK | GP10 (SPI1 SCK) | |
| CS | GP9 | |
| DC | GP8 | |
| RST | GP12 | |
| BUSY | GP13 | Input |
Code Examples
Arduino/ESP32 use GxEPD2 (install "GxEPD2" plus "Adafruit GFX"). The Pi uses Waveshare's epd library from their e-Paper repo. The Pico sketch shows the MicroPython pattern with Waveshare's Pico driver file.
Arduino
// 2.9" e-Paper 296x128 - Arduino Example (GxEPD2)
// CS->10, DC->9, RST->8, BUSY->7, DIN->11, CLK->13
// Libraries: GxEPD2 + Adafruit GFX
#include <GxEPD2_BW.h>
#include <Fonts/FreeSansBold12pt7b.h>
// V2 2.9" panel; if your screen stays blank try GxEPD2_290 instead
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT>
display(GxEPD2_290_T94_V2(10, 9, 8, 7)); // CS, DC, RST, BUSY
void setup() {
display.init(115200);
display.setRotation(1); // landscape 296x128
display.setFont(&FreeSansBold12pt7b);
display.setTextColor(GxEPD_BLACK);
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.setCursor(10, 40);
display.print("ShillehTek");
display.setCursor(10, 75);
display.print("2.9\" e-Paper");
display.drawRect(10, 95, 276, 20, GxEPD_BLACK);
display.fillRect(10, 95, 180, 20, GxEPD_BLACK); // fake gauge
} while (display.nextPage());
display.hibernate(); // ~0 power, image stays
}
void loop() {}
ESP32 (Arduino IDE, partial refresh clock)
// 2.9" e-Paper - ESP32 partial-refresh counter (GxEPD2)
// CS->5, DC->17, RST->16, BUSY->4, DIN->23, CLK->18
#include <GxEPD2_BW.h>
#include <Fonts/FreeMonoBold18pt7b.h>
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT>
display(GxEPD2_290_T94_V2(5, 17, 16, 4));
int counter = 0;
void setup() {
display.init(115200);
display.setRotation(1);
display.setFont(&FreeMonoBold18pt7b);
display.setTextColor(GxEPD_BLACK);
display.setFullWindow(); // one full refresh at boot
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.setCursor(20, 50);
display.print("Counter:");
} while (display.nextPage());
}
void loop() {
// fast partial update of just the number area
display.setPartialWindow(20, 70, 200, 40);
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.setCursor(20, 100);
display.print(counter);
} while (display.nextPage());
counter++;
delay(3000);
if (counter % 20 == 0) display.refresh(); // periodic full clean
}
Raspberry Pi (Python, Waveshare library)
#!/usr/bin/env python3
# 2.9" e-Paper - Raspberry Pi Example (Waveshare epd library)
# Setup once:
# git clone https://github.com/waveshareteam/e-Paper
# cd e-Paper/RaspberryPi_JetsonNano/python && sudo python3 setup.py install
# pip3 install pillow
from waveshare_epd import epd2in9_V2
from PIL import Image, ImageDraw, ImageFont
import time
epd = epd2in9_V2.EPD()
epd.init()
epd.Clear(0xFF)
font_big = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 28)
font_small = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 16)
# landscape canvas: width=296, height=128
image = Image.new("1", (epd.height, epd.width), 255)
draw = ImageDraw.Draw(image)
draw.text((10, 8), "ShillehTek", font=font_big, fill=0)
draw.text((10, 48), "2.9 inch e-Paper on Raspberry Pi", font=font_small, fill=0)
draw.text((10, 72), time.strftime("%Y-%m-%d %H:%M"), font=font_small, fill=0)
draw.rectangle((10, 100, 286, 118), outline=0)
draw.rectangle((10, 100, 190, 118), fill=0)
epd.display(epd.getbuffer(image))
epd.sleep() # deep sleep - image remains
print("Displayed and sleeping.")
Raspberry Pi Pico (MicroPython)
# 2.9" e-Paper - Pico MicroPython Example
# Driver: copy Pico_ePaper-2.9.py from Waveshare's Pico_ePaper_Code repo
# to the Pico as epaper29.py (it contains class EPD_2in9)
# Wiring here matches the tab: CS=9, DC=8, RST=12, BUSY=13, SCK=10, MOSI=11
from epaper29 import EPD_2in9
import time
epd = EPD_2in9() # driver sets up SPI1 + pins
epd.Clear(0xff)
# The driver exposes a framebuf: draw with standard framebuf calls
epd.fill(0xff)
epd.text("ShillehTek", 8, 8, 0x00)
epd.text("e-Paper on Pico", 8, 24, 0x00)
epd.text("No power needed", 8, 40, 0x00)
epd.text("to keep this text!", 8, 56, 0x00)
epd.rect(8, 76, 120, 12, 0x00)
epd.fill_rect(8, 76, 80, 12, 0x00)
epd.display(epd.buffer)
time.sleep(2)
epd.sleep() # image persists with zero power
print("Displayed and sleeping.")