Setting up a rust dev environment for esp32-c3
2024-03-10

intro

	I've recently decided to migrate to risc-v for my embedded hobby project, most notable of which is the abc-8. Setting it up a rust development environment was fairly straight forward, but there were some gotchas.

Despite recently migrating to nixos I see no reason to use flakes for it, in fact I found it overly complicated the project. Not to mention that very few people actually use nix, even less flakes. So I went with the trusty old podman / docker.

This is the most minimal setup I managed to create, it's possible I'll shave some more fat off as we go along but for now I'm quite happy with it. Beyond this you also need cargo-espflash (or just espflash) to flash the --release binary.

The only thing that might be a bit controversial is that I'm using the root account in the container. But I'm running podman rootless so it just maps 1:1 to my user account. If you use docker you might want to change it (or not).

Do note that this makes [~/.cargo, ~/.rustup, rust-target] ephemeral. You will likely want to mount them as docker volumes.

Dockerfile


FROM ubuntu:22.04

RUN apt-get update && apt full-upgrade -y && apt-get install -y \
    file \
    curl \
    clang \
    python3-pip \    
    python3-venv \ 
    git

RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

RUN rustup toolchain install nightly-2024-03-01 --component rust-src
RUN rustup target add riscv32imc-unknown-none-elf --toolchain nightly-2024-03-01
RUN cargo install ldproxy

ENV USER=root

Cargo.toml


[package]
name = "blinky"
version = "0.1.0"
edition = "2021"
authors = ["Anders Tonfeldt"]

[profile.release]
opt-level = "s" # optimize for binary size

[profile.dev]
debug = true
opt-level = "z"

[dependencies]
esp-idf-svc = { version="0.48", default-features=false, features=["std", "binstart"] }
esp-idf-hal = { version="0.43.1", default-features=false }
anyhow = { version="1.0.80" }

[build-dependencies]
embuild = { version="0.31.3", default-features=false }

.cargo/config.toml


[build]
target = "riscv32imc-esp-espidf"
target-dir = "/target"

[target.riscv32imc-esp-espidf]
linker = "ldproxy"
rustflags = [ "--cfg",  "espidf_time64"]

[unstable]
build-std = ["std", "panic_abort"]

[env]
MCU="esp32c3"
ESP_IDF_VERSION = "v5.1.2"

rust-toolchain.toml


[toolchain]
channel = "nightly-2024-03-01"
components = ["rust-src"]
targets = ["riscv32imc-unknown-none-elf"]

build.rs


fn main() {
    embuild::espidf::sysenv::output();
}

src/main.rs


//! Blinks an LED
//!
//! This assumes that a LED is connected to GPIO4.
//! Depending on your target and the board you are using you should change the pin.
//! If your board doesn't have on-board LEDs don't forget to add an appropriate resistor.
//!
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::gpio::*;
use esp_idf_hal::peripherals::Peripherals;

fn main() -> anyhow::Result<()> {
    esp_idf_hal::sys::link_patches();

    let peripherals = Peripherals::take()?;
    let mut led = PinDriver::output(peripherals.pins.gpio5)?;

    loop {
        led.set_high()?;
        // we are sleeping here to make sure the watchdog isn't triggered
        FreeRtos::delay_ms(1000);

        led.set_low()?;
        FreeRtos::delay_ms(1000);
    }
}