Programming ESP32 microcontrollers in Rust without the standard library
So you want to program bare-metal Rust on microcontrollers? One option you could try are ESP32 microcontrollers. The experience is far from perfect, but it’s getting there and there is much to learn.
This is an overview of the setup and the tools used for no-std Rust development on ESP32 microcontrollers and will be updated if anything changes so if anything goes wrong, raise an issue or contact me on Mastodon.
The ESP32-family are excellent microcontrollers for many applications with a WI-FI/BLE Chip Built-in. They come in many different form factors, are commonly used by hobbyists for a wide variety of projects with some being smaller than your average thumb drive like this ESP32C3 that surprisingly still works:
And thanks to Espressif developers we have the ability to write Rust code for it. Either with Espressif’s ESP-IDF Framework running on FreeRTOS or bare-metal, which is arguably way cooler(but also more dangerous).
Espressif has many microcontrollers. There is the ESP8266, which has a hal, but it is in maintenance mode, so I would recommend strongly against it. There is also no wifi-support for the HAL(Hardware-Abstraction-Layer). However, you can of course fork it and make it suitable for your project.
The ESP32 family is a bit more expensive, but offer some compelling features like faster processors or even multiple processors or Thread/Zigbee/Matter support, which seems like a technology that will be highly influential in the IoT space. Here is a list of the ones I’ve tried:
|Notes||a bit older but very common||most common RISC-V model||second CPU is a low-power CPU, very recently released||low-power CPU, also recently released||has some boards with camera|
Personally I mostly use CLion with the Rust plugin(or in the future probably RustRover). This works fine for my use-case, but if you prefer a free alternative for developing Rust for the ESP32 I’d recommend VSCode(or VSCodium if you prefer the freer alternative) with rust-analyzer.
If your ESP32 has a Xtensa CPU(see the microcontrollers table) you need to install
espup first, which is similar to
rustup, but is for managing your Xtensa Rust toolchain and custom LLVM, which will not be necessary anymore once the changes are merged upstream.
To install it run:
cargo install espup espup install . ~/export-esp.sh
To create a new Rust project usually you just run
cargo new --bin my-project and then go from there. This however proves difficult due to the dependencies changing interfaces quite often and finding compatible versions is finicky(guess who wasted hours on that). Instead, I would recommend using esp-template:
cargo install cargo-generate
cargo generate esp-rs/esp-template
String; Dev Containers, which will set up the development environment automatically inside VSCode; Files for Continuous Integration via GitHub Actions; Wokwi Support(see Wokwi) and logging via the
Then to flash your project you need to install
cargo install espflash
On Linux add your current user to the
dialout group via(This may vary from distro to distro):
sudo usermod -a -G dialout $USER
Log out and back in, and you should be good to flash your first application directly onto microcontroller by running this in your projects’ directory:
If this fails during flashing, try holding the boot button on your device and run the command again.
And you flashed your first Rust code onto your ESP32. Congratulations!
Wokwi is a really cool website allowing you to replay your projects(if all your hardware is implemented for Wokwi) completely in the browser. Some features may cost something in the future, but I just found it incredibly helpful in the past. It also has a plugin for VScode, essentially doing the same but right in your IDE.
Finding dependencies usable in no-std environments is not easy. Many of the ones you may be used to using in std environments cannot be used here and commonly library authors don’t mention it in their READMEs. Here are a few tips on how to find them when it is not written in the Readme:
#![no_std]attribute in the crate’s
Most times you will then find the feature flag to enable no_std support this way.
If you are looking for references on how to do certain things(like using an Analog Digital Converter) in this environment you will probably find that resources are really scarce. But here are some I would recommend:
The things I personally am missing from these are probably more complex projects.
A really exciting possibility with this is bare-metal async via embassy. It is still in development, but it is already quite usable. I would highly encourage you to try it if you are interested in async programming on microcontrollers, although there are some constraints like no generics in tasks.
no-std development in Rust as of now is tough. You may not always get proper stacktraces and debugging support without using a debugger (even if there is one on the board). Here is my usual workflow on troubleshooting these issues:
If you want an overview of new features in the last few months in esp-rs you should check out Scott Mabins Blog for updates.
Here is the current repository digest for esp-hal, which houses most of the no-std development I’m interested in:
This is something I struggled with a lot while getting into microcontroller programming using Rust since esp-rs was far less practical and no guides were written down, so I decided to write a comprehensive guide. I hope it helped you and provides you with some resources on how to get help. If there’s anything I missed, don’t hesitate to give me a heads-up!