- Every doc (README, AGENT, docs/*) now ships as English under the default filename and Chinese as *.zh-cn.md; both versions cross-link in their first line, in-document links stay within the same language - AGENT.md §7 records the keep-both-languages-in-sync rule (same commit, no translating later); CHANGELOG kept in both languages - Docs-only change; version 0.5.1 synced in the usual three places
5.4 KiB
English | 简体中文
esp-emu-peris
Simulated peripherals for the browser (wasm) build of Espressif's
esp-emulator: LEDs, push buttons,
touch buttons, toggle switches… — added as plugins, without touching any stock
file in the official www/.
esp-emu-peris.html entry page (UART0 console | peripherals rows | Debug — three panes at once)
src/ TypeScript sources (npm run build → build/)
core/ worker-side framework: version/types/protocol/chips/mmio/sigbus/registry/runtime/worker
ui/ page-side modules: app/worker-client/terminal/chart/debug-panels/peris-panel/config-editor
build/ compiler output (tsc, not in git; core/ + ui/)
peris/
devices/ peripheral plugins (plain JS, drop-in, each with its own version): led/button/touch-button/toggle-switch
vendor/ peris.css vendored xterm + panel styles
examples/ wiring-config examples (JSON, directly importable)
tests/unit/ unit tests (node:test)
tests/m1_smoke.mjs integration smoke test (real wasm + firmware, full chain)
tests/m0_probe.mjs emulator capability probe (run first on upstream upgrades)
tests/firmware/ ESP-IDF test firmware
docs/ ARCHITECTURE / EMU-API / FEASIBILITY / DEVICE-API / CHANGELOG / UPGRADE / ROADMAP
AGENT.md environment/architecture/gotchas (read before contributing)
Architecture, data flow, state ownership and extension points: docs/ARCHITECTURE.md; versioning rules: docs/CHANGELOG.md (project version synced in three places; devices carry their own versions).
Quick start
# one-time: npm deps (typescript installs from the npmmirror registry)
npm install
# dev loop: edit src/ → build → test → merged dev server
npm run build
npm test
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
# open the URL above in a browser, pick a chip, drop a merged flash image (.bin)
# NOTE: it must be a full image produced by idf.py merge-bin — a bare app bin
# will not boot
# peripherals pane (right of the console): the + icon adds LEDs/buttons/etc.
# Each device is one row — the row icon is interactive (LED click changes
# colour / button press), name, GPIO box and V/G pull badge edit on click,
# ▸ expands details & advanced config, ⠿ drags to reorder. Panel head:
# project-name input + save/open icons (export/import the wiring config JSON,
# autosaved to localStorage and remembering the chip, auto-selected next time)
Publish the peripherals panel into the official www directory (add-only, never modifies; build first):
npm run sync # copies the entry html + peris/ (incl. build output) + examples/
Capabilities & limits (measured; details in docs/FEASIBILITY.md)
| Peripheral | Status | Approach |
|---|---|---|
| LEDs, any colour | ✅ M1 | polls the GPIO output registers |
| Push button / touch button (GPIO semantics) / toggle switch | ✅ M1 | drives pin input; firmware reads it and gets interrupts |
| PWM dimmed LED / RGB | 🔜 M2 | reads LEDC duty registers (pin-level PWM is invisible) |
| WS2812 addressable LEDs | 🔜 M3 | decodes from RMT channel RAM (pin-level waveform invisible) |
| Bit-banged I2C/SPI devices, IR | 🔜 M4 | high-rate pin sampling (requires bit-banged firmware) |
| Hardware I2C/SPI devices, displays, ADC, real touch, I2S, SD | ⏸ M5 | waiting for esp-emulator upstream APIs |
Note: the touch peripheral itself is not emulated — the touch button
appears as a GPIO input that firmware reads as plain GPIO
(tests/firmware/peris_probe has an example: GPIO3 input + falling-edge ISR).
Adding a new peripheral
- Drop a module into
peris/devices/exportingDeviceandView(interface: docs/DEVICE-API.md); - Add one line to
peris/devices/manifest.json. Done — page and worker pick it up automatically.
// peris/devices/my-dev.js
export class Device {
static typeId = 'my-dev'; static title = 'My Device'; static category = 'misc';
static defaultConfig = { name: 'My', pin: 6 };
static configSchema = [{ key: 'pin', label: 'GPIO pin', type: 'pin' }];
static pins(cfg) { return [cfg.pin]; }
constructor(ctx, cfg) { this.ctx = ctx; ctx.subscribe([cfg.pin]); }
onPinState(pin, s) { this.on = s.oe && s.out; this.ctx.markDirty(); }
getState() { return { on: this.on }; }
destroy() { this.ctx.unsubscribe(); }
}
export class View {
constructor(el, sendCmd) { el.textContent = 'hello'; }
render(state) { }
}
Test firmware & tests
# build the firmware (IDF env from C:\Users\A\Desktop\quick\idfm.bat)
powershell -ExecutionPolicy Bypass -NoProfile -File tests\firmware\build.ps1
# merged image for loading:
# tests/firmware/peris_probe/build/merged-binary.bin
npm test # unit + integration smoke (test layering in docs/ARCHITECTURE.md)
npm run test:m0 # emulator capability probe (run first on upstream upgrades)
The firmware runs a three-phase loop: GPIO8 blink / GPIO9 LEDC breathing / GPIO4 RMT-WS2812 frames, plus GPIO3 input-interrupt counting, for end-to-end verification.
Note: the page itself has no CDN dependency — xterm/xterm-addon-fit are
vendored into peris/vendor/ (unminified npm builds), so it works offline.