Overview
stm32f4-emu is an emulator for the STM32F407 (ARM Cortex-M4) microcontroller. It runs real compiled firmware — not a reimplementation — by pairing a CPU emulator with a cycle-accurate-enough model of the on-chip peripherals. You can boot a binary, watch its UART, poke GPIO and registers, and drive it from scripts, a web UI, or an MCP client.
The two halves are connected by MMIO memory hooks: every read/write the firmware makes to a peripheral register is intercepted and answered by the peripheral model, so the guest sees exactly what real silicon would present.
Architecture
- CPU — Unicorn 2.1.4 compiled to WASM, executing Thumb-2 instructions. Reset vector, SP/PC, and exception handling follow the Cortex-M programming model.
- Peripheral model — a Rust crate compiled to WASM via
wasm-bindgen. ~33 peripheral modules (RCC, GPIO, USART, TIM, NVIC/SysTick/EXTI, ETH+DMA, I2C, SPI/I2S, CAN, LTDC, DCMI, RTC, ADC, FLASH …) answerperiph_read/periph_write. - JS driver — registers the memory hooks, runs
emu_startin stepped batches, services ETH TX/RX, the interrupt pump, and virtual devices (displays, sensors, bus taps).
Memory-hook protocol: MMIO range → periph_read/write → model → value written back into guest memory.
See docs/architecture.md and docs/peripherals.md for the full design and a per-peripheral implementation matrix.
Featured firmwares
| Firmware | What it shows |
|---|---|
eth_http | DHCP → TCP handshake → HTTP GET/response, driven through a real gVisor network stack (or a canned netsim). The flagship networking demo. |
blinky | Bare-metal blink + UART banner; toggles PA5 so you can watch the GPIO panel update live. |
can_test | Two-node CAN bus: loopback TX, arbitration (lower ID wins), and filter-gated delivery. |
freertos_test | FreeRTOS scheduler: TIM3 ISR → xSemaphoreGiveFromISR → PendSV context switch to a higher-priority task. |
rtc_test | DS3231 I2C RTC: set time, read-back, temperature. |
oled_test / tft_test / buzzer_test / audio_play_test | Virtual peripherals over I2C/SPI/TIM/I2S (SSD1306, ILI9341, PWM, I2S capture). |
doom | doomgeneric F407 port — DOOM 1 shareware running in a Web Worker. Open doom.html. |
How to use
Headless CLI
# boot a firmware and stream its UART to stdout node cli.mjs firmware.bin --inst 20000000 # load an ELF, trace peripheral register accesses to stderr node cli.mjs app.elf --verbose node cli.mjs --help # usage + all flags node cli.mjs --version # version
Flags: --inst <N> instruction budget,
--format auto|bin|hex|elf, --verbose register
trace, --help, --version.
Browser console
# from the repo root python3 -m http.server 8123 --directory site # then open http://localhost:8123/index.html
Pick a firmware preset from the dropdown, click Run, and watch the UART terminal. The console also shows a live GPIO grid, key peripheral registers, device panels (OLED/TFT/buzzer/RTC), and — with a gateway URL — real Ethernet.
Node library API
// npm install stm32f4-emu (or use the local checkout) import { createSTM32F407 } from 'stm32f4-emu'; import { readFileSync } from 'node:fs'; const fw = new Uint8Array(readFileSync('firmware.bin')); const emu = await createSTM32F407({ firmware: fw }); for (let i = 0; i < 200; i++) { emu.step(100000); // run up to 100k instructions const uart = emu.drainUart(); if (uart.length) process.stdout.write(uart.toString()); } console.log('PC=', emu.read32(0x08000000 + 4).toString(16));
Key handle methods: step(n), run(n),
drainUart(), read32/write32/read16/write16/read8/write8,
close(). TypeScript types ship in index.d.ts.
MCP server
# optional peer dep: npm install @modelcontextprotocol/sdk zod npx stm32f4-mcp # speaks MCP over stdio
Exposes load/step/UART/pins/ADC/registers/memory/component tools to an MCP client (Claude Code, Claude Desktop). See docs/mcp.md.
Learn more
- docs/usage.md — CLI, browser, and library usage, config, env vars, building.
- docs/architecture.md — how the pieces fit together.
- docs/peripherals.md — every peripheral and how complete it is.
- docs/components.md — attach virtual LEDs/buttons/sensors to pins.
- docs/mcp.md — drive the emulator from an LLM via MCP.
- CHANGELOG.md — releases and features.