返回资讯中心
外部精选
软件工程
#社区热议

Show HN: Esp32sim – Run ESP32 firmware in the browser

Hi HN,I’ve been working on esp32sim, an open-source ESP32 emulator that runs in the browser.The fun part of the demo is that there is no ESP32 involved — the firmware is executing in the emulator.The goal is to make ESP…

Hacker Newsjoakimeriksson2026/10/17 · 10 天前30 分钟阅读

以下正文同步自 Hacker News,版权归原站所有,已转换为易读排版。

Instruction-level emulation of four ESP32 SoCs, across both of Espressif's CPU architectures. Each boots the real mask ROM, the real 2nd-stage bootloader and an unmodified application image — no patched firmware, no stubs in the way — with enough of the SoC modelled to run real projects end to end. No cloud, no accounts. MIT.

The classic ESP32 also boots its ECO3 ROM on dual LX6 cores, with GPIO, UART and timers; --board ttgo-tdisplay adds the LilyGO TTGO T-Display's ST7789 and buttons.

These full-listing and hardware counts describe earlier manual runs. CI runs the checked-in sampled decoder corpora and emulator golden outputs; it excludes the external full-listing tests. The Xtensa objdump test excludes PIE operand comparisons. Hardware trace comparison normalizes window exceptions and resynchronizes delay loops, so it is not uninterrupted cycle-by-cycle agreement. See the test scope, CI workflow, Xtensa decoder test and hardware comparator.

Most of the SoC is shared: the C3 reuses the S3's UART, USB-Serial/JTAG, systimer, timer groups, GPIO, SPI flash controller, GDMA and SHA/AES/RSA models unchanged, and adds its own memory map, cache controller and interrupt matrix; the C6 reuses the same models again, with its own memory map, PLIC front-end, L1 cache, PCR and always-on LP blocks. Only the CPU crates are genuinely separate.

Try it in a browser, no install: https://mikroverk.github.io/esp32sim/ — Linux on the S3, the Touch-LCD-4B panel with its SID player, the pocket-tank LLM aquarium, and the C3 and C6 demos.

esp32sim/ emu-core/ the Bus and Core traits, Trap, ClockTree, AArch64 encoder — shared by both cores esp-soc/ Machine<S: Soc>: scheduler, device time, console, scripts, web UI, loaders, boards — one machine for every chip; a chip plugs in its cores, bus and interrupt routing; the virtual WiFi access point, the network behind it and the NAT, for every chip esp-periph/ the peripheral IP the chips share (UART, systimer, TIMG, GPIO, SPI_MEM, GDMA, crypto, I2S, RMT, I2C, …), the Device trait, and the device_set! table that mounts them ── ESP32-S3 (Xtensa LX7, dual core) ── xtensa-lx7/ decoder (sampled objdump checks plus optional full listings), interpreter (windowed regs, loops, XEA2 exceptions/interrupts, FPU, MAC16, booleans, PIE SIMD), basic-block interpreter, AArch64 JIT, objdump-compatible disassembler esp32s3/ SoC + boards: memory map, cache MMU, SPI flash/PSRAM, SHA/AES/RSA, RNG, systimer, timer groups, interrupt matrix (per core), GPIO, USB-CDC, UARTs, I2C, GDMA + I2S/LCD_CAM, RMT TX, regi2c, RTC WDT, WiFi MAC; board/: atech14 / waveshare-cam / waveshare-lcd4b / waveshare-amoled18-v2 / none cli/ the esp32sim command line, every chip (--chip); esp32sim-c3 / esp32sim-c6 are alias binaries ── ESP32-C3 and ESP32-C6 (RISC-V, single core) ── riscv-rv32/ RV32IMAC decoder (sampled objdump checks), interpreter, disassembler esp32c3/ the C3 SoC: memory map, interrupt matrix, cache controller, RNG; peripheral models shared with esp32s3 where the IP is identical esp32c6/ the C6 SoC: unified memory map and MMU, interrupt matrix + PLIC/INTPRI, L1 cache, PCR, the LP blocks (reset cause, software reset, RTC timer), ASSIST_DEBUG, the 802.15.4 and WiFi MACs tests/ golden-output regression tests and their fixtures (tests/README.md) ── shared ── wasm/ C-ABI crate wrapping all three Machines for the browser in one module web/ browser UI (board drawing, console, audio, camera) + emu.js/worker.js for wasm hw/ JTAG differential-test scripts against a real board, captured C3 and C6 consoles examples/ hello_world (IDF, S3, C3 and C6), waveshare-cam (autopling run script + photo), wifi-station (S3), c6-wifi-station (builds for the S3, C3 and C6; the WiFi goldens' firmware), c6-radio-dashboard (WiFi + 802.15.4 on the C6 board) tools/ PIE table generator (TRM-derived); bench.py (interleaved A/B benchmark); wasm-build.sh (the WebAssembly module)

Boards (ESP32-S3)

The SoC model emits pin-level events (GPIO edges, RMT symbol streams, I2S samples); a BoardModel (esp-soc/src/board.rs) interprets them. --board atech14 (default) is the Atech 14‑port board with its ST7735, WS2812 ring, encoder and buttons; --board none is a bare module — any ESP32‑S3 firmware, console only; --board waveshare-lcd4b is the Waveshare ESP32‑S3‑Touch‑LCD‑4B (ST7701S 480×480 over the LCD_CAM RGB bus, GT911 touch, TCA9554, codecs) running the esp32-screen LVGL panel with touch; --board waveshare-amoled18-v2 is the Waveshare ESP32-S3-Touch-AMOLED-1.8 V2 (CO5300 368x448 over QSPI, CST820 touch); --board waveshare-cam is the Waveshare ESP32‑S3‑CAM‑OV5640 (CH32V003 IO expander, OV5640 over SCCB, ES8311/ES7210 codecs on I2C0, speaker on I2S1, OV5640 on the LCD_CAM DVP port) — runs the waveshare-autopling firmware (IDF 5.5, 16 MB flash, 8 MB octal PSRAM: --flash-mb 16 --psram-mb 8) end to end: camera frames from --cam-image or the browser (picture upload / webcam) → esp‑dl pedestrian detector on the emulated PIE SIMD unit → pling on the ES8311. See examples/waveshare-cam/. Adding a board = implementing the trait (gpio_changes, rmt_frame, i2c_devices, camera_frame, …).

The Pocket Synth firmware the atech14 demos run is its own project: atech-firmware — a PlatformIO build of the SID synth and a cRSID player for real .sid tunes. This repo carries the built images it produces, in web/wasm/fw/public/.

cargo build --release FW=web/wasm/fw/public # the demo images the goldens and the web page run ./target/release/esp32sim --boot rom
--bootloader $FW/atech-bootloader.bin --ptable $FW/atech-ptable.bin --app $FW/atech-firmware.bin
--script $FW/atech-script1.txt --wav out.wav --tft-png tft.png --max-seconds 5

The mask ROM ELF is picked up from ~/.espressif/tools/esp-rom-elfs/*/esp32s3_rev0_rom.elf (shipped with ESP‑IDF). Without ESP-IDF, tools/fetch-demo-assets.sh --no-linux puts the S3, C3 and C6 ROMs in web/wasm/fw/: pass --rom web/wasm/fw/esp32s3_rev0_rom.elf, or set ESP32SIM_ROM_DIR=web/wasm/fw for the tests. --boot app skips ROM+bootloader and loads the app image directly.

A plain ESP‑IDF project, e.g. examples/hello_world (the IDF 5.5 get-started example built with idf.py set-target esp32s3 && idf.py build):

H=examples/hello_world/build ./target/release/esp32sim --board none --boot rom --console uart0
--bootloader $H/bootloader/bootloader.bin --ptable $H/partition_table/partition-table.bin
--app $H/hello_world.bin --elf $H/hello_world.elf --elf $H/bootloader/bootloader.elf --max-seconds 26

prints the ROM banner, the bootloader and app logs on UART0, Hello world!, the countdown, and then reboots through the RTC watchdog exactly as silicon does (rst:0xc (RTC_SW_CPU_RST)), ~30× faster than real time. Chip resets (software, RTC watchdog) restart the machine from the ROM with the right reset cause; --no-reboot stops at the first reset instead.

esp32sim-c3 runs unmodified ESP-IDF firmware on the emulated RISC-V core, from the real mask ROM through the real bootloader into FreeRTOS. Same flags as esp32sim (--trace, --break, --watch, --peek, --disasm, --log-periph), printing RISC-V mnemonics with symbols.

H=examples/hello_world-c3/build ./target/release/esp32sim-c3 --boot rom --flash-mb 4
--bootloader $H/bootloader/bootloader.bin --ptable $H/partition_table/partition-table.bin
--app $H/hello_world.bin --elf $H/hello_world.elf --max-seconds 26

prints the ROM banner, the bootloader log, Hello world! and the reboot, three times over. Checked line-for-line against a physical C3 module: 205 of 208 console lines identical over three boot cycles — the difference is the ROM's Saved PC: line. --mac, --reset-cause and --strap let a run adopt a board's identity so the comparison is meaningful. WiFi station mode supports open and WPA2 virtual APs with DHCP and NAT. No boards; --boot app unsupported. See docs/esp32c3.md for what works, what does not, and the five emulator bugs the hardware found.

esp32sim-c6 is the same front end on the ESP32-C6: RV32IMAC, the PLIC, a unified memory map.

H=examples/hello_world-c6/build ./target/release/esp32sim-c6 --boot rom --flash-mb 4
--bootloader $H/bootloader/bootloader.bin --ptable $H/partition_table/partition-table.bin
--app $H/hello_world.bin --elf $H/hello_world.elf --max-seconds 27

Checked against a Waveshare ESP32-C6-LCD-1.47: 203 of 204 console lines identical over three boot cycles, including the ROM's Saved PC: after esp_restart(); the one difference is that line on the first boot. --board waveshare-c6-lcd147 adds that board — the ST7789 over SPI2 and the GDMA, the WS2812, the BOOT button — and the C6's 802.15.4 MAC answers energy scans, so the board's LVGL spectrum-scanner firmware runs end to end (examples/waveshare-c6-lcd147/). The MAC also sends and receives frames with the timing of the air, and --cooja makes the C6 an external mote of Cooja-NG, driven in exact lock-step over NDJSON (an unmodified Contiki-NG-on-IDF image exchanging broadcasts with emulated MSP430 nodes). WiFi works too: --wifi attaches the same virtual access point as on the S3 (see WiFi below). See docs/esp32c6.md.

1.5 press btn1 150 # GPIO17 low for 150 ms 2.2 knob cw 2 # two quadrature detents on CLK5/DT4 3.4 press btn2 120 4.0 serial {"action":"set_note","value":"9"} # into USB-CDC RX 5.0 stop

WiFi (ESP32-S3, C3, C6 and the classic ESP32)

--wifi ssid=NAME[,psk=PASS,chan=N,bssid=..] attaches a virtual access point that the unmodified Espressif WiFi blob associates with — scan, authentication, association and, with a passphrase, the WPA2-PSK four-way handshake — and a virtual network behind it (DHCP, ARP, ICMP, DNS, SNTP off the host clock). --net nat (the default) relays the guest's TCP and UDP over ordinary host sockets, so firmware reaches the real network; --net none refuses it. No firmware changes, no root, no tun device.

./target/release/esp32sim --board waveshare-lcd4b --boot rom --flash-mb 16 --psram-mb 8
--bootloader $P/bootloader/bootloader.bin --ptable $P/partition_table/partition-table.bin
--app $P/energy_panel.bin --console usb --wifi "ssid=home,psk=secret" --max-seconds 45

runs the esp32-screen energy panel: it joins, takes a lease, syncs its clock, fetches two days of electricity prices over HTTPS and polls a real Home Assistant on the LAN. docs/networking-howto.md is the how-to (flags, debugging, limits); docs/wifi-plan.md and docs/networking-plan.md describe how the MAC model and the packet path work, and the Networking section of docs/architecture.md the layers every chip shares.

The ESP32-C6 joins the same access point and the same network, through its own MAC model (docs/wifi-c6-plan.md): one station, legacy rates, no power save. examples/c6-wifi-station is the specimen — scan, join, lease, five pings, and the state on the board's screen — and every C6 radio run needs --stub bb_init=0 (the PHY's baseband calibration wants analog hardware):

B=examples/c6-wifi-station/build-emu ./target/release/esp32sim-c6 --boot rom --flash-mb 4 --board waveshare-c6-lcd147 --console usb
--bootloader $B/bootloader/bootloader.bin --ptable $B/partition_table/partition-table.bin
--app $B/c6_wifi_station.bin --elf $B/c6_wifi_station.elf --stub bb_init=0
--wifi ssid=esp32sim,psk=esp32sim-pass --max-seconds 14

The ESP32-C3 joins the same access point too, through a MAC model derived from the S3's. The same station example, built for each chip without the display, is committed under web/wasm/fw/public/ as the input of the WiFi goldens (wifi_station_{s3,c3,c6} in cli/tests/goldens.rs), so it runs without a local build:

P=web/wasm/fw/public ./target/release/esp32sim-c3 --boot rom --flash-mb 4 --console usb
--bootloader $P/c3-wifi-bootloader.bin --ptable $P/c3-wifi-ptable.bin --app $P/c3-wifi_station.bin
--wifi ssid=esp32sim,psk=esp32sim-pass --max-seconds 14

examples/c6-radio-dashboard runs WiFi and 802.15.4 energy scans in one firmware, a page each.

tools/wasm-build.sh # the module -> web/wasm/esp32sim.wasm tools/fetch-d

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →