Experiments with Embassy, the embedded async Rust framework

Published on: 2024-12-01

Home | Archive

Embassy is a framework for writing embedded async Rust applications for microcontrollers. Most of us have heard of (or used) async Rust in the context of complex back-end applications, so it is very interesting to see that this programming model is actually productive (and fun) in a totally different context!

Let me note down a few things I have been trying with Embassy for the past one or two weeks here.

Some resources for getting started

Embassy has excellent support for ARM cortex microcontrollers, as well as RISC-V based processors from Espressif. All the experiments described here were done on an ESP32-C3-DevkitC-02.

Get started by reading the Embassy book

The Embassy github repo has a large number of example programs, check out this link for Raspberry Pi rp2040 examples.

If you are looking for ESP32 example programs, check out the esp-hal repo.

Hello, world

Check out https://git.sr.ht/~pcein/rust-embassy-esp32-demo for a small program that shows how a momentary push-button switch can be used to control the blinking speed of an LED.

We have two tasks here, one blinks the LED and the other (the main task) monitors the button. The two tasks communicate using an atomic integer variable:

static BLINK_DELAY_MS: AtomicU32 = AtomicU32::new(SLOW_BLINK_DELAY_MS);

For a simple example like this, the ability to spawn multiple tasks may not be very interesting; but for non-trivial embedded applications especially those that communicate over the network and interact with large number of I/O devices, sensors etc, this can be a good way to structure the code.

LED remote control

Let’s now look at something a bit more involved. Here is a simple method to control the RGB LED on the esp32c3 remotely - send messages to a telegram bot running on say a Raspberry Pi. The esp32c3 (attached to the same network) receives messages from the telegram bot and uses those message to control the LED.

Here is the link to the telegram bot code. It uses the teloxide Rust crate. You can basically send two messages to the bot: blink and stop. The bot stores the last received message in memory; the esp32c3 code periodically polls the bot for these messages.

ESP32C3 code

The esp32c3 code is structured as 4 independent tasks:

1. wifi_connection_task
2. net_task
3. led_task
4. command_receiver_task

The wifi connection task takes care of establishing the wifi link; it continuously watches for disconnects and tries to reconnect.

The net task runs the network stack which is based on the popular smoltcp TCP/IP stack for embedded systems.

The led task and the command receiver task implement the application logic.

These tasks communicate over a channel:

static COMMAND_CHANNEL: channel::Channel<CriticalSectionRawMutex, u8, 1> = channel::Channel::new();

The command receiver task pings the telegram bot for commands once every few seconds and sends the received command out over the channel.

The led task waits for a “start blinking” message, and once the message is received, runs a loop (with a fixed number of iterations) putting the Green LED ON and OFF.

Every iteration of the loop, the channel is checked to see if a “stop blinking” message has been received; the try_receive call will either receive a message from the channel immediately or return an error if channel is empty.

loop {
    while command_receiver.receive().await != Command::BlinkGreenLed as u8 {
        // wait till a "start blinking" command is received
    }
    // Start blinking. Check for a "stop blinking" command
    // each iteration of the loop.
    for _n in 0..BLINK_CYCLES {
        if let Ok(r) = command_receiver.try_receive() {
            if r == Command::StopBlinking as u8 {
                break;
             }
         }
         rmt_channel.transmit(&green).await.unwrap();
         Timer::after(Duration::from_millis(BLINK_DELAY_MS)).await;
         rmt_channel.transmit(&off).await.unwrap();
         Timer::after(Duration::from_millis(BLINK_DELAY_MS)).await;
    }
}

The RGB LED on the ESP32C3 devkit is controlled using the Remote Control Transceiver peripheral which lets us easily generate the digital signals for the neopixel-style LED.

Next step

I am planning to check out ESP-NOW, esp-now lets us do peer-to-peer communication between esp32 devices without a wifi network and access point. I have one or two small gadget ideas in mind which should be a good fit for this model!