Rust on ESP32

I recently got my hands on an ESP32 microcontroller for hacking purpose and successfully ran Rust code on it 🦀

Installing the tools

First, you need to download and install the prebuilt binaries of the Rust and LLVM compiler forks by Espressif or follow yesterday's guide about how to compile Rust and LLVM for ESP32 (warning: Even if Rust and LLVM compile successfully, it's today not possible to build a Rust program for ESP32 on Raspberry Pi due to https://github.com/esp-rs/esp-idf-sys/issues/14, but flashing works).

First, you need to install the auxiliary tools:

$ cargo install -f ldproxy espflash espmonitor

Then:

$ rustup default esp

Our Rust program

Here is the simplest "Hello world" you can have. As you may have noticed, the std lib is available :)

main.rs

use embedded_svc::anyerror::*;
use esp_idf_hal::prelude::*;
use esp_idf_svc::sysloop::*;
use std::{thread, time::Duration};

fn main() -> anyhow::Result<()> {
    loop {
        println!("Hello world");
        thread::sleep(Duration::from_secs(1));
    }
}

build.rs (from https://github.com/ivmarkov/rust-esp32-std-hello)

use std::path::PathBuf;

use embuild::{
    self, bingen,
    build::{CfgArgs, LinkArgs},
    cargo, symgen,
};

// Necessary because of this issue: https://github.com/rust-lang/cargo/issues/9641
fn main() -> anyhow::Result<()> {
    LinkArgs::output_propagated("ESP_IDF")?;

    let cfg = CfgArgs::try_from_env("ESP_IDF")?;

    if cfg.get("esp32s2").is_some() {
        let ulp_elf = PathBuf::from("ulp").join("rust-esp32-ulp-hello");
        symgen::run(&ulp_elf, 0x5000_0000)?; // This is where the RTC Slow Mem is mapped within the ESP32-S2 memory space
        bingen::run(&ulp_elf)?;

        cargo::track_file(ulp_elf);
    }

    cfg.output();

    Ok(())
}

Cargo.toml

[package]
name = "rust_on_esp32"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

# No xtensa in regular compiler yet
[package.metadata.docs.rs]
default-target = "x86_64-unknown-linux-gnu"

[profile.release]
# symbols are nice and they don't increase the size on Flash
debug = true
opt-level = "z"

[profile.dev]
opt-level = "s"

[features]
bind = []

[dependencies]
anyhow = {version = "1", features = ["backtrace"]}
esp-idf-sys = { version = "0.20" }
embedded-svc = "0.8.3"
esp-idf-svc = { version = "0.20", features = ["binstart"] }
esp-idf-hal = "0.20"

[build-dependencies]
embuild = "0.24"
anyhow = "1"

.cargo/config.toml (from https://github.com/ivmarkov/rust-esp32-std-hello)

[build]
target = "xtensa-esp32-espidf"

[target.xtensa-esp32-espidf]
linker = "ldproxy"

[unstable]
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]
configurable-env = true # No longer necessary since 1.56, as it was stabilized: https://github.com/rust-lang/cargo/blob/master/src/cargo/core/features.rs#L698
extra-link-arg = true   # No longer necessary since 1.56, as it was stabilized: https://github.com/rust-lang/cargo/blob/master/src/cargo/core/features.rs#L695

[env]
ESP_IDF_SYS_GLOB_BASE = { value = ".", relative = true }

ESP_IDF_SYS_GLOB_0 = { value = "/sdkconfig.release" }
ESP_IDF_SYS_GLOB_1 = { value = "/sdkconfig.debug" }
ESP_IDF_SYS_GLOB_2 = { value = "/sdkconfig.defaults" }

sdkconfig.defaults (from https://github.com/ivmarkov/rust-esp32-std-hello)

CONFIG_LWIP_L2_TO_L3_COPY=y
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y
#CONFIG_ESP_SYSTEM_USE_EH_FRAME=y
#CONFIG_COMPILER_CXX_EXCEPTIONS=y

Compiling the Rust program for ESP32

Compiling it is as simple as running:

$ cargo build # or cargo build --release

Flashing the ESP32

Thanks to espflash (which just reached v1.0 🎉) flashing our binary to an ESP32 board is easy:

Press the BOOT button on the ESP32, then:

$ espflash /dev/ttyUSB0 target/xtensa-esp32-espidf/debug/rust_on_esp32 # or target/xtensa-esp32-espidf/release/rust_on_esp32

Monitoring

Finally, getting logs can be achieved with espmonitor that we previously installed:

$ espmonitor --speed 115200 /dev/ttyUSB0

Getting Wi-Fi to work

You can find a more advanced and complex example (from which this Hello-World is derived) with Wi-Fi and a web server on GitHub: https://github.com/ivmarkov/rust-esp32-std-hello

Want to learn more?

Explore the esp-rs GitHub organization and Join the https://matrix.to/#/#esp-rs:matrix.org matrix channel.

The code is on GitHub

As usual, you can find the code on GitHub: github.com/skerkour/kerkour.com

1 email / week to learn how to (ab)use technology for fun & profit: Programming, Hacking & Entrepreneurship.
I hate spam even more than you do. I'll never share your email, and you can unsubscribe at any time.

Tags: programming, rust, tutorial, esp32

Want to learn Rust, Cryptography and Security? Get my book Black Hat Rust!