Introduction

I have started to experiment with Rust on the ESP32. The ESP32 is a low-cost microcontroller with integrated Wi-Fi and Bluetooth. It is a very interesting platform for IoT projects. I have already used it with MicroPython. But I want to try Rust on it now since I’m a big fan of this language.

The ESP32 family of processors is developed by Espressif. They provide many Rust crates, a lot of documentation and a book. Their support for Rust is really great.

image

For this project I choose a tiny ESP32c6 development board from UICPAL (could not find a website). It has a USB-C connector, Wi-Fi and Bluetooth, two buttons and a WS2812 RGB LED. The processor is a Risc-V processor.

Toolchain

As a first step I want to control the LED with Rust. First you have to install all required tools. You need Rust, obviously. I use the rustup tool to install and manage Rust. The installation of the toolchain for the ESP32 is described here. For this example I use the no_std toolchain for the ESP32c6 family. Here the tl;dr version:

1
2
rustup toolchain install nightly --component rust-src
rustup target add riscv32imac-unknown-none-elf

It also makes sense to add some Cargo tools:

1
2
cargo install cargo-generate
cargo install cargo-espflash

Running the project

You can find the source code of this example here. To build and run it on the ESP32 you can use the following commands:

1
cargo espflash flash --bin esp32-rgb-led --release --monitor

The output should be similar to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[2024-01-12T20:01:18Z INFO ] Serial port: '/dev/ttyACM0'
[2024-01-12T20:01:18Z INFO ] Connecting...
[2024-01-12T20:01:19Z INFO ] Using flash stub
    Finished release [optimized] target(s) in 0.07s
Chip type:         esp32c6 (revision v0.0)
Crystal frequency: 40MHz
Flash size:        4MB
Features:          WiFi 6, BT 5
MAC address:       ...
App/part. size:    43,264/4,128,768 bytes, 1.05%
[00:00:00] [========================================]      13/13      0x0                                                                                                                                          [00:00:00] [========================================]       1/1       0x8000                                                                                                                                       [00:00:00] [========================================]      24/24      0x10000                                                                                                                                      [2024-01-12T20:01:20Z INFO ] Flashing has completed!
Commands:
    CTRL+R    Reset chip
    CTRL+C    Exit

ESP-ROM:esp32c6-20220919
Build:Sep 19 2022
rst:0x15 (USB_UART_HPSYS),boot:0xd (SPI_FAST_FLASH_BOOT)
Saved PC:0x408007fe
0x408007fe - __EXTERNAL_INTERRUPTS
    at ??:??
SPIWP:0xee
mode:DIO, clock div:2
load:0x4086c410,len:0xd48
0x4086c410 - _ZN14esp_hal_common4uart6asynch9RX_WAKERS17h1ee90c3b075bf5caE
    at ??:??
load:0x4086e610,len:0x2d68
0x4086e610 - _ZN14esp_hal_common4uart6asynch9RX_WAKERS17h1ee90c3b075bf5caE
    at ??:??
load:0x40875720,len:0x1800
0x40875720 - _ZN14esp_hal_common4uart6asynch9RX_WAKERS17h1ee90c3b075bf5caE
    at ??:??
SHA-256 comparison failed:
Calculated: aff89878a96cbff57c66c38aa6d1a422b7785040b5efea582babef6c357427c2
Expected: 0af544a033ab3492852b8232c904c578d5f07c9d4a423a64473f060db374ab32
Attempting to boot anyway...
entry 0x4086c410
0x4086c410 - _ZN14esp_hal_common4uart6asynch9RX_WAKERS17h1ee90c3b075bf5caE
    at ??:??
I (41) boot: ESP-IDF v5.1-beta1-378-gea5e0ff298-dirt 2nd stage bootloader
I (42) boot: compile time Jun  7 2023 08:02:08
I (43) boot: chip revision: v0.0
I (46) boot.esp32c6: SPI Speed      : 40MHz
I (51) boot.esp32c6: SPI Mode       : DIO
I (56) boot.esp32c6: SPI Flash Size : 4MB
I (60) boot: Enabling RNG early entropy source...
I (66) boot: Partition Table:
I (69) boot: ## Label            Usage          Type ST Offset   Length
I (77) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (84) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (92) boot:  2 factory          factory app      00 00 00010000 003f0000
I (99) boot: End of partition table
I (103) esp_image: segment 0: paddr=00010020 vaddr=42000020 size=0721ch ( 29212) map
I (118) esp_image: segment 1: paddr=00017244 vaddr=40800000 size=00014h (    20) load
I (120) esp_image: segment 2: paddr=00017260 vaddr=42007260 size=02644h (  9796) map
I (131) esp_image: segment 3: paddr=000198ac vaddr=40800014 size=01024h (  4132) load
I (139) boot: Loaded app from partition at offset 0x10000
I (143) boot: Disabling RNG early entropy source...
Hello world!

Besides the “Hello world!” output you should see the RGB LED changing its color. The –monitor option opens a serial monitor. This is very useful for debugging. In this case it is used for the famous “Hello world!” output.

Background information

For more information about these WS2812 LEDs see here. This kind of LED is also available as a strip with many LEDs. You can realize some nice effects with them. Controlling them is a bit tricky though. They require a more or less precise timing. So normally you use some hardware timer source for that. In case of the ESP32 you can use the RMT peripheral for that.

It was a bit tricky to get the LED working. First I tried to use the ws2812 crate. But it did not work. Finally, I got it working with the esp-hal-smartled crate.

For the rainbow effect it is very handy to use HSV colors. Doing so you can easily change the hue value and the LED changes its color. But for the WS2812 LEDs you have to convert the HSV color to RGB. First, I implemented the HSV to RGB conversion by myself. But then I found the palette crate which provides this and supports no_std.

In general, you have to take care to choose crates that support no_std and do not use the esp-idf. Many of the examples I found were for the ESP32 with the esp-idf.