Rust Embassy: Difference between revisions
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,
};
}
}