CircuitPython graphics + touch for the M5Stack M5Paper (IT8951 e-paper + GT911).
Author: Freyr86
Repository: https://github.com/Freyr86/m5paper_epd
License: MIT
CircuitPython supports m5stack_m5paper but not board.DISPLAY. The IT8951 SPI protocol does not fit stock epaperdisplay / fourwire — see circuitpython#9839.
This library provides working display + touch today, in clear layers (drivers stay separate; UI gestures are assembled on top).
Driver e-paper IT8951 + tactile GT911 pour M5Paper. Couches séparées (EPD, Touch), puis gestionnaire Screen pour clic / maintien / glisser / swipe. Pas de displayio natif.
┌─────────────────────────────────────────┐
│ Screen (assemble + callbacks) │
│ GestureTracker (click/hold/drag/swipe)│
├──────────────────┬──────────────────────┤
│ EPD / IT8951 │ Touch (GT911) │
│ SPI display │ I2C points only │
└──────────────────┴──────────────────────┘
| Module | Role |
|---|---|
it8951.py |
Low-level SPI controller |
display.py |
Framebuffer graphics (EPD) |
touch.py |
Raw GT911 points — no display import |
gestures.py |
Gesture state machine — no hardware |
screen.py |
Wires EPD + Touch + gestures |
- M5Paper (ESP32) — CircuitPython 10.0.3
- Panel 960×540 / 540×960 — modes
INIT,DU,GC16,A2 - Touch GT911 on internal I2C (
0x14/0x5D)
CIRCUITPY/
m5paper_epd/ # whole package
code.py
python tools/deploy_now.pyOptional: MPY_PORT=COM5.
from m5paper_epd import EPD, MODE_GC16, MODE_DU
epd = EPD(orientation="landscape", invert=False)
epd.init_clear()
epd.fill(15)
epd.text("Hello", 20, 40, 0, scale=3)
epd.show(mode=MODE_GC16, partial=False)Colors: 0 = black … 15 = white.
from m5paper_epd import Touch
touch = Touch(orientation="landscape")
pts = touch.touches() # [(x, y, size, id), ...]
p = touch.touch() # (x, y) or Nonefrom m5paper_epd import Screen, MODE_DU
screen = Screen(orientation="landscape")
@screen.on_click
def on_click(e):
screen.epd.fill_circle(e.x, e.y, 8, 0)
screen.epd.show(mode=MODE_DU, partial=True)
@screen.on_hold
def on_hold(e):
print("hold", e.x, e.y)
@screen.on_drag
def on_drag(e):
screen.epd.line(e.x - e.dx, e.y - e.dy, e.x, e.y, 0)
screen.epd.show(mode=MODE_DU, partial=True)
@screen.on_swipe
def on_swipe(e):
print("swipe", e.direction) # left|right|up|down
screen.run()Or assemble yourself:
from m5paper_epd import EPD, Touch, GestureTracker, Screen
epd = EPD(orientation="portrait")
touch = Touch(orientation="portrait")
screen = Screen(epd=epd, touch=touch)| Event | When |
|---|---|
press / release |
Finger down / up |
click |
Short tap |
hold |
Finger stays still ~650 ms |
drag_start / drag_move / drag_end |
Move past slop |
swipe |
Fast drag with direction |
Tune via GestureTracker(tap_max_ms=…, hold_ms=…, swipe_min_dist=…).
| Item | Description |
|---|---|
EPD |
Display framebuffer |
Touch |
Raw GT911 |
GestureTracker / Event |
Gesture engine |
Screen |
Combined interactive surface |
screen.on(name, cb) |
Subscribe ("click", "hold", "*", …) |
| File | Role |
|---|---|
code.py |
Screen gestures demo |
examples/screen_gestures.py |
Same |
examples/touch_raw.py |
Touch only (serial print) |
examples/graphics_demo.py |
Display primitives |
examples/hello_epd.py |
Minimal display |
| Signal | board.* |
|---|---|
| SPI | SCK / MOSI / MISO |
| EPD | IT8951_CS / IT8951_BUSY / IT8951_POWER |
| Power | POWER_MAIN, POWER_EXT |
| Touch I2C | SDA / SCL / I2C |
| Touch INT | TOUCH_INT |
MIT — see LICENSE.