use esp_idf_svc::{eventloop::EspSystemEventLoop, hal::{gpio::Pins, modem::Modem, prelude::Peripherals, rmt::RMT, task::thread::ThreadSpawnConfiguration}, nvs::EspDefaultNvsPartition, sys::esp_efuse_mac_get_default}; use rgb::Rgb; use crate::{buffers::{BufferedSurfacePool, Pixbuf}, mappings::StrideMapping, platform::{smart_leds_lib::{rmt::FastWs2812Esp32Rmt, StrideOutput}, Board}, task::FixedSizeScheduler}; use super::{mqtt::MqttTask, wifi::WifiTask}; pub struct Esp32Board { sys_loop: EspSystemEventLoop, modem: Option, pins: Option, rmt: Option, surfaces: Option, } impl Board for Esp32Board { type Output = StrideOutput<[Rgb; 310], FastWs2812Esp32Rmt<'static>>; type Surfaces = BufferedSurfacePool; type Scheduler = FixedSizeScheduler<2>; fn chip_id() -> u64 { let mut chip_id: [u8; 8] = [0; 8]; unsafe { esp_efuse_mac_get_default(&mut chip_id as *mut u8); } return u64::from_be_bytes(chip_id); } fn take() -> Self { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches(); // Bind the log crate to the ESP Logging facilities esp_idf_svc::log::EspLogger::initialize_default(); let peripherals = Peripherals::take().unwrap(); let sys_loop = EspSystemEventLoop::take().unwrap(); Esp32Board { modem: Some(peripherals.modem), sys_loop: sys_loop, surfaces: Some(BufferedSurfacePool::new()), pins: Some(peripherals.pins), rmt: Some(peripherals.rmt) } } fn output(&mut self) -> Self::Output { log::info!("Setting up output for chip ID {:x?}", Self::chip_id()); const POWER_VOLTS : u32 = 5; const POWER_MA : u32 = 500; const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; let pins = self.pins.take().unwrap(); let rmt = self.rmt.take().unwrap(); ThreadSpawnConfiguration { pin_to_core: Some(esp_idf_svc::hal::cpu::Core::Core1), ..Default::default() }.set().unwrap(); // Wifi driver creates too many interrupts on core0, so we need to use RMT on core1. // But the implementation spawns a thread based on the core the driver was created in, // so we create the driver in another thread briefly. // Fun stuff. let output = match Self::chip_id().to_be_bytes() { // panel test board [72, 202, 67, 89, 145, 204, 0, 0] => { StrideOutput::new( Pixbuf::new(), StrideMapping::new_panel(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), MAX_POWER_MW ) }, [0x8C, 0xAA, 0xB5, 0x83, 0x5f, 0x74, 0x0, 0x0] => { //ponderjar StrideOutput::new( Pixbuf::new(), StrideMapping::new_jar(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio14).unwrap() }).join().unwrap(), MAX_POWER_MW ) }, [0x4a, 0xca, 0x43, 0x59, 0x85, 0x58, 0x0, 0x0] => { // Albus the tree StrideOutput::new( Pixbuf::new(), StrideMapping::new_albus(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), POWER_VOLTS * 2_400 ) }, [0x48, 0xca, 0x43, 0x59, 0x9d, 0x48, 0x0, 0x0] => { // kitchen cabinets StrideOutput::new( Pixbuf::new(), StrideMapping::new_fairylights(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), MAX_POWER_MW ) }, [0x48, 0xca, 0x43, 0x59, 0x9e, 0xdc, 0x0, 0x0] => { // front window StrideOutput::new( Pixbuf::new(), StrideMapping::new_fairylights(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), MAX_POWER_MW ) }, [0xfc, 0xf5, 0xc4, 0x05, 0xb8, 0x30, 0x0, 0x0] => { // cyberplague StrideOutput::new( Pixbuf::new(), StrideMapping::new_cyberplague(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio13).unwrap() }).join().unwrap(), MAX_POWER_MW ) }, _ => { StrideOutput::new( Pixbuf::new(), StrideMapping::new(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), MAX_POWER_MW ) } }; ThreadSpawnConfiguration { ..Default::default() }.set().unwrap(); output } fn surfaces(&mut self) -> Self::Surfaces { self.surfaces.take().unwrap() } fn system_tasks(&mut self) -> Self::Scheduler { let nvs = EspDefaultNvsPartition::take().unwrap(); FixedSizeScheduler::new([ Box::new(WifiTask::new(self.modem.take().unwrap(), self.sys_loop.clone(), &nvs)), Box::new(MqttTask::new()) ]) } }