ESP32-S3 + SX1262 + 1.3" SH1106 OLED + CP2102 UART bridge +
1000 mAh Li-Po. Pin map verified against Meshtastic's
variants/esp32s3/ELECROW-ThinkNode-M2. DIO3 TCXO at 3.3 V (vs 1.8 V
on Heltec), VEXT_ENABLE active HIGH (opposite polarity), dedicated
LORA_POWER_EN on GPIO48 sequenced before SPI init. Reuses the
existing async SH1106 driver at I2C address 0x3C.
Refactors hal::esp32s3::{init_spi,init_i2c} and board::esp32s3::init_radio
to accept generic GPIO bounds so ESP32-S3 boards with different pin
assignments can share the helpers. init_radio now takes a
TcxoCtrlVoltage parameter instead of hardcoding Ctrl1V8.
Incidental cleanups needed to make `just check-all` green:
- Replace the core::mem::forget(output) pattern with let _foo = ...;
current esp-hal makes Output non-Drop, so forget is a no-op that
trips clippy's forget_non_drop lint.
- Switch justfile xtensa builds from `cargo +nightly` with a RUSTC
override to `cargo +esp` so the compiler and rust-src versions stay
aligned. Public nightly's rust-src had drifted two months ahead of
the esp fork and broke -Zbuild-std on core.
110 lines
4.6 KiB
Rust
110 lines
4.6 KiB
Rust
//! Shared types and peripheral wiring for ESP32-S3 boards.
|
|
//!
|
|
//! Delegates MCU-level init to `hal::esp32s3` and defines peripheral-level
|
|
//! types (RadioDriver, display) used by ESP32-S3 board files.
|
|
//! Each board defines its own comm types (UsbParts/UartParts) directly.
|
|
|
|
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|
use embassy_time::Delay;
|
|
use esp_hal::gpio::{Input, InputConfig, InputPin, Level, Output, OutputConfig, OutputPin, Pull};
|
|
|
|
use lora_phy::iv::GenericSx126xInterfaceVariant;
|
|
use lora_phy::sx126x::{self, Sx1262, Sx126x};
|
|
|
|
use crate::hal::esp32s3 as mcu;
|
|
|
|
// ── Re-export MCU types ─────────────────────────────────────────────
|
|
|
|
pub use mcu::{I2cBus as DisplayI2c, SpiBus};
|
|
|
|
// ── Concrete peripheral types ───────────────────────────────────────
|
|
|
|
type Nss = Output<'static>;
|
|
type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
|
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
|
|
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
|
|
|
// ── Peripheral bundles ──────────────────────────────────────────────
|
|
|
|
pub struct RadioParts {
|
|
pub driver: RadioDriver,
|
|
pub delay: Delay,
|
|
}
|
|
|
|
// SSD1306 display support is Heltec-specific. ESP32-S3 boards that use a
|
|
// different controller (e.g. ThinkNode-M2 uses SH1106) provide their own
|
|
// DisplayParts / DisplayDriver / create_display directly.
|
|
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))]
|
|
pub struct DisplayParts {
|
|
pub i2c: DisplayI2c,
|
|
}
|
|
|
|
// ── Display driver ──────────────────────────────────────────────────
|
|
|
|
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))]
|
|
pub type DisplayDriver = ssd1306::Ssd1306Async<
|
|
ssd1306::prelude::I2CInterface<DisplayI2c>,
|
|
ssd1306::size::DisplaySize128x64,
|
|
ssd1306::mode::BufferedGraphicsModeAsync<ssd1306::size::DisplaySize128x64>,
|
|
>;
|
|
|
|
/// Construct and initialize an SSD1306 display from raw I2C.
|
|
#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))]
|
|
pub async fn create_display(i2c: DisplayI2c) -> Option<DisplayDriver> {
|
|
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.
|
|
///
|
|
/// Generic over GPIO type so boards with different radio pin assignments can
|
|
/// share this helper. `tcxo_voltage` selects the DIO3-driven TCXO supply:
|
|
/// Heltec boards use `Ctrl1V8`, the ELECROW ThinkNode-M2 uses `Ctrl3V3`, etc.
|
|
pub fn init_radio(
|
|
spi_bus: &'static embassy_sync::mutex::Mutex<NoopRawMutex, SpiBus>,
|
|
nss_pin: impl OutputPin + 'static,
|
|
reset_pin: impl OutputPin + 'static,
|
|
dio1_pin: impl InputPin + 'static,
|
|
busy_pin: impl InputPin + 'static,
|
|
tcxo_voltage: sx126x::TcxoCtrlVoltage,
|
|
) -> RadioParts {
|
|
let nss = Output::new(nss_pin, Level::High, OutputConfig::default());
|
|
let spi_device = SpiDevice::new(spi_bus, nss);
|
|
|
|
let reset = Output::new(reset_pin, Level::High, OutputConfig::default());
|
|
let dio1 = Input::new(dio1_pin, InputConfig::default().with_pull(Pull::Down));
|
|
let busy = Input::new(busy_pin, InputConfig::default().with_pull(Pull::Down));
|
|
|
|
let iv = GenericSx126xInterfaceVariant::new(reset, dio1, busy, None, None)
|
|
.expect("SX1262 interface init");
|
|
|
|
let sx_config = sx126x::Config {
|
|
chip: Sx1262,
|
|
tcxo_ctrl: Some(tcxo_voltage),
|
|
use_dcdc: true,
|
|
rx_boost: false,
|
|
};
|
|
|
|
RadioParts {
|
|
driver: Sx126x::new(spi_device, iv, sx_config),
|
|
delay: Delay,
|
|
}
|
|
}
|