Rust on ESP32: From Blinky to Async Wi-Fi
A step-by-step guide to writing Rust firmware for the ESP32 using the Embassy async runtime — covering toolchain setup, GPIO, serial output, and a working async Wi-Fi client that connects to a network and fetches a URL.
🚧 Work in Progress — This post is a placeholder. Full content coming soon.
The ESP32 is arguably the most popular microcontroller for hobbyist and prototyping work — dual-core Xtensa LX6, Wi-Fi, Bluetooth, cheap, and abundant. Rust support has matured rapidly in 2024–2025. The esp-hal crate and Embassy async runtime now make it possible to write safe, async Wi-Fi firmware without touching the Espressif SDK (ESP-IDF) at all. This post walks through the journey from a blinking LED to an async HTTP request.
Planned Content
1. The ESP32 Rust Ecosystem
- Two approaches:
std(ESP-IDF bindings) vsno_std(bare metal withesp-hal) - Why
no_std+ Embassy is the more interesting path - Supported chips: ESP32, ESP32-S3, ESP32-C3 (RISC-V), ESP32-C6
2. Toolchain Setup
1
2
3
cargo install espup
espup install # installs Xtensa Rust fork + LLVM
cargo install cargo-espflash
espflashfor flashing over USB-UARTprobe-rsfor JTAG debugging (ESP32-S3 only)
3. Blinky: Hello World in esp-hal
esp-halHAL structure- Output GPIO with type-state API
- Using
esp-printlnfor UART debug output - Understanding the boot process: ROM bootloader → app
4. Introducing Embassy
- Cooperative async executor for embedded
#[embassy_executor::task]macroTimer::after_millis()— async delay without blocking- Porting the Blinky to async
5. Async Wi-Fi with esp-wifi
1
2
3
4
let wifi = Wifi::new(peripherals.WIFI, &mut rng, &clocks);
let (mut controller, interfaces) = create_network_interface(wifi);
controller.start().await.unwrap();
controller.connect(&config).await.unwrap();
- Scanning for networks
- Connecting with WPA2
- Getting an IP address via DHCP
- Making an HTTP GET request with
reqwless
6. Real-World Patterns
- Sharing the Wi-Fi stack between tasks with
embassy-syncchannels - Deep sleep and wake-on-timer for battery-powered sensors
- OTA firmware updates via Wi-Fi
Hardware Used
- ESP32-S3-DevKitC-1 (preferred: native USB, JTAG support)
- Or any generic ESP32 DevKit with CP2102 USB-UART
Previous in series: Getting Started with Embedded Rust on ARM Cortex-M (coming soon)