Skip to content

Commit

Permalink
improve examples
Browse files Browse the repository at this point in the history
  • Loading branch information
felixwrt committed Jun 21, 2024
1 parent f838196 commit 2746044
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 43 deletions.
7 changes: 4 additions & 3 deletions examples/embedded/esp32c3-power-meter-mock/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions examples/embedded/esp32c3-power-meter-mock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
[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
9 changes: 7 additions & 2 deletions examples/embedded/esp32c3-sml-reader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
70 changes: 63 additions & 7 deletions examples/embedded/esp32c3-sml-reader/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,4 +43,37 @@ Flash and run the example:

```
cargo run --relase
```
```

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
117 changes: 92 additions & 25 deletions examples/embedded/esp32c3-sml-reader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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]
Expand All @@ -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()
Expand All @@ -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;
Expand All @@ -98,19 +135,26 @@ fn read_blocking(led: &mut AnyOutput, pin: &mut impl embedded_io::Read) -> ! {

#[allow(unused)]
fn read_polling<PIN: embedded_io::Read + embedded_io::ReadReady>(
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;
Expand All @@ -123,6 +167,29 @@ fn read_polling<PIN: embedded_io::Read + embedded_io::ReadReady>(
}
}
}
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<S>(led: &mut S, level: Level)
where
S: smart_leds::SmartLedsWrite<Color = RGB<u8>>,
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)
}

0 comments on commit 2746044

Please sign in to comment.