Esp32sim emulates four ESP32 chips and boots real firmware, with a browser demo

Esp32sim emulates four ESP32 chips and boots real firmware, with a browser demo

Esp32sim, from the GitHub owner mikroverk, is an instruction-level emulator of four ESP32 SoCs, covering both of Espressif's CPU architectures (Xtensa and RISC-V). Each chip boots the real mask ROM, the real 2nd-stage bootloader and an unmodified application image, with no patched firmware and no stubs in the way. The README says enough of each SoC is modelled to run real projects end to end. It needs no cloud and no accounts, and it is MIT-licensed. It was posted as a Show HN.

The chips are the ESP32-S3 (Xtensa LX7, dual core), the ESP32-C3 and ESP32-C6 (both RV32IMAC RISC-V, single core), and the classic ESP32, which boots its ECO3 ROM on dual LX6 cores with GPIO, UART and timers. The classic chip has a board option, ttgo-tdisplay, that adds the LilyGO TTGO T-Display's ST7789 screen and buttons. Much of the code 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. The S3 interpreter covers windowed registers, loops, exceptions and interrupts, FPU, MAC16, booleans and PIE SIMD, and the repository lists a basic-block interpreter and an AArch64 JIT for it.

A browser demo runs with no install at mikroverk.github.io/esp32sim. It includes Linux on the S3, the Waveshare Touch-LCD-4B panel with its SID player, a pocket-tank LLM aquarium, and the C3 and C6 demos. A wasm crate wraps the S3, C3 and C6 machines for the browser in one module.

On the command line, a plain ESP-IDF 5.5 hello_world built for the S3 prints the ROM banner, the bootloader and app logs on UART0, Hello world! and the countdown, then reboots through the RTC watchdog as silicon does. The README puts this at about 30× faster than real time. Resets restart the machine from the ROM with the right reset cause. The ROM ELF comes from an ESP-IDF install, or from tools/fetch-demo-assets.sh when ESP-IDF is absent.

Board models turn the SoC's pin-level events (GPIO edges, RMT symbol streams, I2S samples) into devices. The S3 boards are atech14 (default), waveshare-lcd4b (ST7701S 480×480 display, GT911 touch), waveshare-amoled18-v2 (CO5300 368x448 over QSPI, CST820 touch), waveshare-cam and none, a bare module with console only. The waveshare-cam board runs the waveshare-autopling firmware end to end (IDF 5.5, 16 MB flash, 8 MB octal PSRAM): camera frames, from an image file or a browser picture upload or webcam, go to an esp-dl pedestrian detector running on the emulated PIE SIMD unit, and then a pling plays on the ES8311 codec. The Pocket Synth firmware behind the atech14 demos is a separate project, atech-firmware, a PlatformIO build of a SID synth and a cRSID player for real .sid tunes; the repo carries its built images.

The README backs fidelity with hardware comparisons. Against a physical ESP32-C3 module, 205 of 208 console lines were identical over three boot cycles; the difference is the ROM's Saved PC line. Against a Waveshare ESP32-C6-LCD-1.47, 203 of 204 lines were identical over three boot cycles, and the one difference is that same line on the first boot. Flags such as --mac, --reset-cause and --strap let a run adopt a board's identity so the comparison is meaningful. The C3 notes point to docs/esp32c3.md for the five emulator bugs the hardware found.

Networking is a headline feature. With --wifi, a virtual access point lets the unmodified Espressif WiFi blob scan, authenticate and associate, including the WPA2-PSK four-way handshake when a passphrase is given. Behind it sits a virtual network with DHCP, ARP, ICMP, DNS and SNTP off the host clock. The default --net nat relays the guest's TCP and UDP over ordinary host sockets, so firmware reaches the real network; --net none refuses it. No firmware changes, root or tun device are needed. One example run of an energy-panel firmware joins the network, takes a lease, syncs its clock, fetches two days of electricity prices over HTTPS and polls a real Home Assistant on the LAN. The S3, C3 and C6 all join the same access point. The C3 supports open and WPA2 virtual APs with DHCP and NAT but has no boards, and --boot app is unsupported there. The C6 model supports one station, legacy rates and no power save, and every C6 radio run needs --stub bb_init=0.

The C6 also has an 802.15.4 MAC that answers energy scans, so the board's LVGL spectrum-scanner firmware runs, and that MAC sends and receives frames with the timing of the air. With --cooja the C6 becomes an external mote of Cooja-NG, driven in exact lock-step over NDJSON; an unmodified Contiki-NG-on-IDF image exchanged broadcasts with emulated MSP430 nodes.

Key facts

  • Esp32sim is an MIT-licensed, instruction-level emulator of four ESP32 SoCs across Xtensa and RISC-V, with no cloud or accounts.
  • Each chip boots the real mask ROM, the real 2nd-stage bootloader and an unmodified application image, with no patched firmware and no stubs.
  • A no-install browser demo runs Linux on the S3, the Touch-LCD-4B panel with its SID player, a pocket-tank LLM aquarium, and the C3 and C6 demos.
  • Console output matched real hardware on 205 of 208 lines (ESP32-C3) and 203 of 204 lines (ESP32-C6) over three boot cycles; the README says this is not uninterrupted cycle-by-cycle agreement.
  • A virtual WiFi access point lets unmodified Espressif WiFi firmware join, and with --net nat the guest's TCP and UDP reach the real network through host sockets.

Why it matters

Firmware for ESP32 chips normally needs a physical board to test. Esp32sim runs the actual chip boot path in software: the mask ROM, then the bootloader, then the unmodified application. The README says nothing is patched or stubbed along the way. It covers the S3, C3, C6 and classic ESP32, and it models displays, touch, audio and a camera for several Waveshare and LilyGO boards. The browser demo means a visitor can see this working without installing anything.

Who it affects

Embedded developers working with Espressif's ESP-IDF and the S3, C3, C6 or classic ESP32 are the obvious audience. So are people building firmware for the supported boards (atech14, the three Waveshare boards, the TTGO T-Display, and the C6 LCD board), and anyone testing WiFi-connected firmware against a real network, or experimenting with Contiki-NG and 802.15.4 through the Cooja-NG link on the C6.

How to use it

The project is MIT-licensed and needs no cloud or accounts. The quickest route is the browser demo at https://mikroverk.github.io/esp32sim/. To run locally, build with cargo build --release and launch the esp32sim binary (or esp32sim-c3 or esp32sim-c6, which are alias binaries) with --boot rom, a bootloader, a partition table and an app image; examples/hello_world is the starting point. The S3 ROM ELF is picked up from an ESP-IDF install, or tools/fetch-demo-assets.sh --no-linux fetches the S3, C3 and C6 ROMs. Options include --board, --wifi, --net, --trace, --break, --watch, --peek and --disasm, and a script file can press buttons and turn encoders at set times.

How solid is it

The evidence is the README's own hardware comparisons: 205 of 208 console lines identical to a physical C3 module and 203 of 204 to a Waveshare ESP32-C6-LCD-1.47, each over three boot cycles. The README states that these full-listing and hardware counts describe earlier manual runs. CI runs only checked-in sampled decoder corpora and emulator golden outputs and excludes the external full-listing tests, and the Xtensa objdump test excludes PIE operand comparisons. The hardware trace comparison normalizes window exceptions and resynchronizes delay loops. The ~30× real-time figure is for the S3 hello_world run on the command line. The source offered here is the README, and it is cut off partway through the WiFi section.

Risks and caveats

The README does not claim cycle accuracy; it says the hardware trace comparison is not uninterrupted cycle-by-cycle agreement. The C3 has no board models and does not support --boot app, and its docs record five emulator bugs the hardware comparison found. C6 WiFi is limited to one station, legacy rates and no power save, and every C6 radio run needs --stub bb_init=0. Running locally depends on ROM files from ESP-IDF or the fetch script. The visible text gives no browser performance figures and does not say the classic ESP32 runs in the browser demo.

“No cloud, no accounts. MIT.”

— esp32sim README