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

108 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```sh
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).