Add a thin dual-SSD1683 driver (src/epd.rs, ported from GxEPD2's GxEPD2_579_GDEY0579T93) for the 792x272 e-paper panel: reset/init, RAM addressing, the split-and-mirror full-frame blit across the master/slave seam at x=396, and full-refresh waveform. Frame implements embedded-graphics' DrawTarget. Replace the Spike 1 blink harness in main.rs with the Spike 2 test: SPI at 4 MHz over SCK 12 / DIN 11 / CS 7 / DC 6 / RST 5 / BUSY 4, alternating normal/inverted frames with a seam-straddling circle, "Typoena", and the build tag. Verified on hardware 2026-07-04.
Typoena firmware
Rust crate targeting xtensa-esp32s3-espidf. See the project root
README.md and
docs/v0.1-mvp-technical.md for the wider
context.
Current state
Spike 1 — Blink. Toggles GPIO 2 every 500 ms and logs blink N to the
USB-serial console. This proves three things only:
- The Espressif Rust toolchain (Xtensa) is installed and on PATH.
- The crate links against
esp-idf-svcand compiles forxtensa-esp32s3-espidf. - Basic GPIO output works on real silicon — verified 2026-07-04 on an
ESP32-S3 (rev v0.2, 16 MB flash):
blink Nstreams at 1 Hz over USB-serial (115200) and GPIO 2 + the on-board WS2812 toggle in lockstep.
Everything past that — EPD, SD, USB host, partial refresh, Wi-Fi/TLS,
gitoxide push — is its own follow-up spike per
docs/v0.1-mvp-technical.md.
Quick commands
A justfile wraps the common commands and
sources the espup env itself — run just in this directory for the list
(build, flash, monitor, info, ports).
Build
Once per shell session, source the espup env (sets LIBCLANG_PATH and adds
the Xtensa GCC to PATH):
. ~/export-esp.sh
Then from this directory:
cargo build --release
The first build is slow (the esp-idf C sources are checked out and built
under .embuild/). Subsequent builds are incremental.
Flash (when hardware is on the bench)
cargo run --release triggers espflash flash --monitor via the runner
configured in .cargo/config.toml. With the ESP32-S3-DevKitC-1 connected
over USB you should see:
[…] blink 0
[…] blink 1
[…] blink 2
…
at 1 Hz on the serial monitor, and — if an LED is wired from GPIO 2 → 330 Ω → GND — the LED blinks in lockstep.
Pin choice
GPIO 2 is a safe general-purpose pin on the ESP32-S3-DevKitC-1: it's not tied to a strapping function at boot and not muxed to the USB or PSRAM peripherals. The blink loop also drives the on-board addressable LED — WS2812 on GPIO 48 (GPIO 38 on DevKitC-1 v1.1 boards) — via the RMT peripheral, so both a plain GPIO and the RMT path are exercised.
Board pinout
The bench board follows the ESP32-S3-DevKitC-1 v1.0 pinout — an ESP32-S3-WROOM-1 N16R8 module (16 MB flash, 8 MB octal PSRAM). The v1.0 revision wires the on-board WS2812 RGB LED to GPIO 48; v1.1 moved it to GPIO 38, so match assignments against this diagram, not the v1.1 one.
Source: Espressif ESP32-S3-DevKitC-1 v1.0 user guide. The octal PSRAM consumes GPIO 26–37, so those are unavailable for peripherals.
Editor / rust-analyzer
The repo-level .zed/settings.json configures rust-analyzer for this
crate:
cargo.targetis pinned toxtensa-esp32s3-espidfwithallTargets = false, so RA doesn't try to also check the crate for the host target (which can't buildesp-idf-sys).binary.pathis pinned to the rustup-managed rust-analyzer (stabletoolchain), not Zed's bundled one. Reason: recent Zed builds ship a rust-analyzer that callscargo metadata --lockfile-path, which is still gated behind-Z unstable-optionsin cargo 1.95 and fails on both thestableandesptoolchains. The rustup-managed RA is version-locked to the cargo it ships with and avoids the flag.
If a contributor on a different machine has issues, regenerate the path:
rustup component add rust-analyzer --toolchain stable
rustup which rust-analyzer --toolchain stable
# put the printed path into .zed/settings.json under lsp.rust-analyzer.binary.path
Two things rust-analyzer still needs from the environment Zed was launched in:
LIBCLANG_PATH— required bybindgeninsideesp-idf-sys.- The Xtensa GCC on
PATH— required byembuildduringcargo check.
Both are set by ~/export-esp.sh. The pragmatic workflow:
. ~/export-esp.sh
zed /Users/julien/jclab/typewriter # or: open from this shell
If Zed is launched from Finder/Dock instead, rust-analyzer will report
bindgen errors on the first esp-idf-sys check. Close Zed, source the
env in a terminal, and relaunch from there.
Toolchain pins
rust-toolchain.toml pins the channel to esp (installed by espup install). Cargo.toml currently includes git [patch.crates-io] overrides
for esp-idf-sys / esp-idf-hal / esp-idf-svc (template default). These
follow master and may need pinning to released versions if a master commit
breaks the build.
