Show HN: Esp32sim – 在浏览器中运行 ESP32 固件

Hacker News Top 工具

摘要

Show HN: esp32sim 是一个用 Rust 编写的 ESP32 模拟器,支持 Xtensa(ESP32、S3)与 RISC-V(C3、C6)四种芯片的指令级仿真,可在浏览器中通过 WebAssembly 直接运行真实固件。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/10/17 16:49

mikroverk/esp32sim

来源: https://github.com/mikroverk/esp32sim

esp32sim — 用 Rust 实现的 ESP32 模拟器:

Xtensa(ESP32、S3)与 RISC-V(C3、C6)

对四款 ESP32 SoC 进行指令级模拟,覆盖 乐鑫的两种 CPU 架构。每一款都启动真实的 mask ROM、真实的二级 bootloader 以及未经修改的应用镜像——没有打过补丁的固件、没有挡路的桩(stub)——并对 SoC 建模到足以端到端运行真实项目的程度。

无需云端,无需账号。MIT 许可。经典 ESP32 也会在双 LX6 核心上启动其 ECO3 ROM,并支持 GPIO、UART 和定时器;--board ttgo-tdisplay 会加上 LilyGO TTGO T-Display 的 ST7789 和按键。

ESP32-S3ESP32-C3ESP32-C6
CPU2 × Xtensa LX7 @ 240 MHz1 × RISC-V RV32IMC @ 160 MHz1 × RISC-V RV32IMAC @ 160 MHz
命令esp32simesp32sim-c3esp32sim-c6
核心 cratextensa-lx7riscv-rv32riscv-rv32(+ A 扩展)
已报告的全清单解码器校验977 k 条指令,0 处不匹配161 k 条指令,0 处不匹配126 k 条指令,0 处不匹配
启动路径ROM → bootloader → FreeRTOS → 应用ROM → bootloader → FreeRTOS → 应用ROM → bootloader → FreeRTOS → 应用
开发板 / 显示屏ST7735、ST7701S 480×480 + 触摸、WS2812、摄像头、音频无——仅控制台走 SPI+DMA 的 ST7789 172×320、WS2812、一个 802.15.4 能量检测替代实现
WiFi虚拟 AP、WPA2、DHCP/DNS/NTP、NAT 到真实网络、HTTPS同一个虚拟 AP 和网络:扫描、WPA2、DHCP、NAT(一个 station)同一个虚拟 AP 和网络:扫描、WPA2、DHCP、NAT(一个 station,传统速率)
速度块解释器 + AArch64 JIT,约 150–240 Minsn/s普通解释器(尚无块缓存或 JIT)——在 hello_world 上仍远高于实时同一个解释器
浏览器内运行支持(WebAssembly)支持(WebAssembly)支持(WebAssembly)
已报告的硬件对比JTAG trace,时序循环重同步后 8000 步控制台 diff,208 行中 205 行一致控制台 diff,204 行中 203 行一致
状态成熟草稿——见 docs/esp32c3.md草稿——见 docs/esp32c6.md

这些全清单和硬件计数来自早期的手动运行。CI 运行的是签入的采样解码器语料库和模拟器 golden 输出;它不包含外部的全清单测试。Xtensa objdump 测试排除了 PIE 操作数比较。硬件 trace 对比会规范化窗口异常并对延时循环重新同步,因此它并非逐周期不间断的一致。参见 测试范围、CI workflow、Xtensa 解码器测试 和 硬件对比器。

SoC 的大部分是共享的:C3 原封不动地复用了 S3 的 UART、USB-Serial/JTAG、systimer、定时器组、GPIO、SPI flash 控制器、GDMA 以及 SHA/AES/RSA 模型,并加上自己的内存映射、缓存控制器和中断矩阵;C6 再次复用这些模型,并配有自己内存映射、PLIC 前端、L1 缓存、PCR 和常开的 LP 模块。只有 CPU crate 是真正独立的。

在浏览器中试用,无需安装:

—— S3 上的 Linux、带 SID 播放器的 Touch-LCD-4B 面板、pocket-tank LLM 水族箱,以及 C3 和 C6 的演示。

esp32sim/
  emu-core/      Bus 与 Core trait、Trap、ClockTree、AArch64 编码器——两个核心共享
  esp-soc/       Machine:调度器、设备时间、控制台、脚本、Web UI、加载器、开发板——每颗芯片共用一台机器;芯片插入自己的核心、总线和中断路由;虚拟 WiFi 接入点、其背后的网络与 NAT,适用于每颗芯片
  esp-periph/    芯片共享的外设 IP(UART、systimer、TIMG、GPIO、SPI_MEM、GDMA、加密、I2S、RMT、I2C、...)、Device trait,以及挂载它们的 device_set! 表

  ── ESP32-S3(Xtensa LX7,双核)──
  xtensa-lx7/    解码器(采样的 objdump 校验加可选的全清单)、解释器(窗口寄存器、循环、XEA2 异常/中断、FPU、MAC16、布尔、PIE SIMD)、基本块解释器、AArch64 JIT、兼容 objdump 的反汇编器
  esp32s3/       SoC + 开发板:内存映射、缓存 MMU、SPI flash/PSRAM、SHA/AES/RSA、RNG、systimer、定时器组、中断矩阵(每核)、GPIO、USB-CDC、UART、I2C、GDMA + I2S/LCD_CAM、RMT TX、regi2c、RTC WDT、WiFi MAC;board/: atech14 / waveshare-cam / waveshare-lcd4b / waveshare-amoled18-v2 / none
  cli/            `esp32sim` 命令行,支持每颗芯片(`--chip`);`esp32sim-c3` / `esp32sim-c6` 是别名二进制

  ── ESP32-C3 与 ESP32-C6(RISC-V,单核)──
  riscv-rv32/     RV32IMAC 解码器(采样的 objdump 校验)、解释器、反汇编器
  esp32c3/        C3 SoC:内存映射、中断矩阵、缓存控制器、RNG;IP 相同处与 esp32s3 共享外设模型
  esp32c6/        C6 SoC:统一内存映射与 MMU、中断矩阵 + PLIC/INTPRI、L1 缓存、PCR、LP 模块(复位原因、软件复位、RTC 定时器)、ASSIST_DEBUG、802.15.4 与 WiFi MAC

  tests/          golden 输出回归测试及其 fixtures(tests/README.md)

  ── 共享 ──
  wasm/           C-ABI crate,把三台 Machine 封装进浏览器的一个模块
  web/            浏览器 UI(开发板绘图、控制台、音频、摄像头)+ emu.js/worker.js(配合 wasm)
  hw/             针对真实开发板的 JTAG 差分测试脚本,采集的 C3 与 C6 控制台
  examples/       hello_world(IDF、S3、C3 与 C6)、waveshare-cam(autopling 运行脚本 + 照片)、wifi-station(S3)、c6-wifi-station(为 S3、C3 和 C6 构建;WiFi golden 所用的固件)、c6-radio-dashboard(C6 开发板上的 WiFi + 802.15.4)
  tools/          PIE 表生成器(源自 TRM);bench.py(交错 A/B 基准测试);wasm-build.sh(WebAssembly 模块)

开发板(ESP32-S3)

SoC 模型会发出引脚级事件(GPIO 边沿、RMT 符号流、I2S 采样);由 BoardModel(esp-soc/src/board.rs)来解释它们。

--board atech14(默认)是 Atech 14 口开发板,带 ST7735、WS2812 灯环、编码器和按键; --board none 是裸模块——任何 ESP32-S3 固件,仅控制台; --board waveshare-lcd4b 是 Waveshare ESP32-S3-Touch-LCD-4B(通过 LCD_CAM RGB 总线的 ST7701S 480×480、GT911 触摸、TCA9554、编解码器),运行 esp32-screen LVGL 面板并支持触摸; --board waveshare-amoled18-v2 是 Waveshare ESP32-S3-Touch-AMOLED-1.8 V2(QSPI 的 CO5300 368x448、CST820 触摸); --board waveshare-cam 是 Waveshare ESP32-S3-CAM-OV5640(CH32V003 IO 扩展器、SCCB 上的 OV5640、I2C0 上的 ES8311/ES7210 编解码器、I2S1 上的扬声器、LCD_CAM DVP 端口上的 OV5640)——端到端运行 waveshare-autopling 固件(IDF 5.5,16 MB flash,8 MB 八线 PSRAM:--flash-mb 16 --psram-mb 8):来自 --cam-image 或浏览器(图片上传 / 摄像头)的摄像头帧 → 模拟 PIE SIMD 单元上的 esp-dl 行人检测 → 在 ES8311 上生成 pling。参见 examples/waveshare-cam/。

新增一块开发板 = 实现该 trait(gpio_changes、rmt_frame、i2c_devices、camera_frame 等)。

atech14 演示所运行的 Pocket Synth 固件是另一个项目:atech-firmware(https://github.com/joakimeriksson/atech-firmware)——一个 SID 合成器和 cRSID 播放器的 PlatformIO 构建,用于播放真实的 .sid 曲目。本仓库在其生成的产物中携带这些已构建镜像,位于 web/wasm/fw/public/。

运行 — ESP32-S3

cargo build --release
FW=web/wasm/fw/public   # golden 和网页所运行的演示镜像
./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

mask ROM ELF 会从 ~/.espressif/tools/esp-rom-elfs/*/esp32s3_rev0_rom.elf(随 ESP-IDF 提供)自动选取。若没有 ESP-IDF,tools/fetch-demo-assets.sh --no-linux 会把 S3、C3 和 C6 的 ROM 放进 web/wasm/fw/:传入 --rom web/wasm/fw/esp32s3_rev0_rom.elf,或为测试设置 ESP32SIM_ROM_DIR=web/wasm/fw。

--boot app 会跳过 ROM + bootloader,直接加载应用镜像。

一个普通的 ESP-IDF 项目,例如 examples/hello_world(用 idf.py set-target esp32s3 && idf.py build 构建的 IDF 5.5 get-started 示例):

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

会在 UART0 上打印 ROM 横幅、bootloader 和应用日志、Hello world!、倒计时,然后如同真实芯片一样经 RTC 看门狗重启(rst:0xc (RTC_SW_CPU_RST)),速度约为实时的 30 倍。

芯片复位(软件复位、RTC 看门狗)会带上正确的复位原因从 ROM 重新启动机器;--no-reboot 则在第一次复位时停下。

运行 — ESP32-C3

esp32sim-c3 在模拟的 RISC-V 核心上运行未经修改的 ESP-IDF 固件,从真实的 mask ROM 经由真实的 bootloader 进入 FreeRTOS。

与 esp32sim 相同的参数(--trace、--break、--watch、--peek、--disasm、--log-periph),并打印带符号的 RISC-V 助记符。

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

会打印 ROM 横幅、bootloader 日志、Hello world! 和重启,共重复三次。

与一块物理 C3 模块逐行核对:三个启动周期中 208 行控制台输出有 205 行一致——差异在于 ROM 的 Saved PC: 行。--mac、--reset-cause 和 --strap 让一次运行可以采用某块开发板的身份,从而使对比有意义。

WiFi station 模式支持开放与 WPA2 虚拟 AP,带 DHCP 和 NAT。没有开发板;不支持 --boot app。参见 docs/esp32c3.md,了解哪些可用、哪些不可用,以及硬件发现的五个模拟器 bug。

运行 — ESP32-C6

esp32sim-c6 是同一个前端在 ESP32-C6 上的版本:RV32IMAC、PLIC、统一内存映射。

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

与一块 Waveshare ESP32-C6-LCD-1.47 核对:三个启动周期中 204 行控制台输出有 203 行一致,包括 esp_restart() 之后 ROM 的 Saved PC:;唯一的差异是首次启动时的那一行。

--board waveshare-c6-lcd147 会加上这块开发板——走 SPI2 和 GDMA 的 ST7789、WS2812、BOOT 按键——并且 C6 的 802.15.4 MAC 会响应能量扫描,因此该板的 LVGL 频谱扫描器固件得以端到端运行(examples/waveshare-c6-lcd147/)。该 MAC 还按空口时序收发帧,--cooja 让 C6 成为 Cooja-NG 的外部 mote,通过 NDJSON 以精确同步锁步驱动(一个未经修改的 Contiki-NG-on-IDF 镜像与模拟的 MSP430 节点交换广播)。

WiFi 也能用:--wifi 会挂载与 S3 相同的虚拟接入点(参见下文 WiFi)。

参见 docs/esp32c6.md。

脚本(在模拟时间执行的宿主动作)

1.5  press btn1 150                # GPIO17 拉低 150 ms
2.2  knob cw 2                     # CLK5/DT4 上转过两个正交档位
3.4  press btn2 120
4.0  serial {"action":"set_note","value":"9"}   # 写入 USB-CDC RX
5.0  stop

WiFi(ESP32-S3、C3、C6 与经典 ESP32)

--wifi ssid=NAME[,psk=PASS,chan=N,bssid=..] 会挂载一个虚拟接入点,未经修改的乐鑫 WiFi blob 会与之关联——扫描、认证、关联,以及有口令时的 WPA2-PSK 四次握手——以及其背后的一个虚拟网络(DHCP、ARP、ICMP、DNS、基于宿主时钟的 SNTP)。

--net nat(默认)通过普通宿主 socket 中继客户机的 TCP 和 UDP,因此固件可以访问真实网络;--net none 则拒绝。无需修改固件,无需 root,无需 tun 设备。

./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

会运行 esp32-screen 能量面板:它会加入网络、获取租约、同步时钟、通过 HTTPS 获取两天的电价,并轮询局域网中一个真实的 Home Assistant。

docs/networking-howto.md 是使用指南(参数、调试、限制);docs/wifi-plan.md 和 docs/networking-plan.md 描述 MAC 模型与数据包路径的工作方式;docs/architecture.md 的 Networking 部分描述每颗芯片共享的各层。

ESP32-C6 通过自己的 MAC 模型加入同一个接入点和同一个网络(docs/wifi-c6-plan.md):一个 station、传统速率、无省电。

examples/c6-wifi-station 就是样例——扫描、加入、租约、五次 ping,以及开发板屏幕上的状态——并且每次 C6 无线运行都需要 --stub bb_init=0(PHY 的基带校准需要模拟硬件):

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

ESP32-C3 也加入同一个接入点,通过一个源自 S3 的 MAC 模型。同一个 station 示例为每颗芯片构建(不带显示屏),已提交在 web/wasm/fw/public/ 下,作为 WiFi golden 的输入(cli/tests/goldens.rs 中的 wifi_station_{s3,c3,c6}),因此无需本地构建即可运行:

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 在一个固件里同时运行 WiFi 和 802.15.4 能量扫描,每样一页。

在浏览器中(WebAssembly)

tools/wasm-build.sh        # 构建模块 -> web/wasm/esp32sim.wasm
tools/fetch-demo-assets.sh # mask ROM、xterm.js 和 Linux 镜像(--no-linux 会跳过其 16 MB)
python3 -m http.server -d web 8790   # 然后打开 http://127.0.0.1:8790/?wasm&fw=hello

演示固件已提交;mask ROM、xterm.js 和 Linux 镜像未提交,而每个演示都从 ROM 启动,所以请先运行一次 fetch。它获取的文件与 Pages 站点所提供的是同一套。

同一个模拟器编译为 WebAssembly,运行在页面内的 Web Worker 中:选择开发板、从磁盘加载 ROM ELF 和固件(或用 ?wasm&fw= 指向托管的 manifest),按下 Boot 即可。

hello_world、带 SID 播放器的 Touch-LCD-4B 面板、Atech 开发板、ESP32-C3 和 ESP32-C6 都能在 Chrome 中实时运行;没有 NAT(浏览器没有 socket)。S3 拥有自己的 WebAssembly JIT(热块与热区域、WASM SIMD 上的 PIE)。

参见 docs/wasm.md。

在线体验:https://mikroverk.github.io/esp32sim/

—— ESP32-S3 上的 Linux 6.11(svermigo/Linux-on-esp32-S3(https://github.com/svermigo/Linux-on-esp32-S3),登录并在控制台输入)、带 SID 播放器的 Touch-LCD-4B 面板、Atech 开发板、pocket-tank(https://github.com/mediacutlet/pocket-tank,一个 4-bit transformer 在 Touch-AMOLED-1.8 上操纵鱼儿),以及启动 hello_world 的 ESP32-C3 和 C6;也可以从磁盘为任意芯片加载你自己的固件(选择开发板,或 esp32c3 / esp32c6)。

调试

--trace [--trace-from N]、--break ADDR、--watch ADDR(某个字发生变化时停止)、--peek ADDR[,N]、--profile(pc 直方图)、--log-periph(每个未知寄存器的首次触碰)、--stop-after-exceptions N、--gram-png(原始 ST7735 GRAM)、--no-jit(改为解释执行而非运行本地代码——结果必须完全一致)。

环境变量:`ES

相似文章

@knowledgefxg: 趣站推荐:Wokwi 一个在浏览器里就能玩的电子电路模拟器,可以轻松在线搞嵌入式或者物联网开发,你可以直接在线拖拽搭电路、写代码(甚至支持 MicroPython)、点一下就能跑模拟,连 WiFi 网络行为都能模拟。不用花一分钱买硬件,就…

X AI KOLs Timeline

Wokwi 是一个在浏览器中运行的电子电路模拟器,支持 ESP32 等嵌入式系统开发,可在线拖拽搭建电路、编写代码(包括 MicroPython)并模拟 Wi-Fi 行为,无需硬件即可验证项目。

The Rust on ESP Book

Hacker News Top

《The Rust on ESP Book》是一本全面的指南,用于在 Espressif 产品上进行嵌入式 Rust 开发,涵盖项目生成、工具链和软件栈结构。

Show HN: 在浏览器中编写、仿真和综合 VHDL/Verilog

Hacker News Top

RisingEdge.pro 是一个基于浏览器的平台,用于通过编写、仿真和综合 VHDL 和 SystemVerilog 代码来学习和练习硬件设计,使用真实的 EDA 工具(如 GHDL、Icarus 和 Yosys),并提供交互式波形和引导式课程。

Show HN: 运行于 WebAssembly 的 Firefox

Hacker News Top

Firefox 被编译为在 WebAssembly 中运行,采用基于 WebGL 的渲染和实验性的 JS 到 WASM JIT,网页内容通过 Puter 托管的 Wisp 服务器进行代理。