Files
xteink-web-emulator/ROADMAP.md
T
2026-07-19 19:23:51 -04:00

5.8 KiB
Raw Blame History

xteink-web-emulator — architecture & roadmap

Goal: an offline, static-asset web page where you upload an xteink X3/X4 firmware .bin and it runs client-side — real ESP32-C3 machine code, with the e-ink screen on a canvas, on-screen buttons, and a virtual SD card. No server, no proprietary engine.

Why QEMU (and why not the alternatives)

Approach Runs any .bin Open source Offline in browser
Wokwi (closed engine, licensed)
Recompile firmware → WASM (crosspoint-simulator + Emscripten) source only
QEMU (espressif fork) → WASM

Only QEMU satisfies all three. espressif/qemu models the ESP32-C3 SoC and executes real firmware; ktock/qemu-wasm shows QEMU compiling to WASM and running in-browser from static files. The endgame merges those two.

Status

Phase 1 (native peripheral models) is done for the X3. The unmodified canonical firmware (crosspoint-reader/crosspoint-reader, X3-selected) boots to the home screen, mounts a FAT32 SD image, lists its contents, and responds to button presses — all under native espressif-qemu with our patch. Reproduce:

make qemu                 # clone espressif/qemu @ pinned tag, apply patch, build
make firmware sdimage     # 16 MB flash.bin + FAT32 sd.img from a pio build
make run                  # boot the xteink-x3 machine (add `-display none` for headless)

Screenshots (via QMP screendump) confirmed: boot → home → Browse Files.

Device selection (X3 vs X4)

X3 and X4 share one ESP32-C3 binary and pick a profile at runtime via an I²C fingerprint (BQ27220 gauge + DS3231 RTC + QMI8658 IMU on SDA20/SCL0), with an NVS override (cphw/dev_ovr: 1=X4, 2=X3). The emulator picks X3 vs X4 at launch by starting the matching machine (xteink-x3, later xteink-x4); the X3 machine will expose the X3 fingerprint so stock dual firmware auto-detects. No recompiled/custom firmware — the current test build forces X3 only because the fingerprint I²C devices aren't modelled yet.

The interfaces (native now → browser later)

Each peripheral is modelled behind a QEMU-native seam that the WASM front-end reuses unchanged — no bespoke abstraction layer:

Peripheral Native seam Browser binding (phase 3)
e-ink panel xteink-x3-eink SSI device → QEMU graphical console (528×792, physical orientation) <canvas> from the same display surface
SD card ssi-sd + sd-card-spi backed by QEMU block layer (-drive if=sd) browser-supplied blockdev (File/OPFS)
Buttons + battery esp32c3.adc QOM props adci[1]/adci[2]; power button = GPIO3 input DOM buttons → qom-set equivalent
Firmware image flash via -drive if=mtd uploaded .bin written to emulated flash

Phase 1 device models (in qemu/patches/0001-xteink-x3-machine.patch)

  • esp32c3.adc — SAR ADC: oneshot completes immediately; per-channel value is settable live via QOM (adci[*]). Feeds battery and the two resistor-ladder button groups. Replaced the original always-done register shim.
  • esp32_gpio — real OUT/ENABLE/IN registers with 32 named input + output lines. Outputs read high while a pin is input-configured (matches the board's pull-ups; without this, display CS floated low during SD probing).
  • ssi.esp32c3.spi2 — the general-purpose SPI2 controller (CPU-buffer transactions on an SSI bus). DMA path deliberately unimplemented until needed.
  • xteink-x3-eink — UC8253 panel: decodes DTM1/DTM2 planes (52 272 B each), drives BUSY, renders to a QEMU console. Protocol from chip/eink-x3.chip.c.
  • ssi-sd fix — track card idle state so CMD58 reports ready after ACMD41 (the 9.2 fork hardcoded idle, which SdFat rejects).
  • xteink-x3 machine — subclass of esp32c3 wiring panel CS21/DC4/RST5/ BUSY6 and SD CS12 onto SPI2, and the SD card onto QEMU's block layer.

Remaining phases

  1. X4 machine — SSD1677 panel + X4 battery/ADC profile as a sibling machine. The X3 command trace (0x18/0x0c/0x44/0x45…) is already captured.
  2. Compile the patched fork to WASM — merge Espressif's SoC models with qemu-wasm's WASM TCG/TCI backend; build qemu-system-riscv32 with Emscripten. Output: static .wasm + .js + packaged BIOS/ROM.
  3. Browser front-end (static) — upload .bin → emulated flash; <canvas> from the display surface; DOM buttons → ADC/GPIO injection; preloaded SD image; launch-time X3/X4 selection. Fully airgapped.

Repo layout

Path Role
flake.nix espressif-qemu release pkg (nix run .#run-upstream) + full QEMU build devShell
Makefile qemu (source build), firmware, sdimage, run, chip
scripts/build-qemu.sh clone pinned espressif/qemu, apply patch, build riscv32-softmmu
qemu/patches/ the xteink X3 machine + device models (applied onto the pinned tag)
chip/eink-x3.chip.c legacy Wokwi model — the reverse-engineered X3 e-ink protocol reference
web/ (phase 3) static front-end: upload + canvas + buttons

Debugging notes

  • Headless capture: run with -qmp unix:… and use scripts/qmp-screendump.py; the console is 528×792 (physical portrait), rotate not needed.
  • Inject a button: scripts/qmp-qom.py <sock> set /machine/adc "adci[1]" 2760 (ladder-1 midpoints: Back 3512 / Confirm 2694 / Left 1493 / Right ~5; ladder-2 Up 2242 / Down ~5). Value > 3900 = no button.
  • Symbolize a panic PC: llvm-addr2line -f -C -e firmware.elf 0x4210d286.
  • Flash must be 16 MB — the partition table spans 16 MB even though the app fits under 4 MB. Espressif QEMU only accepts 2/4/8/16 MB mtd images.
  • Trace SD/panel: flip DEBUG_SSI_SD in hw/sd/ssi-sd.c or add an info_report in xteink_x3_eink.c (both off by default in the patch).