From 2746044af03250f27aefd0aa7f9dbc7a596b6396 Mon Sep 17 00:00:00 2001 From: Felix Wirth Date: Fri, 21 Jun 2024 11:46:18 +0200 Subject: [PATCH] improve examples --- .../esp32c3-power-meter-mock/Cargo.toml | 7 +- .../esp32c3-power-meter-mock/README.md | 12 +- .../embedded/esp32c3-sml-reader/Cargo.toml | 9 +- .../embedded/esp32c3-sml-reader/README.md | 70 +++++++++-- .../embedded/esp32c3-sml-reader/src/main.rs | 117 ++++++++++++++---- 5 files changed, 172 insertions(+), 43 deletions(-) diff --git a/examples/embedded/esp32c3-power-meter-mock/Cargo.toml b/examples/embedded/esp32c3-power-meter-mock/Cargo.toml index 8845207..57fb518 100644 --- a/examples/embedded/esp32c3-power-meter-mock/Cargo.toml +++ b/examples/embedded/esp32c3-power-meter-mock/Cargo.toml @@ -21,14 +21,15 @@ esp-println = { version = "0.9.1", features = ["esp32c3", "log"] } hex-literal = "0.4.1" log = { version = "0.4.21" } smart-leds = { version = "0.4.0", optional = true } + +[features] +smart-led = ["dep:smart-leds", "dep:esp-hal-smartled"] + [profile.dev] # Rust debug is too slow. # For debug builds always builds with some optimization opt-level = "s" -[features] -smart-led = ["dep:smart-leds", "dep:esp-hal-smartled"] - [profile.release] codegen-units = 1 # LLVM can perform better optimizations using a single thread debug = 2 diff --git a/examples/embedded/esp32c3-power-meter-mock/README.md b/examples/embedded/esp32c3-power-meter-mock/README.md index 550dfdb..6921735 100644 --- a/examples/embedded/esp32c3-power-meter-mock/README.md +++ b/examples/embedded/esp32c3-power-meter-mock/README.md @@ -2,7 +2,7 @@ This is a simple program for the ESP32-C3 that continuously sends sml messages via UART. -The example is used to mock a digital german power meter and test the [`sml-rs`](https://github.com/felixwrt/sml-rs) library. +The example is used to mock a digital german power meter and test the [`sml-rs`][1] library. It sends an sml message approximately every second and turns an LED on while sending the data. @@ -18,7 +18,7 @@ You can adapt the pin configuration in the source code (see the comment block "P ### Led configuration -This project can use either a smart RGB LED (such as the one found on the [ESP32-C3-DevKitC-02][1]) or a simple LED that can +This project can use either a smart RGB LED (such as the one found on the [ESP32-C3-DevKitC-02][2]) or a simple LED that can be driven by setting the output to high / low. By default, this project assumes a regular LED. Activate the `smart-led` feature to use a smart RGB LED: @@ -29,7 +29,7 @@ cargo ... --features smart-led ## Usage -Install [`espflash`](https://github.com/esp-rs/espflash/tree/main/espflash): +Install [`espflash`][3]: ``` cargo install espflash @@ -47,6 +47,6 @@ When using a smart RGB LED: cargo run --relase --features smart-led ``` - - -[1]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitc-02.html \ No newline at end of file +[1]: https://github.com/felixwrt/sml-rs +[2]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitc-02.html +[3]: https://github.com/esp-rs/espflash/tree/main/espflash \ No newline at end of file diff --git a/examples/embedded/esp32c3-sml-reader/Cargo.toml b/examples/embedded/esp32c3-sml-reader/Cargo.toml index 454bbc4..61af7d2 100644 --- a/examples/embedded/esp32c3-sml-reader/Cargo.toml +++ b/examples/embedded/esp32c3-sml-reader/Cargo.toml @@ -16,11 +16,16 @@ esp-backtrace = { version = "0.12.0", features = [ "println", ] } esp-hal = { version = "0.18.0", features = ["embedded-io", "esp32c3"] } -esp-hal-smartled = { version = "0.11.0", features = ["esp32c3"] } +esp-hal-smartled = { version = "0.11.0", features = ["esp32c3"], optional = true} esp-println = { version = "0.9.1", features = ["esp32c3", "log"] } log = { version = "0.4.21" } -smart-leds = "0.4.0" +smart-leds = { version = "0.4.0", optional = true } sml-rs = { version = "0.4.0", default-features = false } + +[features] +smart-led = ["dep:smart-leds", "dep:esp-hal-smartled"] +polling = [] + [profile.dev] # Rust debug is too slow. # For debug builds always builds with some optimization diff --git a/examples/embedded/esp32c3-sml-reader/README.md b/examples/embedded/esp32c3-sml-reader/README.md index 4dc73ce..1551405 100644 --- a/examples/embedded/esp32c3-sml-reader/README.md +++ b/examples/embedded/esp32c3-sml-reader/README.md @@ -1,16 +1,39 @@ # esp32c3 SML Reader -This is a simple program for the ESP32-C3 that reads sml messages from a pin. +This is a simple program for the ESP32-C3 that reads sml messages from a UART pin. -The project shows how to use the [`sml-rs`](https://github.com/felixwrt/sml-rs) library in an embedded context. +The example shows how to use the [`sml-rs`][1] library in an embedded context. -Pins: -- GPIO 9: Data is read from this pin (RX). -- GPIO 18: LED indicating when data is being received. +It receives data from a UART pin, tries to parse it as an sml message and prints the result on the terminal. +Additionally, it toggles an LED whenever a byte is received on the UART pin. + +Note: also take a look at the [`esp32c3-power-meter-mock`](../esp32c3-power-meter-mock/) example which can be used to generate +sml messages that can be consumed by this example. + +## Configuration + +By default, the following pins are used: + +- GPIO 10: UART TX pin (unused, but needs to be provided to the UART peripheral) +- GPIO 9: UART RX pin used to receive data +- GPIO 8: LED pin (see below) + +You can adapt the pin configuration in the source code (see the comment block "Pin configuration"). + +### Led configuration + +This project can use either a smart RGB LED (such as the one found on the [ESP32-C3-DevKitC-02][2]) or a simple LED that can +be driven by setting the output to high / low. + +By default, this project assumes a regular LED. Activate the `smart-led` feature to use a smart RGB LED: + +``` +cargo ... --features smart-led +``` ## Usage -Install [`espflash`](https://github.com/esp-rs/espflash/tree/main/espflash): +Install [`espflash`][3]: ``` cargo install espflash @@ -20,4 +43,37 @@ Flash and run the example: ``` cargo run --relase -``` \ No newline at end of file +``` + +When using a smart RGB LED: + +``` +cargo run --relase --features smart-led +``` + +### Polling mode + +The example contains two working modes: blocking (default) and polling. In the blocking mode, the implementation +continuously tries to read from the UART pin, which blocks if no data is available currently. This is simple to implement, but +also means that it's not possible to concurrently work on several tasks within the main loop. + +In the polling mode, the implementation first checks whether data is available and only reads from the pin if that is the case. +This allows doing additional work in the main loop. The example prints a message to the terminal every 5 seconds. + +Note: also take a look at the [`esp32c3-sml-reader-async`](../esp32c3-sml-reader-async/) example which uses `embassy` and `async/await` to enable multitasking. + +The polling mode can be activated using the `polling` feature: + +``` +cargo run --relase --features polling +``` + +Or, when using a smart RGB LED: + +``` +cargo run --relase --features smart-led,polling +``` + +[1]: https://github.com/felixwrt/sml-rs +[2]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitc-02.html +[3]: https://github.com/esp-rs/espflash/tree/main/espflash \ No newline at end of file diff --git a/examples/embedded/esp32c3-sml-reader/src/main.rs b/examples/embedded/esp32c3-sml-reader/src/main.rs index 430bbbc..3c73ad4 100644 --- a/examples/embedded/esp32c3-sml-reader/src/main.rs +++ b/examples/embedded/esp32c3-sml-reader/src/main.rs @@ -4,10 +4,12 @@ use core::slice; use esp_backtrace as _; +#[cfg(not(feature = "smart-led"))] +use esp_hal::gpio::AnyOutput; use esp_hal::{ clock::ClockControl, delay::Delay, - gpio::{AnyOutput, Io, Level}, + gpio::{Io, Level}, peripherals::Peripherals, prelude::*, system::SystemControl, @@ -17,6 +19,13 @@ use esp_hal::{ }, }; +#[cfg(feature = "smart-led")] +use esp_hal::rmt::Rmt; +#[cfg(feature = "smart-led")] +use esp_hal_smartled::{smartLedBuffer, SmartLedsAdapter}; +#[cfg(feature = "smart-led")] +use smart_leds::RGB; + use sml_rs::{transport::Decoder, util::ArrayBuf}; #[entry] @@ -28,14 +37,34 @@ fn main() -> ! { let delay = Delay::new(&clocks); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // LED - let mut led = AnyOutput::new(io.pins.gpio18, Level::High); - - // UART PORT - let tx_pin = io.pins.gpio1; + // ----------------------------------------------------------------------- + // Pin configuration - adapt to your board + // ----------------------------------------------------------------------- + let led_pin = io.pins.gpio8; + // tx is unused, but needs to be provided for UART + let tx_pin = io.pins.gpio10; let rx_pin = io.pins.gpio9; - // let pins = TxRxPins::new_tx_rx(tx_pin, rx_pin); + // ----------------------------------------------------------------------- + // ----------------------------------------------------------------------- + // ----------------------------------------------------------------------- + + // Init logging + esp_println::logger::init_logger_from_env(); + + // LED Configuration + let mut led; + #[cfg(feature = "smart-led")] + { + let rmt = Rmt::new(peripherals.RMT, 80.MHz(), &clocks, None).unwrap(); + let rmt_buffer = smartLedBuffer!(1); + led = SmartLedsAdapter::new(rmt.channel0, led_pin, rmt_buffer, &clocks); + } + #[cfg(not(feature = "smart-led"))] + { + led = AnyOutput::new(led_pin, Level::High); + } + // UART Configuration let uart_config = Config::default() .baudrate(9600) .parity_none() @@ -50,38 +79,46 @@ fn main() -> ! { ) .unwrap(); - esp_println::logger::init_logger_from_env(); + // Closure toggling the LED + let mut led_state = false; + let mut toggle_led = || { + led_state = !led_state; + led_write(&mut led, led_state.into()); + }; + // Test LED log::info!("Testing LED..."); - for _ in 0..4 { - led.set_high(); - delay.delay(200.millis()); - led.set_low(); + for _ in 0..8 { + toggle_led(); delay.delay(200.millis()); } log::info!("Reading SML messages..."); - // read_blocking(&mut led, &mut uart1) - // Not currently implemented in esp-hal, see https://github.com/esp-rs/esp-hal/issues/1620 - read_polling(&mut led, &mut uart1) + #[cfg(not(feature = "polling"))] + { + read_blocking(&mut uart1, toggle_led) + } + #[cfg(feature = "polling")] + { + read_polling(&mut uart1, toggle_led) + } } #[allow(unused)] -fn read_blocking(led: &mut AnyOutput, pin: &mut impl embedded_io::Read) -> ! { +fn read_blocking(pin: &mut impl embedded_io::Read, mut toggle_led: impl FnMut()) -> ! { let buf = ArrayBuf::<4069>::default(); let mut decoder = Decoder::from_buf(buf); - let mut led_toggle = false; - loop { - let mut b = 0u8; // read byte from the pin + let mut b = 0u8; pin.read(slice::from_mut(&mut b)).unwrap(); - led.toggle(); - led_toggle = !led_toggle; + // toggle the LED + toggle_led(); + // process the read byte match decoder.push_byte(b) { Ok(None) => { continue; @@ -98,19 +135,26 @@ fn read_blocking(led: &mut AnyOutput, pin: &mut impl embedded_io::Read) -> ! { #[allow(unused)] fn read_polling( - led: &mut AnyOutput, pin: &mut PIN, + mut toggle_led: impl FnMut(), ) -> ! { let buf = ArrayBuf::<4069>::default(); let mut decoder = Decoder::from_buf(buf); + let mut last_print_time = 0; + + // main loop loop { + // reading only when data is available (non-blocking) if pin.read_ready().unwrap() { - let mut b = 0u8; // read byte from the pin - + let mut b = 0u8; pin.read(slice::from_mut(&mut b)).unwrap(); + // toggle the LED + toggle_led(); + + // process the read byte match decoder.push_byte(b) { Ok(None) => { continue; @@ -123,6 +167,29 @@ fn read_polling( } } } - led.toggle(); + + // print a message every 5 seconds + let mut secs_since_start = esp_hal::time::current_time().duration_since_epoch().to_secs(); + if secs_since_start >= last_print_time + 5 { + last_print_time = secs_since_start; + log::info!("Hello from the print task!"); + } } } + +#[cfg(feature = "smart-led")] +fn led_write(led: &mut S, level: Level) +where + S: smart_leds::SmartLedsWrite>, + S::Error: core::fmt::Debug, +{ + let color = match level { + Level::High => RGB::new(0, 0, 2), + Level::Low => RGB::new(0, 0, 0), + }; + led.write([color].into_iter()).unwrap() +} +#[cfg(not(feature = "smart-led"))] +fn led_write(led: &mut AnyOutput, level: Level) { + led.set_level(level) +}