feat: RGB LED support — blinks green on RX, red on TX

- Added RgbLed trait with impl on () for boards without LED
- Added WS2812 driver (driver/ws2812.rs) using ESP32-S3 RMT peripheral
  - 80MHz clock, 12.5ns resolution, 24-bit GRB encoding
  - Zero extra crate dependencies
- Heltec V3/V3-UART/V4: WS2812B on GPIO38 via RMT channel 0
- RAK WisBlock/Wio Tracker: type LedDriver = (), led = None (no-op)
- Display task: green blink on RX packet, red blink on TX packet
- LED auto-off on timer tick (~1s), off when display is off/reset
- LoRaBoard trait gains type LedDriver: RgbLed associated type
- BoardParts gains led: Option<L> field
This commit is contained in:
swaits committed 2026-04-07 11:18:30 -06:00
1 parent 2b9e236f32
commit 5ac4b73739
11 files changed
+157 -20

No files matched your search

+22 -1
View File
@@ -1,8 +1,11 @@
use esp_hal::gpio::{Level, Output, OutputConfig};
use esp_hal::rmt::{Rmt, TxChannelConfig, TxChannelCreator};
use esp_hal::time::Rate;
use static_cell::StaticCell;
use super::esp32s3;
use super::traits::{BoardParts, LoRaBoard};
use crate::driver::ws2812::Ws2812;
use crate::hal::esp32s3 as mcu;
pub use super::esp32s3::{
@@ -11,6 +14,8 @@ pub use super::esp32s3::{
pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>;
pub type LedDriver = Ws2812;
pub struct UsbParts {
pub driver: UsbDriver,
}
@@ -27,6 +32,7 @@ impl LoRaBoard for Board {
type CommParts = UsbParts;
type DisplayParts = DisplayParts;
type DisplayDriver = DisplayDriver;
type LedDriver = Ws2812;
fn init() -> Self {
let p = esp_hal::init(esp_hal::Config::default());
@@ -37,7 +43,7 @@ impl LoRaBoard for Board {
mcu::mac_address()
}
fn into_parts(self) -> BoardParts<RadioParts, UsbParts, DisplayParts> {
fn into_parts(self) -> BoardParts<RadioParts, UsbParts, DisplayParts, Ws2812> {
let p = self.p;
mcu::start_timer(p.TIMG0);
@@ -71,10 +77,25 @@ impl LoRaBoard for Board {
let i2c = mcu::init_i2c(p.I2C0, p.GPIO17, p.GPIO18);
let display = Some(DisplayParts { i2c });
// WS2812B RGB LED on GPIO38 via RMT channel 0
let rmt = Rmt::new(p.RMT, Rate::from_mhz(80))
.expect("RMT init")
.into_async();
let led_channel = rmt.channel0
.configure_tx(
p.GPIO38,
TxChannelConfig::default()
.with_clk_divider(1)
.with_idle_output(true),
)
.expect("RMT TX channel init");
let led = Some(Ws2812::new(led_channel));
BoardParts {
radio,
host,
display,
led,
mac: Self::mac_address(),
}
}