Show HN:Esp32sim 用 Rust 在浏览器中运行 ESP32 固件
Show HN: Esp32sim – Run ESP32 firmware in the browser
Esp32sim 是一个用 Rust 编写的 ESP32 模拟器,可指令级模拟 ESP32、S3、C3、C6 四款 SoC,覆盖 Xtensa 与 RISC-V 两种架构,直接启动真实 mask ROM、二级 bootloader 和未修改的应用镜像。
esp32sim — 用 Rust 编写的 ESP32 模拟器:Xtensa(ESP32、S3)与 RISC-V(C3、C6)
对四款 ESP32 SoC 进行指令级模拟,覆盖 Espressif 的两种 CPU 架构。 每款都启动真实的 mask ROM、真实的二级引导程序以及未经修改的应用程序 镜像——没有打补丁的固件,也没有挡路的桩——并对 SoC 建模到足以端到端运行真实 项目的程度。无云端,无账号。MIT。
经典 ESP32 还会在双 LX6 核心上启动其 ECO3 ROM,带 GPIO、UART 和定时器;--board ttgo-tdisplay 增加了 LilyGO TTGO T-Display 的 ST7789 和按键。
| ESP32-S3 | ESP32-C3 | ESP32-C6 | |
|---|---|---|---|
| CPU | 2 × Xtensa LX7 @ 240 MHz | 1 × RISC-V RV32IMC @ 160 MHz | 1 × RISC-V RV32IMAC @ 160 MHz |
| 命令 | esp32sim |
esp32sim-c3 |
esp32sim-c6 |
| 核心 crate | xtensa-lx7 |
riscv-rv32 |
riscv-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、摄像头、音频 | 无——仅控制台 | ST7789 172×320(SPI+DMA)、WS2812、一个 802.15.4 能量检测替身 |
| WiFi | 虚拟 AP、WPA2、DHCP/DNS/NTP、到真实网络的 NAT、HTTPS | 同样的虚拟 AP 和网络:扫描、WPA2、DHCP、NAT(一个站点) | 同样的虚拟 AP 和网络:扫描、WPA2、DHCP、NAT(一个站点,传统速率) |
| 速度 | 块解释器 + AArch64 JIT,约 150–240 Minsn/s | 纯解释器(尚无块缓存或 JIT)——在 hello_world 上仍远快于实时 | 同样的解释器 |
| 在浏览器中 | 是(WebAssembly) | 是(WebAssembly) | 是(WebAssembly) |
| 报告的硬件对比 | JTAG trace,时序循环重新同步后 8000 步 | 控制台 diff,205/208 行一致 | 控制台 diff,203/204 行一致 |
| 状态 | 成熟 | 草稿——见 docs/esp32c3.md | 草稿——见 docs/esp32c6.md |
这些全清单和硬件计数描述的是早先的手动运行。CI 运行的是已检入的 抽样解码器语料和模拟器黄金输出;它不包含外部全清单测试。Xtensa objdump 测试 排除 PIE 操作数比较。硬件 trace 对比会归一化窗口异常并重新同步延时循环, 因此并非不间断的逐周期一致。见 测试范围、CI 工作流、 Xtensa 解码器测试 和 硬件比较器。
大部分 SoC 是共享的:C3 原样复用 S3 的 UART、USB-Serial/JTAG、systimer、定时器组、 GPIO、SPI flash 控制器、GDMA 和 SHA/AES/RSA 模型,并加入自己的内存映射、 缓存控制器和中断矩阵;C6 再次复用同样的模型,并带有自己的内存 映射、PLIC 前端、L1 缓存、PCR 和常开 LP 模块。只有 CPU crate 是真正 独立的。
在浏览器中试用,无需安装: https://mikroverk.github.io/esp32sim/ —— S3 上的 Linux、 带 SID 播放器的 Touch-LCD-4B 面板、pocket-tank LLM 水族箱,以及 C3 和 C6 演示。
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)
开发板(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(ST7701S 480×480,通过 LCD_CAM RGB 总线,GT911 触摸,TCA9554,编解码器),运行带触摸的 esp32-screen LVGL 面板;--board waveshare-amoled18-v2 是 Waveshare ESP32-S3-Touch-AMOLED-1.8 V2(CO5300 368x448,通过 QSPI,CST820 触摸);--board waveshare-cam 是 Waveshare ESP32‑S3‑CAM‑OV5640(CH32V003 IO 扩展器,OV5640 通过 SCCB,ES8311/ES7210 编解码器在 I2C0 上,扬声器在 I2S1 上,OV5640 在 LCD_CAM DVP 端口上)——端到端运行 waveshare-autopling 固件(IDF 5.5,16 MB flash,8 MB 八线 PSRAM:--flash-mb 16 --psram-mb 8):来自 --cam-image 或浏览器(图片上传 / 摄像头)的相机帧 → 在模拟的 PIE SIMD 单元上运行 esp‑dl 行人检测器 → 在 ES8311 上发出提示音。参见 examples/waveshare-cam/。添加一块板 = 实现该 trait(gpio_changes、rmt_frame、i2c_devices、camera_frame、……)。
atech14 演示所运行的 Pocket Synth 固件是它自己的项目:atech-firmware——一个 PlatformIO 构建的 SID 合成器和用于真实 .sid 曲调的 cRSID 播放器。本仓库在 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
掩码 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+引导加载程序,直接加载应用镜像。
一个普通的 ESP‑IDF 项目,例如 examples/hello_world(用 idf.py set-target esp32s3 && idf.py build 构建的 IDF 5.5 入门示例):
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 横幅、引导加载程序和应用日志,Hello world!,倒计时,然后像真实芯片一样通过 RTC 看门狗重启(rst:0xc (RTC_SW_CPU_RST)),比实时快约 30 倍。芯片复位(软件、RTC 看门狗)会以正确的复位原因从 ROM 重新启动机器;--no-reboot 则在第一次复位时停止。
运行——ESP32-C3
esp32sim-c3 在模拟的 RISC-V 核心上运行未经修改的 ESP-IDF 固件,从真实掩码 ROM 经过真实引导加载程序进入 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 横幅、引导加载程序日志、Hello world! 和重启,共三次。与物理 C3 模块逐行核对:三个启动周期内 208 行控制台输出中有 205 行完全相同——差异在于 ROM 的 Saved PC: 行。--mac、--reset-cause 和 --strap 让一次运行采用某块板的身份,从而使比较有意义。WiFi 站点模式支持开放和 WPA2 虚拟 AP,带 DHCP 和 NAT。无板卡;--boot app 不支持。参见 docs/esp32c3.md 了解哪些可用、哪些不可用,以及硬件发现的五个模拟器缺陷。
运行——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 行完全一致,包括 ROM 在 esp_restart() 之后的 Saved PC:;唯一的差异是首次启动时的那一行。--board waveshare-c6-lcd147 增加了该开发板——SPI2 上的 ST7789 和 GDMA、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 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 以及经典 ESP32)
--wifi ssid=NAME[,psk=PASS,chan=N,bssid=..] 挂载一个虚拟接入点,未经修改的乐鑫 WiFi blob 可与之关联——扫描、认证、关联,以及在有口令时完成 WPA2-PSK 四次握手——并在其后提供一个虚拟网络(DHCP、ARP、ICMP、DNS、基于主机时钟的 SNTP)。--net nat(默认)通过普通主机套接字中继客户机的 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):单站点、传统速率、无省电。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 14ESP32-C3 也通过源自 S3 的 MAC 模型加入同一接入点。同一个站点示例在不含显示屏的情况下为各芯片构建,已提交在 web/wasm/fw/public/ 下作为 WiFi 黄金测试的输入(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 14examples/c6-radio-dashboard 在一个固件中运行 WiFi 和 802.15.4 能量扫描,各占一页。
在浏览器中(WebAssembly)
tools/wasm-build.sh # the module -> web/wasm/esp32sim.wasm tools/fetch-demo-assets.sh # mask ROMs, xterm.js and the Linux image (--no-linux skips its 16 MB) python3 -m http.server -d web 8790 # then open http://127.0.0.1:8790/?wasm&fw=hello
演示固件已提交;掩膜 ROM、xterm.js 和 Linux 镜像未提交,且每个演示都从 ROM 启动,因此只需执行一次获取。它获取的是 Pages 站点所提供的相同文件。
同一个模拟器编译为 WebAssembly,在页面的 Web Worker 中运行:选择一块板子,从磁盘加载 ROM ELF 和固件(或使用 ?wasm&fw=<name> 获取托管清单),然后按 Boot。hello_world、带 SID 播放器的 Touch-LCD-4B 面板、Atech 板、ESP32-C3 和 ESP32-C6 都能在 Chrome 中实时运行;没有 NAT(浏览器没有套接字)。S3 有自己的 WebAssembly JIT(热块和热区域,基于 WASM SIMD 的 PIE)。参见 docs/wasm.md。在线体验:https://mikroverk.github.io/esp32sim/ — ESP32-S3 上的 Linux 6.11(svermigo/Linux-on-esp32-S3,登录并在控制台输入),带 SID 播放器的 Touch-LCD-4B 面板、Atech 板、pocket-tank(一个 4 位 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(解释执行而非运行原生代码——结果必须完全一致)。环境变量:ESP_EMU_DEBUG、ESP_EMU_DEBUG_SPI、ESP_EMU_DEBUG_USB、ESP_EMU_DEBUG_WIFI[_FRAMES]、ESP_EMU_DEBUG_NET、ESP_EMU_DEBUG_AES、ESP_EMU_DEBUG_SHA、ESP_EMU_DEBUG_RSA。
文档
- docs/adding.md — 添加外设、带设备的板子、CPU/芯片、观察器
docs/ — 架构、外设覆盖、板子、CLI 参考、Web UI 协议、设计决策与陷阱、路线图、网络操作指南、WiFi 和 网络设计说明,以及 测试计划。
来源
根据 ESP‑IDF 寄存器头文件、ESP‑IDF 附带的 Xtensa 核心配置、RISC‑V 规范以及 C3 和 C6 技术参考手册,并结合观察到的固件行为编写。仅参考 QEMU 确认指令语义(未复制代码)。MIT。
与真实芯片的差分测试(hw/)
每款芯片都用不同方法对照硬件检查:S3 采用 JTAG 锁步(见下文),C3 和 C6 则通过对比捕获的控制台输出与运行相同二进制文件的模拟器(hw/c3-hello-world-real.txt,205/208 行一致——参见 docs/esp32c3.md;hw/c6-hello-world-real.txt,203/204——参见 docs/esp32c6.md)。这些对比各自发现了五个模拟器缺陷,包括一个设备命令竞态和一个复位时未重新启动的周期计数器——这两者都是无板测试无法发现的。
ESP32-S3,JTAG 锁步
DIFF_DIR=hw/<board> FLASH_MB=8 hw/difftest.sh 3000 — 脚本通过 JTAG 从连接的芯片读取 efuses/strap,然后在同一 flash 转储上锁步运行芯片和模拟器(如果存在 flash-8M.bin)。Atech 板,2026-08-25:从复位起 3000 步,0 次分歧。
任何通过 USB 连接的 ESP32‑S3 板子都可以(其内置 USB‑Serial/JTAG 同时承载控制台和 JTAG)。整个流程全部脚本化:
# one-time: dump the board's bootloader/partition table/app start (esptool) into hw/flash-0-1M.bin hw/difftest.sh 8000 # reset → single-step 8000 instructions on the chip and in the emulator, diff hw/difftest-at.sh 403c8948 6000 # run both to a PC (here the 2nd-stage bootloader entry), then step 6000 and diff
difftest*.sh 读取芯片的 efuses(hw/efuse.txt)、strapping 引脚和外设复位状态(hw/reset-regs.txt,在 reset halt 时通过 JTAG 转储),并从相同的镜像和状态启动模拟器。hw/compare.py 逐指令对比 pc a0..a15 ps windowbase,屏蔽 PS.INTLEVEL(单步执行期间强制),隐藏窗口异常处理程序(调试器会原子地跳过它们),并在 CCOUNT 计时的延迟循环之间重新同步——当每一步耗时数毫秒时,这些循环的迭代次数会不同。
目前结果:ROM 复位路径(8000 步)和 IDF 5.5 引导加载程序的 3000+ 步运行均未出现 PC 分歧;剩余的寄存器差异是依赖上次启动的 RTC 域值。
实时板卡 UI(ESP32-S3)
./target/release/esp32sim --boot rom --bootloader … --ptable … --app … --web 8766
# open http://127.0.0.1:8766/--web PORT 实时运行模拟器并提供 web/run.html:14 端口板卡(旋钮 + LED 环、按钮、扬声器 VU、按物理方向摆放的 ST7735 以及一份可读副本)、USB-CDC 和 UART0 控制台、用于 SDK JSON 协议的操作框,以及通过 WebAudio 输出的音频。输入方式:点击按钮,在旋钮上滚轮/拖动/←→,点击顶盖将其按下。
性能 / 实时性(ESP32-S3)
指令块只解码一次并从缓存执行,然后编译为原生 AArch64 代码(--no-jit 回退到解释器且必须给出相同结果);空闲核心(waiti)会被跳过,设备时间按计算出的定时器截止时间惰性提供,中断线仅在源发生变化时重新计算。每一步背后的测量数据见 docs/speed-plan.md。
Pocket Synth 固件在空闲或演奏音符时都能以有余量的实时速度运行;唯一跟不上的是完整显示重绘,此时核心 1 以 100% 占用进行 SPI 位操作(每 20 ms 4.8 M 条指令——需要 240 Minsn/s,在 M 系列 MacBook 上约达到 70 Minsn/s)。因此每次重绘会造成约 0.3 s 的延迟,UI 的自适应音频缓冲区会吸收它(在欠载时增长,最多 400 ms),之后节拍器会恢复;如果模拟器落后超过 0.5 s,它会重新同步而不是突发追赶。页眉显示 real time、⚠ N s behind 和重新同步次数。
ESP_EMU_RT_LOG=1 打印每个实际耗时 > 40 ms 的 20 ms 窗口,包含两个核心的指令计数和 PC。hw/wsdrive.py [port] [seconds] 无需浏览器即可驱动 UI 协议(按钮按下 + 旋钮转动),并报告推送间隔、延迟和已交付音频——用它来测量调度器的改动。
贡献
欢迎贡献。CONTRIBUTING.md 列出了拉取请求所需的检查(与 CI 运行的相同)以及测试、实验和证据的约定。
许可证
esp32sim 在 MIT 许可证 下发布,版权归 Joakim Eriksson(@joakimeriksson)、Alice(@aliceisjustplaying)和(@mfiumara)Mattia Fiumara 所有。
第三方代码、数据和演示文件保留各自的许可证:
- pocket-tank(mediacutlet/pocket-tank,MIT)。其引导加载程序、分区表和应用程序连同
pocket-tank-LICENSE.txt一起提交在web/wasm/fw/public/下;其模型是获取的。 - MicroPython(micropython.org,MIT):T-Display 演示的官方 ESP32_GENERIC v1.29.0 镜像,由
tools/fetch-micropython.sh获取,未提交;演示的应用程序和文件系统位于examples/ttgo-tdisplay中,包含 Russ Hughes 的 st7789py 驱动和字体(MIT),声明见web/wasm/fw/public/tdisplay-NOTICE.txt。 - Mask ROM ELF(espressif/esp-rom-elfs,Apache-2.0),获取,未提交。
- xterm.js(xtermjs/xterm.js,MIT),获取,未提交。
- Linux 镜像(svermigo/Linux-on-esp32-S3,GPL-3.0),获取,未提交。
- ESP-IDF ADC 校准数据(Espressif,Apache-2.0):来自 #165 的曲线拟合系数和 S3 ADC1 校准,现已合并到
esp-periph/src/sar_adc.rs中;见esp-periph/LICENSE-ADC。
来源:Hacker News · github.com