From acd97966aabebb3562500c3244c13050f4e4ed1a Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Tue, 7 Apr 2026 11:28:17 -0600 Subject: [PATCH] fix: V3 has white LED on GPIO35, not WS2812B on GPIO38 The Heltec V3 has a plain white LED on GPIO35 (simple GPIO, not addressable). Only the V4 has a WS2812B RGB LED on GPIO38. - Created driver/simple_led.rs for single-color GPIO LEDs - V3/V3-UART boards: SimpleLed on GPIO35 (white blink on RX/TX) - V4 board: unchanged (WS2812 on GPIO38, green/red colors) - nRF boards: unchanged (no LED, type LedDriver = ()) --- firmware/src/board/heltec_v3.rs | 21 +++++++-------------- firmware/src/board/heltec_v3_uart.rs | 21 +++++++-------------- firmware/src/driver/mod.rs | 5 ++++- firmware/src/driver/simple_led.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 29 deletions(-) create mode 100644 firmware/src/driver/simple_led.rs diff --git a/firmware/src/board/heltec_v3.rs b/firmware/src/board/heltec_v3.rs index 9658cf60..dd803399 100644 --- a/firmware/src/board/heltec_v3.rs +++ b/firmware/src/board/heltec_v3.rs @@ -7,13 +7,11 @@ //! For stock (unmodified) V3 boards, use the `heltec_v3_uart` feature instead. 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::driver::simple_led::SimpleLed; use crate::hal::esp32s3 as mcu; pub use super::esp32s3::{ @@ -21,7 +19,7 @@ pub use super::esp32s3::{ }; pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>; -pub type LedDriver = Ws2812; +pub type LedDriver = SimpleLed; pub struct UsbParts { pub driver: UsbDriver, @@ -39,7 +37,7 @@ impl LoRaBoard for Board { type CommParts = UsbParts; type DisplayParts = DisplayParts; type DisplayDriver = DisplayDriver; - type LedDriver = Ws2812; + type LedDriver = LedDriver; fn init() -> Self { let p = esp_hal::init(esp_hal::Config::default()); @@ -50,7 +48,7 @@ impl LoRaBoard for Board { mcu::mac_address() } - fn into_parts(self) -> BoardParts { + fn into_parts(self) -> BoardParts { let p = self.p; mcu::start_timer(p.TIMG0); @@ -84,14 +82,9 @@ 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)); + // White LED on GPIO35 + let led_pin = Output::new(p.GPIO35, Level::Low, OutputConfig::default()); + let led = Some(SimpleLed::new(led_pin)); BoardParts { radio, diff --git a/firmware/src/board/heltec_v3_uart.rs b/firmware/src/board/heltec_v3_uart.rs index 6e6b4120..f45d4fcd 100644 --- a/firmware/src/board/heltec_v3_uart.rs +++ b/firmware/src/board/heltec_v3_uart.rs @@ -7,12 +7,10 @@ //! use the `heltec_v3` feature instead for native USB CDC-ACM support. use esp_hal::gpio::{Level, Output, OutputConfig}; -use esp_hal::rmt::{Rmt, TxChannelConfig, TxChannelCreator}; -use esp_hal::time::Rate; use super::esp32s3; use super::traits::{BoardParts, LoRaBoard}; -use crate::driver::ws2812::Ws2812; +use crate::driver::simple_led::SimpleLed; use crate::hal::esp32s3 as mcu; pub use super::esp32s3::{ @@ -20,7 +18,7 @@ pub use super::esp32s3::{ }; pub type UartDriver = esp_hal::uart::Uart<'static, esp_hal::Async>; -pub type LedDriver = Ws2812; +pub type LedDriver = SimpleLed; pub struct UartParts { pub driver: UartDriver, @@ -38,7 +36,7 @@ impl LoRaBoard for Board { type CommParts = UartParts; type DisplayParts = DisplayParts; type DisplayDriver = DisplayDriver; - type LedDriver = Ws2812; + type LedDriver = LedDriver; fn init() -> Self { let p = esp_hal::init(esp_hal::Config::default()); @@ -49,7 +47,7 @@ impl LoRaBoard for Board { mcu::mac_address() } - fn into_parts(self) -> BoardParts { + fn into_parts(self) -> BoardParts { let p = self.p; mcu::start_timer(p.TIMG0); @@ -80,14 +78,9 @@ 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)); + // White LED on GPIO35 + let led_pin = Output::new(p.GPIO35, Level::Low, OutputConfig::default()); + let led = Some(SimpleLed::new(led_pin)); BoardParts { radio, diff --git a/firmware/src/driver/mod.rs b/firmware/src/driver/mod.rs index 7caf7055..1e7b4971 100644 --- a/firmware/src/driver/mod.rs +++ b/firmware/src/driver/mod.rs @@ -1,5 +1,8 @@ #[cfg(feature = "wio_tracker_l1")] pub mod sh1106; -#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))] +#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart"))] +pub mod simple_led; + +#[cfg(feature = "heltec_v4")] pub mod ws2812; diff --git a/firmware/src/driver/simple_led.rs b/firmware/src/driver/simple_led.rs new file mode 100644 index 00000000..ca97882f --- /dev/null +++ b/firmware/src/driver/simple_led.rs @@ -0,0 +1,28 @@ +//! Simple single-color LED driver (GPIO on/off). +//! +//! For boards with a plain LED (not addressable RGB). Any non-zero +//! RGB value turns the LED on; all zeros turns it off. + +use esp_hal::gpio::Output; + +use crate::board::RgbLed; + +pub struct SimpleLed { + pin: Output<'static>, +} + +impl SimpleLed { + pub fn new(pin: Output<'static>) -> Self { + Self { pin } + } +} + +impl RgbLed for SimpleLed { + async fn set_rgb(&mut self, r: u8, g: u8, b: u8) { + if r > 0 || g > 0 || b > 0 { + self.pin.set_high(); + } else { + self.pin.set_low(); + } + } +}