Sun 2026.10.18
esp32sim: running unmodified ESP32 firmware with no board
Today's pick: esp32sim
esp32sim is an emulator product that runs firmware for four ESP32-family chips without a physical board. It boots unmodified firmware from the chip’s own ROM and runs in a CLI and in the browser.
It is today’s pick because I could directly check whether it boots from the chip’s own ROM without a board.
- Found on
- Show HN
- Built with
- Rust, WebAssembly, Claude
The problem
The ESP32 is a small chip made by Espressif, with Wi-Fi and Bluetooth built in. Developers check how its firmwareThe program built into a device to control its hardware. On devices such as TVs, it means all the system software, including the operating system. behaves by loading it onto a board with the chip. App and server developers also need the board to test communication with the firmware. If a team has only one board, they must wait while the firmware developer uses it. The CIContinuous integration: a setup that builds and tests code automatically every time it is pushed. server has no board, so firmware tests cannot run automatically either. A user named raminf commented on esp32sim’s Show HNA post on Hacker News, a developer community, in which someone presents what they made. Its title starts with "Show HN:". post on Hacker News on October 17, 2026. They wrote that on each IoT project, they had built a separate "functional simulator" instead of waiting for hardware. Such a program imitates the device at the level of Bluetooth Low Energy (BLE) or Wi-Fi commands.
Espressif officially supports QEMU for emulation. QEMU is an open-source emulator. It lets programs for a CPU of a different design run on an ordinary computer. Espressif forkCopying a repository to develop it separately, or the copy made that way. QEMU and added ESP32 support. The QEMU Emulator page in the ESP-IDFEspressif’s development framework for the ESP32, with its libraries and build tools. Programming Guide on docs.espressif.com says Espressif maintains the fork. The fork emulates the ESP32’s CPU, memory and several peripheralA part inside a chip that works alongside the CPU, such as a timer or a communication circuit.. On a computer with ESP-IDF installed, a developer runs idf.py qemu monitor to build the app. The command also runs QEMU and prints the firmware’s log on screen.
How it solves it
esp32sim, by Joakim Eriksson and Mattia Fiumara, emulates four chips at the level of CPU instructions: ESP32, ESP32-S3, ESP32-C3 and ESP32-C6. A chip holds code stored in its mask ROMMemory whose contents are written in during manufacturing. It cannot be modified, and its code is the first to run when the power comes on.. esp32sim reads Espressif’s published ROM files (esp-rom-elfs) and runs that code first. It then runs the bootloaderA small program that loads the app or the operating system into memory and starts it after power-on. and an unmodified app. The display, buttons, camera and audio chip connected to a board are emulated too.
esp32sim also includes a virtual wireless network. The emulator has a virtual router. The firmware finds it and joins it with a password. The virtual router assigns IP addresses and answers ping requests, domain lookups and time-sync requests. esp32sim relays the firmware’s internet traffic to the host network. esp32sim’s code also builds to WebAssembly, so firmware can boot from the ROM in the browser as well.
What is different
First, Espressif’s QEMU requires you to install ESP-IDF, QEMU itself and the system libraries. esp32sim needs only one executable, the ROM files and the firmware. The trade-off is the amount of code. The authors used no code from other emulators. They wrote the emulation of both CPU types and the peripherals themselves. The code totals 69,914 lines of Rust. The JITJust-in-time compilation: turning frequently run code into machine code while the program runs, to make it faster. works only on ARM-based computers (such as Apple Silicon).
Second, the ESP-IDF document says only that the QEMU fork emulates the CPU, memory and several peripherals. esp32sim runs the chip’s own ROM and bootloader without modifications. The console shows the ROM’s first message and the restart cause. The README says the authors compared the emulator’s console output with a real C3 board’s output line by line. They found 205 of 208 lines identical over three boots. The trade-off also comes from running the chip’s own ROM. The ROM’s wireless code needs real analog circuitry to work. For the C6, the README describes replacing the radio calibration function with an empty one.
Third, firmware can join esp32sim’s virtual router and ping it without a real router. The README says that only one device can join the virtual router. It supports only the transfer rates of older Wi-Fi standards and has no power-saving mode. The roadmap says there are no plans to emulate Wi-Fi and BLE down to the radio chip’s internals. Espressif’s QEMU document says nothing about wireless, so I did not compare wireless support.
What it makes easier
- App and server developers on a team with one board. Instead of waiting for the board, each can test the firmware’s Wi-Fi join and traffic on their own laptop. Today the C3 and C6 firmware joined the virtual router and completed 5 pings.
- Teams that run CI. esp32sim needs only an executable, the ROM files and the firmware, so a CI server can boot firmware from the ROM and check the console output. Today the S3 ran 26 seconds of emulated time in 0.7 seconds of real time.
- Developers new to the ESP32. Before buying a board, they can check firmware booting in the browser. I did not check the browser build today.
How it was built
- 337 commits from 2026-09-21 to 10-16. Mattia Fiumara made 226 of them, and Joakim Eriksson made 88. 174 commits end with a Co-authored-by line saying they were written with Claude Opus 5.5.
- 12 Rust packages (crates) and 973 test functions. According to docs/architecture.md, the authors consulted QEMU only to confirm how CPU instructions behave.
- According to AGENTS.mdA file in a repository that gives AI coding agents their working rules., experiment records hold only reproducible facts (commands, inputs, hashes, numbers and outcomes) and do not narrate the agent’s working process. A test passes only if every bit of its output matches the expected output.
Running it
- Build. I built only the CLI version with cargo build --release. It took 1 minute 00 seconds. The three executables (esp32sim, esp32sim-c3, esp32sim-c6) are 17 MB each.
- ROMs. tools/fetch-rom-elfs.sh downloaded Espressif’s ROM archive (esp-rom-elfs 20260528) and checked its SHA-256 hash. It then unpacked the archive and extracted the four ROM files. 1.4 seconds.
- ESP32-S3 hello_world. I booted the repository’s demo firmware from the chip’s own ROM. The console showed the ROM’s first message "ESP-ROM:esp32s3-20210327", the bootloader log, Hello world and a 10-second countdown. Then the chip restarted. The emulator ran 26 seconds of emulated time in 0.7 seconds of real time (36 times faster). After both restarts, the chip booted again from the ROM. The test computer is Intel/AMD, so the JIT never ran.
- C3, C6 and ESP32 (the first model). The same hello_world firmware booted three times on each chip. The C3 ran 26 seconds of emulated time in 5.0 seconds of real time (5 times faster). The C6 ran 27 seconds in 6.7 seconds (4 times faster). The ESP32 ran 26 seconds in 0.9 seconds (29 times faster).
- C3 Wi-Fi join. The --wifi option brought up a virtual router (named esp32sim, with WPA2 encryption). The firmware found and joined that router. It got the IP address 10.0.2.15. All 5 pings sent to the router succeeded. The emulator ran 14 seconds of emulated time in 3.3 seconds of real time. The traffic sent to the host network was 0 bytes.
- C6 Wi-Fi join. The README gives an option that replaces the calibration function (--stub bb_init=0). The repository’s firmware carries no function-name information (ELF), so with that option esp32sim printed an unknown symbol error and exited. Without the option, the firmware found and joined the router. All 5 pings completed. This took 4.1 seconds of real time. It also printed three calibration errors (pll_cal exceeds 2ms). The test’s expected output, recorded with the option, has the same three lines.
$ esp32sim --board none --boot rom --console uart0 --rom roms/esp32s3_rev0_rom.elf ... --max-seconds 26
[emu] ROM loaded from roms/esp32s3_rev0_rom.elf
[emu] ROM boot from reset vector 0x40000400
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0xf (SPI_FAST_FLASH_BOOT)
...
This is esp32s3 chip with 2 CPU core(s), WiFi/BLE, silicon revision v0.2, 8MB external flash
...
Restarting now.
[emu] chip reset at t=11.005s: cause 0xc (RTC_SW_CPU_RESET)
ESP-ROM:esp32s3-20210327
...
[emu] stop: Halted — core0 11407872 + core1 6276760 insns in 0.7s wall = 25.3 Minsn/s; emulated 26.000s
[emu] blocks: 16434 built (0 cache flushes) core0, 2156 (0) core1; jit: 0 compiled, 0 KB code
Not checked
- The browser (WebAssembly) build and the display, camera and audio boards. Next time, check a display board’s screen in the browser.
- The BLE examples and the 973 tests. Next time, run the C3 BLE firmware and cargo test.
- The comparison with a real board. The README’s 208-line comparison is the authors’ own measurement.
Verdict
It works. All four chips booted from their own ROMs and printed Hello world. That said, the C3 and C6 ran only 4 to 5 times faster than real time on Intel and AMD (x86-64) computers, where the JIT does not run. It is worth a try when you want to boot ESP-IDF firmware without a board, on a CI server or in a browser. It fits when you only need to check console output and a Wi-Fi connection. If you need to verify behaviour at the level of the radio chip’s internals or precise execution timing, this product is not the right choice.
What to take away
- In a repository built with agents, decide the format of the experiment records first. esp32sim wrote that format down in AGENTS.md.
- Compare an emulator’s output with a real board line by line. According to the README, the authors found five bugs in the emulator while comparing it with a real C3 board.
- Pin the versions of ROMs and demo firmware with hashes. Today I downloaded the four ROMs in 1.4 seconds with a script. The script contains the SHA-256 hash of their archive. The demo firmware was in the repository, so it could boot as soon as the build (1 minute) finished.
Getting started
git clone https://github.com/mikroverk/esp32sim && cd esp32simcargo build --release -p esp32simtools/fetch-rom-elfs.sh romsP=web/wasm/fw/public; target/release/esp32sim --board none --boot rom --console uart0 --rom roms/esp32s3_rev0_rom.elf --flash-mb 8 --psram-mb 2 --bootloader $P/hello-bootloader.bin --ptable $P/hello-ptable.bin --app $P/hello_world.bin --max-seconds 26
- You need
- Rust stable and Cargo. The repository has no file that pins the Rust version · The four Espressif ROM files (tools/fetch-rom-elfs.sh downloads them as one archive and checks the archive’s SHA-256 hash) · Firmware files: the demo firmware in the repository, or the bootloader, partition-table and app .bin files built with ESP-IDF
- Cost
- MIT license, free. There is no pricing page.
The JIT is for ARM-based (AArch64) computers only, so on Intel and AMD (x86-64) computers esp32sim runs in a slower mode. With the --wifi option, esp32sim relays the firmware’s traffic to the host network, so run it in an isolated environment.
Steps select 1 ran.
Progress
- Day 3Active
- Day 7Active
- Day 15Upcoming
- Day 30Upcoming
- Day 90Upcoming
- Day 180Upcoming
- Day 365Upcoming
Latest check Day 7, Sat 2026.10.24
- Commits since launch
- 0 → 60
- Stars
- 149 → 641
- Latest release
- v0.3.0
- Last commit
- 2026.10.23
- HN points
- 122 → 211
- `--chip esp32` selects the dual-core ESP32. The emulator boots it from the chip’s own ROM and then runs an unmodified app (v0.3.0).
- WiFi and Bluetooth work on every chip (v0.3.0).
- With `--wifi-nearby`, a WiFi scan on the emulated chip also finds nearby networks that the emulator simulates.
- The new host options `--adc` and `--adc-raw` set the chip’s analog input values.
- The emulator now supports GP-SPI3, a general-purpose SPI controller on the ESP32-S3.