Skip to content

Releases: rvben/esprite

v0.4.5: real LVGL firmware support

Choose a tag to compare

@rvben rvben released this 03 Jul 14:48
v0.4.5
a2eef0f

Highlights

A genuine LVGL app, fully drivable under the emulator

tools/qemu/lvgl_demo is a real LVGL 9 application (a two-screen device control panel) on the standard component stack - lvgl + esp_lvgl_port + the standard esp_lcd_touch interface. esprite taps flip its switch, set its slider, and BOOT changes screens, with every state byte-matched against committed goldens. It runs on the same board spec as the bundled fixture: ESPRITE_QEMU_IMAGE selects the firmware.

  • New component esp_lcd_touch_esprite: the standard touch-driver contract implemented over the input agent, so touch reaches LVGL through a normal indev driver - no esprite-specific input code in the application.
  • Guest-held taps: the agent's new tap x y ms verb holds the press guest-side; live-state touch pollers (LVGL indev drivers) can miss a host-paced press entirely.
  • Capture pumping around injections: a pending frame blocks the guest's whole UI task, input polling included, until the host consumes it; the backend now syncs the display around every tap, swipe, and gpio injection so interactions complete.
  • The README documents the full recipe, including the full-refresh + static-buffer display rule for RAM-tight chips.

The informational CI job builds and caches the LVGL image alongside the fixture and runs the whole gated suite against real emulated firmware on every push.

Binaries below carry the generic native targets plus both qemu board specs; the waveshare_amoled_18 target needs a source build against a firmware checkout (see README). esprite is alpha software; interfaces may change.

v0.4.4: shutdown, capture, and reporting hardening

Choose a tag to compare

@rvben rvben released this 03 Jul 13:52
v0.4.4
633370c

Highlights

Hardening

  • The run session now notices Ctrl-C/SIGTERM while idle: a signal landing in the gap before its input read blocks (or delivered on another thread) previously left the session waiting for the next command line; a self-pipe wake-up closes the window.
  • The QEMU child's serial capture keeps a bounded 1 MiB tail instead of growing for as long as a serve session runs; expect regexes and failure diagnostics only ever read recent output.
  • Boot failures for a missing emulator binary name the offending path on both macOS and Linux.
  • The fixture build image is pinned by digest, so an upstream re-tag cannot silently change fixture bytes and golden stability.

Docs

  • New README section: What each backend is authoritative for - a fidelity matrix across host-native, QEMU tier 1 (any flash image), and QEMU tier 2 (cooperating firmware), including per-architecture determinism (ESP32-C3 byte-exact under icount; ESP32/S3 wall-clock).

Binaries below carry the generic native targets plus both qemu board specs; the waveshare_amoled_18 target needs a source build against a firmware checkout (see README). esprite is alpha software; interfaces may change.

v0.4.3: QEMU networking

Choose a tag to compare

@rvben rvben released this 03 Jul 11:59
v0.4.3
41adfd6

Highlights

snapshot works on emulated firmware (tier 2)

The QEMU machine emulates an OpenCores ethernet; a board spec with "http": {"guest_port": N} boots with user-mode networking and a localhost port forwarded into the guest, so snapshot POSTs into the firmware's real HTTP server (lwIP over the emulated NIC, built with CONFIG_ETH_USE_OPENETH=y):

  • One code path for both backends: the CLI targets whatever HTTP port the active backend reports (native: the in-process webserver; qemu: the forward).
  • The scenario snapshot step works on qemu targets too - the bundled golden scenario now taps, presses a button, and posts data into a real emulated guest, byte-comparing all three frames.
  • serve prints the forwarded URL so live bridges can drive emulated firmware.

Flash images boot in snapshot mode

QEMU opens mtd drives for writing, so a boot could mutate your image file - and a read-only image failed to boot outright. Guest flash writes now land in a throwaway overlay: images stay pristine, and read-only images (CI cache artifacts, shared fixtures) boot fine.

Emulator suite in CI

A separate informational workflow runs the full gated emulator suite (display, input agent, networking, byte-exact goldens) on every push, with the prebuilt emulator and the docker-built fixture cached. Its runtime library list is ldd-derived and documented in tools/qemu/ci-deps.sh.

Binaries below carry the generic native targets plus both qemu board specs; the waveshare_amoled_18 target needs a source build against a firmware checkout (see README). esprite is alpha software; interfaces may change.

v0.4.2: QEMU input

Choose a tag to compare

@rvben rvben released this 03 Jul 10:36
v0.4.2
7e72842

Highlights

Input injection for emulated firmware (tier 2)

tap, swipe, gpio, and button now work on qemu-backed targets whose firmware runs esprite's tiny esprite_qemu_agent component (one task on UART1, wired by esprite to a second serial chardev):

  • The QEMU fork emulates no GPIO or touch hardware (its C3 gpio model reflects nothing into gpio_get_level), so cooperating firmware polls the agent's event-counter APIs - counters, not live state, so a fast host tap or a short pulse is never missed.
  • Board specs grew "agent": true and a "buttons" array; spec buttons render as clickable bezel nubs in --window (view-only on qemu for now) and are pressable by label.
  • button <label> works on every backend now: any board button is pressable by its silk-screen name (e.g. button BOOT on the CYD).
  • New error kind agent_failed (exit 10) when the guest agent is absent or unresponsive.

Scenarios run on qemu targets, with the first emulator goldens

scenario boots through the backend seam and gates per step:

  • New settle {ms} step: the portable time verb (native pumps the loop, qemu pumps the child).
  • New pixel {x,y,value,timeout_ms} step: a framebuffer assertion with a retry deadline - deterministic against wall-clock guests, exact on flat-color fixtures.
  • scenarios/qemu_esp32c3_rgb.json taps and presses a real emulated guest and its screenshots byte-match committed goldens (make qemu-goldens regenerates).
  • steps and expect stay native-only and say so; serial matches against the backend's capture on both.

Binaries below carry the generic native targets plus both qemu board specs; the waveshare_amoled_18 target needs a source build against a firmware checkout (see README). esprite is alpha software; interfaces may change.

v0.4.1: QEMU display

Choose a tag to compare

@rvben rvben released this 03 Jul 09:26
v0.4.1
590732e

Highlights

The QEMU backend gained a display (tier 2)

A qemu-backed target whose firmware is built against Espressif's esp_lcd_qemu_rgb component now renders into esprite's framebuffer via QMP screendump:

  • New target qemu_esp32c3_rgb (320x240 virtual RGB panel). screenshot, serve --shot, and the live --window work exactly as on native targets - the bezel window shows the real emulated guest's pixels, refreshed at 10 Hz.
  • Draw full frames in cooperating firmware: the virtual panel consumes one pending draw per host-side capture, so per-line drawing stalls headless. The bundled fixture (tools/qemu/rgb_demo) shows the pattern.
  • New error kind capture_failed (exit 9) for a screendump or decode failure after boot.

Qemu targets are data, not code

Board specs live in targets/qemu/*.json (key, machine, arch, optional display dimensions) and ship inside the binary. ESPRITE_QEMU_BOARD=/path/to/board.json registers your own qemu board at runtime, no rebuild. The serial-only qemu_esp32c3 target is unchanged in behavior, now defined by data.

More

  • Gated end-to-end display tests against a scripted esp_lcd_qemu_rgb fixture image (make qemu-fixtures); the default build and tests still never require QEMU.
  • A qemu boot now records the active target, so board-aware capability gating works identically across backends.

Binaries below carry the generic native targets (sample_gfx, cyd, cyd_tft) plus both qemu board specs; the waveshare_amoled_18 target needs a source build against a firmware checkout (see README). esprite is alpha software; interfaces may change.

v0.4.0: device-bezel window, board-named targets

Choose a tag to compare

@rvben rvben released this 03 Jul 08:13
v0.4.0
e378cc2

Highlights

Device-bezel window

serve --window now presents the device instead of a bare framebuffer:

  • The screen renders pixel-exact inside a slim device bezel. Bezel chrome draws at desktop density, so --scale N enlarges the screen while the chrome stays crisp.
  • Physical buttons appear as clickable nubs on the bezel edge, at the positions the board declares (SimButton gained edge/pos fields; existing board definitions compile unchanged). Hover a nub for its label and keyboard shortcut.
  • ? opens a key-map overlay; backtick opens a hardware-controls panel (battery level, charging/USB, rotation) on boards that declare those capabilities.
  • Input semantics hardened: opening an overlay releases every held button and in-flight drag, so no phantom input leaks into the firmware.
  • Layout math lives in an SDL-free module with unit tests; SDL2 remains optional.

Targets are named by the board, not the app

The target formerly named agentgauge is now waveshare_amoled_18 (Waveshare ESP32-S3-Touch-AMOLED-1.8, 480x480). Boards are a finite, stable set while firmwares are open-ended, so target ids follow the hardware, matching cyd/cyd_tft. The agentgauge firmware itself keeps its name. If you scripted --target agentgauge, switch to --target waveshare_amoled_18.

More

  • motion injection and serial expect for scenario tests against motion-driven firmware behavior.
  • The firmware's LVGL font sources are now globbed, so the sim build tolerates the firmware adding or dropping font sizes between checkouts.

Fixes

  • serve schema describes the bezel window accurately.
  • The board-variant integration tests skip cleanly when the firmware checkout is absent.

Binaries below carry the generic targets (sample_gfx, cyd, cyd_tft); the waveshare_amoled_18 target needs a source build against a firmware checkout (see README). esprite is alpha software; interfaces may change.

v0.3.0: QEMU emulator backend

Choose a tag to compare

@rvben rvben released this 02 Jul 17:55
v0.3.0
8ebc25a

Highlights

QEMU emulator backend (new, tier 1)

esprite can now run a real compiled flash image under Espressif's QEMU fork, alongside the host-native backend, behind the same CLI:

  • New target qemu_esp32c3: serial, logs, and headless serve against any ESP32-C3 image (ESPRITE_QEMU_IMAGE). Boots real ESP-IDF and Arduino binaries.
  • Deterministic on ESP32-C3: icount mode gives byte-for-byte identical serial output across runs. Xtensa (ESP32/S3) runs wall-clock only (icount hangs in the current fork release).
  • make qemu-fetch (pinned prebuilt emulators, no source build), make qemu-fixtures (scripted test images), make qemu-test (self-skipping; default build and tests never require QEMU).
  • list-targets reports a backend field; missing prerequisites yield the new backend_unavailable error kind; everything not yet emulator-capable degrades explicitly to unsupported.
  • Signal-safe child lifecycle: the emulator process never outlives the CLI, including Ctrl-C mid-boot.

More

  • agentgauge firmware target onboarded (Wi-Fi Claude usage-limit desk gauge).
  • wifi up/wifi down to control the simulated link state.
  • swipe command for gesture validation.
  • expect assertions in scenarios and the run session.
  • Live BLE bridge: serve --ble-port N exposes the virtual link to real host processes.

Fixes

  • LVGL screen ownership for ui snapshots; display and buffer reuse.
  • strlcpy shim only defined where libc lacks it.
  • peripherals library declares its real link dependency on core (fixes GNU ld builds).

Binaries below carry the generic targets (sample_gfx, cyd, cyd_tft); the agentgauge target needs a source build against a firmware checkout (see README). esprite is alpha software; interfaces may change.

v0.2.0

Choose a tag to compare

@rvben rvben released this 02 Jul 07:24
v0.2.0
bf8f97e

The firmware's primary data path, BLE, now runs in the simulator. Still early software; interfaces may evolve.

Added

  • BLE Hardware Buddy simulation: a virtual BLE link bound at the firmware's own ble.h boundary runs the real protocol.cpp/app_buddy.cpp compiled from source. Heartbeat snapshots, permission prompts (Approve/Deny on-screen, decisions sent back over the link), HID keyboard reports, passkey pairing with the code rendered on the device, and the 3 s hold-to-pair gesture all work end to end. New target: waveshare_amoled_216_c6_buddy.
  • ble command in all three dialects (one-shot, run session, scenario): connect [--passkey N], pair, disconnect, send, recv, hid. One-shot ble send completes the whole round trip: connects bonded, delivers the JSON line, returns the device's replies, and --shot captures the resulting frame.
  • button pwr-long / pwr-release inject the power button's hold-gesture edges; the SDL window's PWR control follows the hardware's hold semantics (short click vs 1.5 s long-press).
  • Waveshare AMOLED 2.16 (ESP32-S3) target: rotation-capable variant of the 480x480 panel, sharing the one compiled firmware. Seven targets.
  • Prebuilt binaries: release tags now attach macOS (arm64) and Linux (x86_64) tarballs with the generic targets; Clawdmeter-backed targets need a source build against a firmware checkout (CLAWDMETER_SRC).
  • make dist, make build CLAWDMETER_SRC=... passthrough, and a manual workflow that tests against upstream Clawdmeter.

Fixed

  • Firmware version skew no longer breaks configure in either direction: new firmware files warn by name, files missing from an older checkout drop out cleanly.
  • serve shuts down cleanly on Ctrl-C/SIGTERM.
  • A webserver test raced the kernel's accept queue under load.

v0.1.1

Choose a tag to compare

@rvben rvben released this 02 Jul 06:18
v0.1.1
4b7bb67

Hardening and correctness fixes across the CLI, shims, and rendering pipeline. Still early software; interfaces may evolve.

Fixed

  • Screenshots taken after injecting data (snapshot --shot, scenario steps, run-session captures) could show the previous frame: capture now settles past a full LVGL refresh period first.
  • An invalid serial expect regex crashed the process; it is now a structured bad_args error in both the one-shot CLI and the run session.
  • Unknown --options, garbage or out-of-range numbers, out-of-bounds taps, and non-JSON snapshot bodies were silently accepted; all are now rejected against the schema contract. A bare -- ends option parsing.
  • The run session desynced on lines over 16 KB, corrupted LVGL state on a second boot, and spoke an undocumented flat error vocabulary; it now replies with the same kind/message envelope as the one-shot CLI, rejects re-boots, and answers an oversized line with one error.
  • Scenario steps bypassed board-capability gating and used off-schema exit codes.
  • Shim fidelity: collectHeaders retains only declared headers (device semantics), real HTTP reason phrases, read-only Preferences reject writes, Serial.printf no longer truncates at 1 KB, webserver listeners reset on every boot, and snapshot posts can no longer land on an unrelated host process.
  • TFT_eSPI: drawing on an unbuffered sprite no longer writes to the screen, sprites compose through their parent, createSprite rejects non-positive dimensions, and rotations 2/3 mirror their 0/1 counterparts.

Added

  • --version (any argument position), version single-sourced from CMake into the schema.
  • make release and make install (optimized build, cmake --install).
  • CI on ubuntu and macos running the same make targets used locally.
  • Configure-time detection of new firmware source files (clear warning instead of a linker error).

Changed

  • Run-session and scenario error replies now use the documented {"error":{"kind":...,"message":...}} envelope; scenario failures exit with the first failing step's schema exit code.

esprite v0.1.0

Choose a tag to compare

@rvben rvben released this 01 Jul 22:28
v0.1.0
32ae801

First tagged release of esprite, a host-native ESP32 firmware simulator.

  • Compiles firmware from source and renders the display; agent-facing CLI (screenshots, input injection, scenarios, JSON, clispec).
  • Runtime board selection: one firmware, many boards. Targets: Waveshare AMOLED 2.16 C6 (480x480), Waveshare AMOLED 1.8 (368x448), sample_gfx, and the CYD (ESP32-2432S028R) via Arduino_GFX and TFT_eSPI.
  • Supported display libraries: LVGL, Arduino_GFX, TFT_eSPI.

Also reserves the esprite name on crates.io and PyPI.