From 239e72d0a8706fbd47069e6263f2be780d049391 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Tue, 7 Apr 2026 11:47:32 -0600 Subject: [PATCH] fix: prevent RMT peripheral drop on V4, log WS2812 errors The Rmt struct was being dropped after extracting channel0, which likely disabled the RMT peripheral. Use ManuallyDrop to keep it alive. Also log WS2812 transmit errors instead of silently discarding them. --- src/board/heltec_v4.rs | 17 ++++++++++++----- src/driver/ws2812.rs | 4 +++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/board/heltec_v4.rs b/src/board/heltec_v4.rs index 5603bea..ae52f31 100644 --- a/src/board/heltec_v4.rs +++ b/src/board/heltec_v4.rs @@ -77,11 +77,18 @@ 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 + // WS2812B RGB LED on GPIO38 via RMT channel 0. + // Wrap in ManuallyDrop so the Rmt peripheral isn't disabled when + // the struct is dropped after we take channel0. + let rmt = core::mem::ManuallyDrop::new( + Rmt::new(p.RMT, Rate::from_mhz(80)) + .expect("RMT init") + .into_async(), + ); + // SAFETY: we're reading channel0 out of the ManuallyDrop wrapper. + // The Rmt won't be dropped, so the peripheral stays enabled. + let channel0 = unsafe { core::ptr::read(&raw const rmt.channel0) }; + let led_channel = channel0 .configure_tx( p.GPIO38, TxChannelConfig::default() diff --git a/src/driver/ws2812.rs b/src/driver/ws2812.rs index 46dbb58..840c2a2 100644 --- a/src/driver/ws2812.rs +++ b/src/driver/ws2812.rs @@ -43,6 +43,8 @@ impl Ws2812 { impl RgbLed for Ws2812 { async fn set_rgb(&mut self, r: u8, g: u8, b: u8) { let data = Self::encode(r, g, b); - let _ = self.channel.transmit(&data).await; + if let Err(e) = self.channel.transmit(&data).await { + defmt::warn!("WS2812 transmit error: {}", e); + } } }