Vendored the panel's init/LUT/refresh register sequence from three
independent Waveshare reference drivers for this exact panel+controller
(RaspberryPi/c, ESP32, and the ESP32-S3-ePaper-13.3E6 ESP-IDF example),
which all agree byte-for-byte. The epd13in3e.c #error is gone; it
compiles clean and links (verified via /build-firmware ee02).
That vendor code also revealed the panel's SPI wire raster is a native
1200x1600 (portrait), not 1600x1200 as previously assumed -- rotated 90
degrees from the panel's landscape mount/marketing size. The old
assumption wasn't just a rotation bug: 1600x1200 and 1200x1600 don't
share a row stride, so packing at the wrong one would have shredded
images into a repeating diagonal garble on real hardware, not just
displayed them sideways. Fixed with a new PANEL_WIRE_TRANSPOSE in
image_pipeline.py, applied after the existing per-frame
ORIENTATION_TRANSPOSE, with a direction-agnostic regression test that
catches the stride bug specifically (a byte-count check alone can't,
since both orientations pack to the same total size).
A full ee02 build still fails, but no longer because of this driver --
main/{back,next,combo}_button.c call an ESP32-C6-only deep-sleep
GPIO-wakeup API with no ESP32-S3 fallback, a separate pre-existing gap
that was simply hidden behind the panel driver's old #error. See
docs/hardware.md for details; CI's continue-on-error on this board
stays in place until that's fixed too.
ESPresso Frame
A DIY e-ink photo frame: an ESP32 board pulls photos from your
Immich library and displays them on a full-color
e-paper panel, waking on a timer to refresh and spending the rest of its
time in deep sleep. The original build is a 7.3" panel on an ESP32-C6;
a larger 13.3" panel on Seeed's EE02 (ESP32-S3) is supported
server-side, but its firmware driver isn't working yet -- see
docs/hardware.md.
- No cables to a computer, no SD card shuffling. Provisioning is a captive portal with a QR code drawn on the panel itself -- scan, join, fill in your WiFi and server address, done.
- The frame never decodes an image. A small self-hosted server does all the work (pulling from Immich, cropping, dithering, packing into the panel's exact pixel format) and hands the device a stream it can write straight to SPI. The ESP32-C6 has no PSRAM and not much SRAM to spare -- keeping it a dumb display client is what makes that workable (the same design carries over to the ESP32-S3 board even though it does have PSRAM, for consistency).
- Crops toward faces, not just the center, using face bounding boxes Immich already computed for its own People feature -- no bundled face detector.
- Refresh interval and album are configurable from a web UI, no reflashing needed to change them.
Hardware
- ESP32-C6 dev board (8MB flash), or Seeed's XIAO ESP32-C6 (production board) -- both drive the panel below.
- Waveshare 7.3" E Ink Spectra 6 (E6) panel -- 800x480, 6-color, SPI.
- Experimental, not yet working: Waveshare 13.3" E Ink Spectra 6 (1600x1200) on Seeed's EE02 (ESP32-S3) -- server-side support exists, but the firmware driver's panel init sequence isn't ported from vendor code yet.
See docs/hardware.md for wiring,
docs/architecture.md for how the two halves talk
to each other, and docs/widgets.md for the server's
placeable photos/calendar/whiteboard widget system.
Getting started
server/-- run the FastAPI server first (Docker Compose, points at your Immich instance). Seeserver/README.md.firmware/-- build and flash the board, then scan the QR codes it draws on first boot to provision it. Seefirmware/README.md.
Repo layout
firmware/ ESP-IDF project (ESP32-C6 devkit/xiao boards, ESP32-S3 ee02)
server/ FastAPI server: Immich -> crop/dither/pack -> the frame
docs/ Wiring and architecture notes
License
MIT -- see LICENSE. A few small pieces of vendored
third-party code (a QR code generator, a bitmap font table) keep their
own permissive licenses; see LICENSE for details.
Built with substantial assistance from Claude Code.