Rust Embassy: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
 
Line 69: Line 69:
         led.toggle();
         led.toggle();
         Timer::after(Duration::from_millis(330)).await;
         Timer::after(Duration::from_millis(330)).await;
    }
}
</syntaxhighlight>
=Async Uart=
So wanted to implement the example from [RTOS]. Setting up was really easy once I knew how
==Set up Uart Device==
First set up the device to be async
<syntaxhighlight lang="rs">
    // Configure Pins
    let led = Output::new(peripherals.GPIO27, Level::High);
    let (tx_pin, rx_pin) = (peripherals.GPIO17, peripherals.GPIO16);
    // Configure UART
    let uart2 = Uart::new_with_config(
        peripherals.UART2,
        Config::default().baudrate(9600),
        rx_pin,
        tx_pin,
    )
    .unwrap()
    .into_async();
    // Split UART into RX and TX
    let (rx, tx) = uart2.split();
</syntaxhighlight>
==Processing==
Next I create a queue to retrieve messages from the other task. Check if there is anything of the serial port, Send message to other task
<syntaxhighlight lang="rs">
// Task: command line interface (CLI)
#[embassy_executor::task]
async fn do_cli(mut rx: UartRx<'static, Async>, mut tx: UartTx<'static, Async>) {
    let cmd_len = COMMAND.len();
    // Loop forever
    loop {
        // Delay for a 20 milliseconds to give time to other task
        Timer::after(Duration::from_millis(20)).await;
        // See if there's a message in the queue (do not block)
        if let Ok(rcv_msg) = MSG_QUEUE.try_receive() {
            info!("Received message on msg_queue: {:?}", rcv_msg.body);
            if let Err(e) = embedded_io_async::Write::write(&mut tx, &rcv_msg.body).await {
                println!("Error writing to UART: {:?}", e);
            }
            // Apped linefeed to rcv_msg.count
            let mut count_buf = [0u8; 32];
            count_buf[0] = rcv_msg.count;
            count_buf[1] = b'\n';
            if let Err(e) = embedded_io_async::Write::write(&mut tx, &count_buf).await {
                println!("Error writing to UART: {:?}", e);
            }
        }
        let mut read_buffer: [u8; READ_BUF_SIZE] = [0u8; READ_BUF_SIZE];
        let read_ready = embedded_io_async::ReadReady::read_ready(&mut rx);
        match read_ready {
            Ok(result) => {
                if result == false {
                    // info!("Not Ready for read...");
                    Timer::after(Duration::from_millis(1000)).await;
                    continue;
                }
                info!("Ready for read...");
            }
            Err(e) => {
                info!("Error reading from UART: {:?}", e);
                continue;
            }
        }
        info!("Ready for read...");
        // Read characters from serial
        let read_result = embedded_io_async::Read::read(&mut rx, &mut read_buffer[0..]).await;
        info!("Finished reading...");
        // let read = uart.read_byte();
        match read_result {
            Ok(idx) => {
                if (idx == 0) {
                    continue;
                }
                // info!("received serial: {:?}", read_buffer);
                // Compare the first 6 characters to "delay "
                let cmd_buf = &read_buffer[..cmd_len];
                // info!("command buffer: {:?}", cmd_buf);
                if cmd_buf == COMMAND.as_bytes() {
                    // Extract everything after  COMMAND
                    let data_with_linefeed = &read_buffer[cmd_len..];
                    // info!("data with linefeed: {:?}", data_with_linefeed);
                    // Remove trailing newline
                    let data = data_with_linefeed
                        .split(|c| *c == b'\n' || *c == b'\r')
                        .next()
                        .unwrap();
                    let led_delay = core::str::from_utf8(data)
                        .unwrap()
                        .trim()
                        .parse::<u32>()
                        .unwrap_or(0);
                    // Send integer to other task via queue
                    info!("Sending message on delay_queue: {:?}", led_delay);
                    if DELAY_QUEUE.try_send(led_delay) != Ok(()) {
                        if let Err(e) = embedded_io_async::Write::write(
                            &mut tx,
                            b"ERROR: Could not put item on delay queue.",
                        )
                        .await
                        {
                            println!("Error writing to UART: {:?}", e);
                        }
                    }
                }
                // Flush transmit buffer
                embedded_io_async::Write::flush(&mut tx).await.unwrap();
            }
            Err(_) => continue,
        };
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 01:40, 8 January 2025

Introduction

After spending some time on RTOS I wanted to compare using Rust to do the same thing. Embassy seem more used than the non-std rust. In fact getting the bluetooth to work seem too hard

Getting Started

For the older wroom-32 boards, they use xtensa chips. For this we need to install the toolchain

cargo install espup
espup install # To install Espressif Rust ecosystem
. $HOME/export-esp.sh # This add stuff to the path

Once done we can create a project with

cargo install esp-generate
esp-generate --chip esp32 esp-blink

Template

The template provides this

#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use esp_backtrace as _;
use esp_hal::prelude::*;
use log::info;

#[main]
async fn main(spawner: Spawner) {
    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::max();
        config
    });

    esp_println::logger::init_logger_from_env();

    let timer0 = esp_hal::timer::timg::TimerGroup::new(peripherals.TIMG1);
    esp_hal_embassy::init(timer0.timer0);

    info!("Embassy initialized!");

    // TODO: Spawn some tasks
    let _ = spawner;

    loop {
        info!("Hello world!");
        Timer::after(Duration::from_secs(1)).await;
    }
}

Could not find and led on my board so I used pin 23.

Tasks

We need to create and executor which I assume is the same as the scheduler.

    static EXECUTOR: StaticCell<Executor> = StaticCell::new();
    let executor = EXECUTOR.init(Executor::new());

    executor.run(|spawner| {
        // Then add the tasks
        spawner.spawn(blink_me(led)).unwrap();
    });

I guess this is really easy once you know

#[embassy_executor::task]
async fn blink_me(mut led: Output<'static>) {
    loop {
        led.toggle();
        Timer::after(Duration::from_millis(330)).await;
    }
}

Async Uart

So wanted to implement the example from [RTOS]. Setting up was really easy once I knew how

Set up Uart Device

First set up the device to be async

     // Configure Pins
     let led = Output::new(peripherals.GPIO27, Level::High);
     let (tx_pin, rx_pin) = (peripherals.GPIO17, peripherals.GPIO16);
 
     // Configure UART
     let uart2 = Uart::new_with_config(
         peripherals.UART2,
         Config::default().baudrate(9600),
         rx_pin,
         tx_pin,
     )
     .unwrap()
     .into_async();
 
     // Split UART into RX and TX
     let (rx, tx) = uart2.split();

Processing

Next I create a queue to retrieve messages from the other task. Check if there is anything of the serial port, Send message to other task

// Task: command line interface (CLI)
#[embassy_executor::task]
async fn do_cli(mut rx: UartRx<'static, Async>, mut tx: UartTx<'static, Async>) {
    let cmd_len = COMMAND.len();

    // Loop forever
    loop {
        // Delay for a 20 milliseconds to give time to other task
        Timer::after(Duration::from_millis(20)).await;

        // See if there's a message in the queue (do not block)
        if let Ok(rcv_msg) = MSG_QUEUE.try_receive() {
            info!("Received message on msg_queue: {:?}", rcv_msg.body);
            if let Err(e) = embedded_io_async::Write::write(&mut tx, &rcv_msg.body).await {
                println!("Error writing to UART: {:?}", e);
            }
            // Apped linefeed to rcv_msg.count
            let mut count_buf = [0u8; 32];
            count_buf[0] = rcv_msg.count;
            count_buf[1] = b'\n';

            if let Err(e) = embedded_io_async::Write::write(&mut tx, &count_buf).await {
                println!("Error writing to UART: {:?}", e);
            }
        }

        let mut read_buffer: [u8; READ_BUF_SIZE] = [0u8; READ_BUF_SIZE];

        let read_ready = embedded_io_async::ReadReady::read_ready(&mut rx);
        match read_ready {
            Ok(result) => {
                if result == false {
                    // info!("Not Ready for read...");
                    Timer::after(Duration::from_millis(1000)).await;
                    continue;
                }
                info!("Ready for read...");
            }
            Err(e) => {
                info!("Error reading from UART: {:?}", e);
                continue;
            }
        }

        info!("Ready for read...");

        // Read characters from serial
        let read_result = embedded_io_async::Read::read(&mut rx, &mut read_buffer[0..]).await;

        info!("Finished reading...");
        // let read = uart.read_byte();
        match read_result {
            Ok(idx) => {
                if (idx == 0) {
                    continue;
                }
                // info!("received serial: {:?}", read_buffer);

                // Compare the first 6 characters to "delay "
                let cmd_buf = &read_buffer[..cmd_len];

                // info!("command buffer: {:?}", cmd_buf);

                if cmd_buf == COMMAND.as_bytes() {
                    // Extract everything after  COMMAND
                    let data_with_linefeed = &read_buffer[cmd_len..];
                    // info!("data with linefeed: {:?}", data_with_linefeed);

                    // Remove trailing newline
                    let data = data_with_linefeed
                        .split(|c| *c == b'\n' || *c == b'\r')
                        .next()
                        .unwrap();

                    let led_delay = core::str::from_utf8(data)
                        .unwrap()
                        .trim()
                        .parse::<u32>()
                        .unwrap_or(0);

                    // Send integer to other task via queue
                    info!("Sending message on delay_queue: {:?}", led_delay);
                    if DELAY_QUEUE.try_send(led_delay) != Ok(()) {
                        if let Err(e) = embedded_io_async::Write::write(
                            &mut tx,
                            b"ERROR: Could not put item on delay queue.",
                        )
                        .await
                        {
                            println!("Error writing to UART: {:?}", e);
                        }
                    }
                }
                // Flush transmit buffer
                embedded_io_async::Write::flush(&mut tx).await.unwrap();
            }
            Err(_) => continue,
        };
    }
}