From 7a54c4dff3c332a443c42f3d77349796b78cd10b Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Tue, 7 Apr 2026 09:45:08 -0600 Subject: [PATCH] refactor: board-owned display init, zero cfg in application code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Each board now exports DisplayDriver type and create_display() async fn - ESP32-S3 boards share SSD1306 create_display in board/esp32s3.rs - RAK WisBlock has its own SSD1306 create_display (different I2C type) - Wio Tracker has SH1106 create_display (different display IC) - Removed all cfg_if from display.rs — calls board::create_display() - Removed display init retry logic (single attempt, return None on failure) - ZERO board-specific #[cfg(feature)] outside board/, host/mod.rs, and hal/ --- src/board/esp32s3.rs | 28 +++++++++++++++++++ src/board/heltec_v3.rs | 5 +++- src/board/heltec_v4.rs | 5 +++- src/board/rak_wisblock_4631.rs | 25 +++++++++++++++++ src/board/wio_tracker_l1.rs | 14 ++++++++++ src/display.rs | 50 +++------------------------------- 6 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/board/esp32s3.rs b/src/board/esp32s3.rs index 5026991..dfb6d59 100644 --- a/src/board/esp32s3.rs +++ b/src/board/esp32s3.rs @@ -47,6 +47,34 @@ pub struct DisplayParts { pub mac: [u8; 6], } +// ── Display driver ────────────────────────────────────────────────── + +pub type DisplayDriver = ssd1306::Ssd1306Async< + ssd1306::prelude::I2CInterface, + ssd1306::size::DisplaySize128x64, + ssd1306::mode::BufferedGraphicsModeAsync, +>; + +/// Construct and initialize an SSD1306 display from raw I2C. +pub async fn create_display(i2c: DisplayI2c) -> Option { + use ssd1306::mode::DisplayConfigAsync; + use ssd1306::prelude::{Brightness, DisplayRotation}; + use ssd1306::size::DisplaySize128x64; + use ssd1306::{I2CDisplayInterface, Ssd1306Async}; + + let interface = I2CDisplayInterface::new(i2c); + let mut display = Ssd1306Async::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) + .into_buffered_graphics_mode(); + + embassy_time::Timer::after_millis(100).await; + if display.init().await.is_err() { + defmt::error!("SSD1306 display init failed"); + return None; + } + let _ = display.set_brightness(Brightness::BRIGHTEST).await; + Some(display) +} + // ── Shared peripheral init helpers ────────────────────────────────── /// Construct SX1262 radio from an initialized SPI bus. diff --git a/src/board/heltec_v3.rs b/src/board/heltec_v3.rs index 4c089a8..8b71726 100644 --- a/src/board/heltec_v3.rs +++ b/src/board/heltec_v3.rs @@ -5,7 +5,10 @@ use super::traits::LoRaBoard; use crate::hal::esp32s3 as mcu; #[allow(unused_imports)] // Re-exported for other modules (display, radio, uart tasks) -pub use super::esp32s3::{DisplayI2c, DisplayParts, RadioDriver, RadioParts, UartDriver, UartParts}; +pub use super::esp32s3::{ + create_display, DisplayDriver, DisplayI2c, DisplayParts, RadioDriver, RadioParts, UartDriver, + UartParts, +}; // ── Board init ────────────────────────────────────────────────────── diff --git a/src/board/heltec_v4.rs b/src/board/heltec_v4.rs index c1872e3..607e90b 100644 --- a/src/board/heltec_v4.rs +++ b/src/board/heltec_v4.rs @@ -5,7 +5,10 @@ use super::traits::LoRaBoard; use crate::hal::esp32s3 as mcu; #[allow(unused_imports)] // Re-exported for other modules (display, radio, usb tasks) -pub use super::esp32s3::{DisplayI2c, DisplayParts, RadioDriver, RadioParts, UsbDriver, UsbParts}; +pub use super::esp32s3::{ + create_display, DisplayDriver, DisplayI2c, DisplayParts, RadioDriver, RadioParts, UsbDriver, + UsbParts, +}; // ── Board implementation ──────────────────────────────────────────── diff --git a/src/board/rak_wisblock_4631.rs b/src/board/rak_wisblock_4631.rs index 5dc937e..e9808fe 100644 --- a/src/board/rak_wisblock_4631.rs +++ b/src/board/rak_wisblock_4631.rs @@ -21,6 +21,31 @@ pub type RadioDriver = Sx126x; pub type UsbDriver = mcu::UsbNrfDriver; pub type DisplayI2c = mcu::I2cBus; +pub type DisplayDriver = ssd1306::Ssd1306Async< + ssd1306::prelude::I2CInterface, + ssd1306::size::DisplaySize128x64, + ssd1306::mode::BufferedGraphicsModeAsync, +>; + +pub async fn create_display(i2c: DisplayI2c) -> Option { + use ssd1306::mode::DisplayConfigAsync; + use ssd1306::prelude::{Brightness, DisplayRotation}; + use ssd1306::size::DisplaySize128x64; + use ssd1306::{I2CDisplayInterface, Ssd1306Async}; + + let interface = I2CDisplayInterface::new(i2c); + let mut display = Ssd1306Async::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) + .into_buffered_graphics_mode(); + + embassy_time::Timer::after_millis(100).await; + if display.init().await.is_err() { + defmt::error!("SSD1306 display init failed"); + return None; + } + let _ = display.set_brightness(Brightness::BRIGHTEST).await; + Some(display) +} + // ── Peripheral bundles ────────────────────────────────────────────── pub struct RadioParts { diff --git a/src/board/wio_tracker_l1.rs b/src/board/wio_tracker_l1.rs index 464c901..b1ac908 100644 --- a/src/board/wio_tracker_l1.rs +++ b/src/board/wio_tracker_l1.rs @@ -21,6 +21,20 @@ pub type RadioDriver = Sx126x; pub type UsbDriver = mcu::UsbNrfDriver; pub type DisplayI2c = mcu::I2cBus; +pub type DisplayDriver = crate::driver::sh1106::Sh1106; + +pub async fn create_display(i2c: DisplayI2c) -> Option { + let mut display = crate::driver::sh1106::Sh1106::new(i2c, 0x3D); + + embassy_time::Timer::after_millis(100).await; + if display.init().await.is_err() { + defmt::error!("SH1106 display init failed"); + return None; + } + let _ = display.set_brightness(0xFF).await; + Some(display) +} + // ── Peripheral bundles ────────────────────────────────────────────── pub struct RadioParts { diff --git a/src/display.rs b/src/display.rs index 4a73561..998daa3 100644 --- a/src/display.rs +++ b/src/display.rs @@ -123,52 +123,10 @@ pub async fn display_task( mac: &mac_str, }; - cfg_if::cfg_if! { - if #[cfg(feature = "wio_tracker_l1")] { - let mut display = crate::driver::sh1106::Sh1106::new(parts.i2c, 0x3D); - - Timer::after_millis(100).await; - - if display.init().await.is_err() { - defmt::error!("SH1106 init failed — retrying"); - Timer::after_millis(500).await; - if display.init().await.is_err() { - defmt::error!("SH1106 init failed twice, giving up"); - return; - } - } - if display.set_brightness(0xFF).await.is_err() { - defmt::warn!("display brightness set failed"); - } - } else { - use ssd1306::mode::DisplayConfigAsync; - use ssd1306::prelude::DisplayRotation; - use ssd1306::size::DisplaySize128x64; - use ssd1306::{I2CDisplayInterface, Ssd1306Async}; - - let interface = I2CDisplayInterface::new(parts.i2c); - let mut display = Ssd1306Async::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) - .into_buffered_graphics_mode(); - - Timer::after_millis(100).await; - - if display.init().await.is_err() { - defmt::error!("SSD1306 init failed — retrying"); - Timer::after_millis(500).await; - if display.init().await.is_err() { - defmt::error!("SSD1306 init failed twice, giving up"); - return; - } - } - if display - .set_brightness(ssd1306::prelude::Brightness::BRIGHTEST) - .await - .is_err() - { - defmt::warn!("display brightness set failed"); - } - } - } + let Some(mut display) = crate::board::create_display(parts.i2c).await else { + defmt::error!("display init failed, giving up"); + return; + }; let mut state = DisplayState::new(); let Some(mut receiver) = status.receiver() else {