No description
  • C 67.6%
  • Python 25%
  • Nix 3.9%
  • CMake 2.6%
  • Shell 0.9%
Find a file
Max Fritz f591558b6a
All checks were successful
build / native build + lint (push) Successful in 21s
build / cross-build (aarch64 musl-static) (push) Successful in 39s
build / cross-build (x86_64-v3 musl-static) (push) Successful in 29s
build / cross-build (armv6l musl-static) (push) Successful in 11m49s
set wait_ready=1 to reduce display artefacts
2026-06-01 11:58:54 +02:00
.forgejo/workflows feat: replace python wheel release artifact with python3-epaper deb 2026-05-30 17:05:04 +02:00
cmake make Nix the only build path; switch packaging plan to nfpm 2026-05-19 21:35:20 +02:00
docs feat: add it8951_1bpp config option 2026-05-30 17:08:10 +02:00
packaging feat: add it8951_1bpp config option 2026-05-30 17:08:10 +02:00
prompts prompts: 14-prebuffer-mode.md — pre-uploaded SDRAM frames 2026-05-28 08:33:38 +02:00
python python/examples: scale up clock, GoL upscaling, add show_panels 2026-05-26 10:32:58 +02:00
scripts harden inter-panel sync (phase 8) 2026-05-21 15:29:32 +02:00
src set wait_ready=1 to reduce display artefacts 2026-06-01 11:58:54 +02:00
tests feat: add it8951_1bpp config option 2026-05-30 17:08:10 +02:00
third_party add the virtual display backend 2026-05-20 02:21:35 +02:00
.clang-format adds glue logic for multi controller displays 2024-06-20 01:41:55 +02:00
.gitignore add cross-compile toolchain and CI scaffolding 2026-05-19 19:12:55 +02:00
CHANGELOG.md release: tag-triggered CI workflow, versioning, smoke-test docs 2026-05-28 20:40:35 +02:00
CLAUDE.md make Nix the only build path; switch packaging plan to nfpm 2026-05-19 21:35:20 +02:00
CMakeLists.txt fwtest: add single-panel hwtest with LD_IMG_AREA bpp=1 path 2026-05-26 10:31:45 +02:00
flake.lock add cross-compile toolchain and CI scaffolding 2026-05-19 19:12:55 +02:00
flake.nix feat: replace python wheel release artifact with python3-epaper deb 2026-05-30 17:05:04 +02:00
LICENSE Added GPL 2020-04-26 15:37:12 +02:00
README.md docs: update README for python deb, examples, and config 2026-05-30 17:08:21 +02:00

epaperd

epaperd is a daemon that drives a multi-panel e-paper display over USB. Four IT8951 controllers and a Good Display panel hang off a Raspberry Pi Zero; producers (clock, status display, Game of Life, …) connect over a Unix socket and the daemon composites their surfaces into one framebuffer, pushing only the changed regions to the hardware.

This repository also keeps it8951, the original single-shot CLI for talking to one IT8951 controller. It stays around as a hardware debugging utility.

The daemon is being built up prompt-by-prompt — see prompts/. Today's deliverable is the build/deploy/CI scaffolding; the daemon itself is a stub.

Installing

On Debian / Raspberry Pi OS, grab the two .deb files from the release page and install them. The daemon binary is statically linked (musl), so epaperd pulls in no runtime dependencies. The Python package depends on python3 and python3-numpy, which apt resolves automatically.

$ sudo dpkg -i epaperd_<ver>_arm64.deb       # arm64 / aarch64 Pi
$ sudo dpkg -i python3-epaper_<ver>_all.deb  # same file for every arch
$ sudo usermod -aG epaper "$USER"            # log out + back in to take effect

The daemon starts automatically on install and on every boot — no manual systemctl enable needed.

On NixOS, add the flake and set services.epaperd.enable = true;.

Configuration lives in /etc/epaperd.conf (created with defaults on first install, never overwritten by upgrades). The shipped default has every key documented inline — open it to see what's tunable. Full reference is in docs/config.md.

Full install instructions — package matrix, NixOS module options, building the .debs yourself — are in docs/install.md.

Running a producer

Example producers live in python/examples/ inside the source tarball (also on the release page). Unpack the tarball, then run any example directly — they connect to the daemon's socket and exit cleanly on Ctrl-C:

$ tar xf epaperd-<ver>.tar.gz
$ cd epaperd-<ver>/python/examples

# clock overlay — updates once per second, transparent background
$ python3 clock_overlay.py

# Conway's Game of Life — fills the whole display
$ python3 gol.py

# custom socket path if needed
$ python3 clock_overlay.py --socket /run/epaperd/socket

The --socket option defaults to /run/epaperd/socket, which is where the systemd unit listens. Multiple producers can run simultaneously; the daemon composites their surfaces by z-order.

Building

The dev loop is edit on your laptop, cross-compile, ship to the Pi. Native builds also work for local testing.

$ nix build .#default
$ ./result/bin/it8951 -v

Or drop into a dev shell and use cmake directly:

$ nix develop
$ cmake -S . -B build
$ cmake --build build
$ ./build/src/it8951/it8951 -v

Cross-build (Nix)

Three musl-static targets — each output is a single self-contained ELF with libc, libusb, and libudev baked in. No runtime deps on the Pi.

Target For
cross-armv6l Pi Zero (original), Pi 1
cross-aarch64 Pi Zero 2W, Pi 3 / 4 / 5
cross-x86_64-v3 Modern x86_64 (Haswell or later)
$ nix build .#cross-aarch64
$ file result/bin/it8951
# ELF 64-bit LSB executable, ARM aarch64, ... statically linked, ...

Deploying to a host

$ EPAPERD_PI=epaper.local scripts/deploy.sh
# or
$ scripts/deploy.sh epaper.local

The script probes the host's uname -m, picks the matching cross target, cross-builds, scps it8951 to /tmp/, and runs it8951 -v to confirm the binary loads.

Running it8951

Clear the display
$ sudo it8951 -c -m 0 /dev/sdb 0 0 800 600

Send an 8-bit grayscale image
$ sudo it8951 /dev/sdb 0 0 800 600 < image.raw

Generate an image and display it
$ convert -background white -fill black \
    -font Ubuntu -pointsize 50 label:"$(date)" \
    -gravity Center -extent 800x600 \
    -depth 8 gray:- \
  | sudo it8951 -d -m 2 /dev/sdb 0 0 800 600

Draw an image in A2 (fast 1-bit) mode
$ sudo it8951 -m 4 /dev/sdb 0 0 800 600 < image.raw

See docs/development.md for the full dev workflow.