Embedded Rust on ESP32C3 Board, a Hands-on Quickstart Guide
Tags: embedded, rust, esp32, tutorial
Published: 2023-02-22 19:01:33

Today, I'll be showing you how to use the [Rust programming language](https://www.rust-lang.org) on a [Rust ESP board](https://github.com/esp-rs/esp-rust-board), a recent embedded platform packed with Wi-Fi and Bluetooth capabilities.

We won't go through the details of [`no_std` vs `std`](https://esp-rs.github.io/book/overview/index.html) development, just know that the latter allows us to use full Wi-Fi and Bluetooth capabilities along with the actual entire [Rust Standard Library](https://doc.rust-lang.org/std/index.html) with ease which is usually **NOT** straightforward but hey, this is 2023, enjoy!

The original article and associated examples are available in my [Rust ESP Quickstart](https://gitlab.com/cyril-marpaud/rust_esp_quickstart) GitLab repository.

## TL;DR

If you are already running Linux and just want to flash your ESP board with a fully configured Rust "Hello, World!" project, see the [`script/setup.sh` script](https://gitlab.com/cyril.marpaud/rust_esp_quickstart/-/blob/main/script/setup.sh), it should help you get started in no time. Be aware, though, that it has been made for a freshly installed 22.10 Ubuntu distribution and is untested on any other platform yet (i.e. use it at your own risks). On the other hand, please read on if you intend to walk the learning path instead.

## Requirements

Here's everything we are going to need during our little adventure:

- A computer with internet access
- A [Rust ESP Board](https://github.com/esp-rs/esp-rust-board) (this is open hardware so you can either buy one or build one yourself!)
- A USB cable
- An empty USB stick that we are going to erase completely so don't forget to **BACKUP YOUR DATA FIRST** (A 16GiB one is fine)
- Less than an hour of your time

## The OS

We'll be using a live 22.10 Ubuntu distribution so that:

- anyone with a decent computer can follow this guide
- our setups are the exact same, thus greatly reducing the rate of environment-related errors
- everything is instantly reversible, meaning that your computer will be left completely **untouched**
- we won't need to go through an actual Linux installation

One drawback of this solution is that each reboot will wipe the environment but feel free to actually install linux if persistence across said reboots is required.

### Making a bootable USB stick

- Download the 22.10 "Kinetic Kudu" Ubuntu ISO image [here](https://ubuntu.com/download/desktop/thank-you?version=22.10&architecture=amd64)
- Flash the USB stick using either [`usb-creator-gtk`](https://manpages.ubuntu.com/manpages/trusty/man8/usb-creator-gtk.8.html) if running Linux or [`Win32DiskImager`](https://win32diskimager.download/) if running Windows

#### Installing `usb-creator-gtk` on Linux

Open a terminal (the default shortcut is `Ctrl`+`Alt`+`T` on Ubuntu) and run those commands:

```bash
sudo apt update
sudo apt install --yes usb-creator-gtk
```

#### Installing `Win32DiskImager` on Windows

Download the installer from [the project's page](https://sourceforge.net/projects/win32diskimager/) and run it.

#### Flashing the USB stick

In both cases, we will need to select the ISO image we want to flash and the device we want to use then click the **Write** or **Make startup disk** button. The process should complete within a few minutes.

### Booting Ubuntu

Let's ask [ChatGPT](https://chat.openai.com) how to do that:

<div align="center">

![Booting from a USB stick](https://gitlab.com/cyril.marpaud/rust_esp_quickstart/-/raw/main/images/chatgpt_boot_from_usb.png "Booting from a USB stick")

</div>

Thank you ChatGPT ! Finally, hit that **Try Ubuntu** button to be greeted with a GNOME desktop.

The next steps require internet access so connect to a Wi-Fi (see the top-right corner menu) or wired network if you want to go old-school.

## The tools

Here, we are going to make **heavy** use of a terminal. On Ubuntu, you can open one by pressing `Ctrl`+`Alt`+`T`. You might want to copy/paste the following commands to avoid typos but suit yourself!

### Build dependencies

We need a few more packages than Ubuntu provides by default. Please bear with the trauma of installing them:

```bash
sudo add-apt-repository --yes universe
sudo apt install --yes clang curl git libudev-dev libssl-dev pkg-config python3-pip
```

### Rust and Cargo components

We'll now use [Rustup](https://rustup.rs) to install both [Rust](https://github.com/rust-lang/rust) and [Cargo](https://github.com/rust-lang/cargo) (Rust's package manager):

```bash
curl --proto '=https' --tlsv1.2 --fail --show-error --silent https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
rustup toolchain install nightly --component rust-src
```

Moreover, we need a few additional Cargo modules:

- `espflash` to flash the device (see [espflash](https://github.com/esp-rs/espflash))
- `ldproxy` to forward linker arguments (see [ldproxy](https://github.com/esp-rs/embuild/tree/f2cbbf9795676af52d2ffb53f102d70cac25116a/ldproxy))
- `cargo-generate` to generate projects according to a template (see [cargo-generate](https://github.com/cargo-generate/cargo-generate))

```bash
cargo install espflash ldproxy cargo-generate
```

## Generating a project

The awesome [ESP IDF Template](https://github.com/esp-rs/esp-idf-template) will save us the pain of configuring a fully functional project ourselves, use it like so:

```bash
cargo generate --git https://github.com/esp-rs/esp-idf-template cargo
```

During the process, you will be asked a few things:
- a **Project Name**. Go wild and use **test-project**, for instance
- an **ESP-IDF native build version**. Go with the **stable** one (it is **v4.4** at the moment)
- an **MCU**. Our board is equipped with an **ESP32C3**
- support for **dev containers**. We don't need that for now so that's a **false**
- STD support. We're building an std app so that's a **true**

<div align="center">

![Cargo generate answers](https://gitlab.com/cyril.marpaud/rust_esp_quickstart/-/raw/main/images/cargo_generate_answers.png "For those who feel like cheating")

</div>

## Hello, World!

### Getting access to the USB peripheral

You might like "Permission Denied" errors. If not, those commands will create a udev rule to avoid that when accessing our board through USB:

```bash
echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"303a\", ATTRS{idProduct}==\"1001\", MODE=\"0660\", GROUP=\"plugdev\"" | sudo tee /etc/udev/rules.d/99-esp-rust-board.rules > /dev/null
sudo udevadm control --reload-rules && sudo udevadm trigger
```

### Flash, run & monitor

Now make use of that USB cable and connect the board to your computer, then run this simple yet elegant command:

```bash
cargo run
```

If everything goes smoothly, Cargo should build the project, flash it on the MCU and finally run it, meaning that a line reading "Hello, World!" should be displayed in the terminal. Hit `Ctrl`+`R` to restart the program (not very useful at that point but it might be later) or `Ctrl`+`C` to quit monitoring it.

Congratulations, you are officially programming Rust on your ESP board !

## The IDE

We could configure Vim, Emacs or any other terminal-based IDE, but the low-friction solution is to use Visual Studio Code with the appropriate extensions. This way, we are only a few clicks away from syntax highlighting, code completion and much more.

### VSCodium

It is the exact same as VSCode but without telemetry/tracking stuff, plus it uses MIT license (see [the official website](https://vscodium.com)). Install and run it like so:

```bash
snap install codium --classic
codium
```

An **Open Folder** button should be visible in the explorer tab on the left. Click it to add our project to the workspace. We are now able to edit source code way more handily than in a terminal.

### Extensions

Go to the extensions tab and type "rust analyzer" in the search bar, then click the **install** button ("Rust language support for Visual Studio Code"). That's it! You can find more information about it on the [official website](https://rust-analyzer.github.io). The [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) and [crates](https://marketplace.visualstudio.com/items?itemName=serayuzgur.crates) extensions might also be of interest to you.

## Going further

The [examples folder](https://gitlab.com/cyril.marpaud/rust_esp_quickstart/-/tree/main/examples) of this repository contains three projects:
- **blink**: makes the LED connected to GPIO7 blink
- **i2c-sensors**: read and display data from the embedded SHTC3 & ICM42670 I2C sensors (temperature, humidity, accelerometer & gyroscope)
- **Wi-Fi**: very basic Wi-Fi connection + ping example, just uncomment and fill `WIFI_SSID` and `WIFI_PWD` environment variables at the bottom of `.cargo/config.toml`

## Conclusion

As a kid, I used to boot my sister's laptop from a live 8.04 "Hardy Heron" Ubuntu CD (you could order one for free back then!) to bypass her password protection. This way, no one ever knew what I was doing (meerely looking for cheat codes for the games I used to play at the time 😉️). Today, I still believe this technique is of great value to anyone wishing to try something out without breaking anything.

Growing up, I then had the pleasure to learn how embedded development can sometimes be a pain: messy setup, weird and abstruse error messages... But hopefully, it has been less than an hour since you began reading this article and you are now able to use a modern language with `std` support on a recent Wi-Fi-enabled microcontroller. Ain't that marvelous ?

I hope you enjoyed it as much as I do, especially when considering the tremendous amount of work that was needed to make this so easy for us.

Cheers!

## See also (aka useful links)

### Documentation

- [The Rust Language](https://www.rust-lang.org)
- [The Rust ESP Board](https://github.com/esp-rs/esp-rust-board)
- [Espressif's Rust-related repos](https://github.com/esp-rs)

### Crates

- [Board Support Package](https://github.com/esp-rs/esp-rust-board-bsp)
- [ESP32C3 `no_std` Hardware Abstraction Layer](https://docs.rs/esp32c3-hal/latest/esp32c3_hal/)
- [ESP32C3 `std` Hardware Abstraction Layer](https://esp-rs.github.io/esp-idf-hal/esp_idf_hal/)
- [`esp-template`: `no_std` project template](https://github.com/esp-rs/esp-template)
- [`esp-idf-template`: `std`-enabled project template](https://github.com/esp-rs/esp-idf-template)
- [`embedded-svc`: service-related traits](https://docs.rs/embedded-svc/latest/embedded_svc/)
- [`esp-idf-svc`: `embedded-svc` implementation](https://esp-rs.github.io/esp-idf-svc/esp_idf_svc/)
- [`shared-bus`: share I2C/SPI/ADC buses between peripherals (namely: sensors)](https://docs.rs/shared-bus/latest/shared_bus/)
- [`shtcx`: access the SHTC3 embedded temperature+humidity I2C sensor](https://docs.rs/shtcx/latest/shtcx/)
- [`icm42670`: access the ICM42670 embedded temperature+accelerometer+gyroscope I2C sensor](https://docs.rs/icm42670/latest/icm42670/)

### Examples

- [A complete STD demo on ESP32C3](https://github.com/ivmarkov/rust-esp32-std-demo)
- [A Rust ESP Board simulator (simple `no_std` blink project)](https://wokwi.com/projects/357177745915181057)

### Tutorials

- [Rust on ESP book](https://esp-rs.github.io/book/)
- [Embedded Rust on Espressif (Ferrous Systems training)](https://espressif-trainings.ferrous-systems.com)
- [Another quickstart guide](https://betterprogramming.pub/rust-for-iot-is-it-time-67b14ab34b8)

## Whoami

My name is [Cyril Marpaud, I'm an embedded systems freelance engineer and a Rust enthusiast 🦀](https://www.linkedin.com/in/cyrilmarpaud/) I have nearly 10 years experience and am currently living in Lyon (France).