//! Heltec WiFi LoRa 32 V3 — native USB (requires hardware mod). //! //! Stock V3 boards route USB through a CP2102 bridge to UART. To use native //! USB CDC-ACM, solder R29 (D+) and R3 (D-) on the PCB and disconnect the //! CP2102 from the USB data lines. See the project README for details. //! //! For stock (unmodified) V3 boards, use the `heltec_v3_uart` feature instead. use esp_hal::gpio::{Level, Output, OutputConfig}; use static_cell::StaticCell; use super::esp32s3; use super::traits::{BoardParts, LoRaBoard}; use crate::driver::simple_led::SimpleLed; use crate::hal::esp32s3 as mcu; pub use super::esp32s3::{create_display, DisplayDriver, DisplayParts, RadioDriver, RadioParts}; pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>; pub type LedDriver = SimpleLed>; pub struct UsbParts { pub driver: UsbDriver, } pub struct Board { p: esp_hal::peripherals::Peripherals, } impl LoRaBoard for Board { const NAME: &'static str = "Heltec V3"; const TX_POWER_RANGE: (i8, i8) = (-9, 22); type RadioParts = RadioParts; type CommParts = UsbParts; type DisplayParts = DisplayParts; type DisplayDriver = DisplayDriver; type LedDriver = LedDriver; fn init() -> Self { let p = esp_hal::init(esp_hal::Config::default()); Self { p } } fn mac_address() -> [u8; 6] { mcu::mac_address() } fn into_parts(self) -> BoardParts { let p = self.p; mcu::start_timer(p.TIMG0, p.SW_INTERRUPT); // Vext power: GPIO36, active LOW to enable peripherals. // Output has no Drop impl, so the pin stays configured for program lifetime. let _vext = Output::new(p.GPIO36, Level::Low, OutputConfig::default()); let spi_bus = mcu::init_spi(p.SPI2, p.DMA_CH0, p.GPIO9, p.GPIO10, p.GPIO11); let radio = esp32s3::init_radio( spi_bus, p.GPIO8, p.GPIO12, p.GPIO14, p.GPIO13, lora_phy::sx126x::TcxoCtrlVoltage::Ctrl1V8, ); // Note: switches internal USB PHY from Serial-JTAG to OTG. let usb_inst = esp_hal::otg_fs::Usb::new(p.USB0, p.GPIO20, p.GPIO19); static EP_OUT_BUF: StaticCell<[u8; 1024]> = StaticCell::new(); let ep_out_buf = EP_OUT_BUF.init([0u8; 1024]); let host = UsbParts { driver: esp_hal::otg_fs::asynch::Driver::new( usb_inst, ep_out_buf, esp_hal::otg_fs::asynch::Config::default(), ), }; // SSD1306 display reset: pulse GPIO21 low->high before I2C init. // Output has no Drop impl; pin stays HIGH after this block. let mut display_rst = Output::new(p.GPIO21, Level::Low, OutputConfig::default()); esp_hal::delay::Delay::new().delay_millis(10); display_rst.set_high(); esp_hal::delay::Delay::new().delay_millis(10); let i2c = mcu::init_i2c(p.I2C0, p.GPIO17, p.GPIO18); let display = Some(DisplayParts { i2c }); // White LED on GPIO35 let led_pin = Output::new(p.GPIO35, Level::Low, OutputConfig::default()); let led = SimpleLed(led_pin); BoardParts { radio, host, display, led, mac: Self::mac_address(), } } }