use std::sync::Arc; use std::fmt::Debug; use std::sync::Mutex; use chrono::DateTime; use chrono::Utc; use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System}; use esp_idf_svc::hal::modem::Modem; use esp_idf_svc::hal::prelude::Peripherals; use esp_idf_svc::netif::IpEvent; use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault}; use esp_idf_svc::sntp::EspSntp; use esp_idf_svc::sys::esp_efuse_mac_get_default; use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent}; use rgb::Rgb; use super::Board; use crate::buffers::BufferedSurfacePool; use crate::task::FixedSizeScheduler; use crate::task::Task; use crate::buffers::Pixbuf; use crate::mappings::StrideMapping; use crate::platform::smart_leds_lib::StrideOutput; use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt; use crate::time::Periodically; pub struct Esp32Board { output: Option<::Output>, sys_loop: EspSystemEventLoop, modem: Option, surfaces: BufferedSurfacePool } impl Board for Esp32Board { type Output = StrideOutput<[Rgb; 310], FastWs2812Esp32Rmt<'static>>; type Surfaces = BufferedSurfacePool; type Scheduler = FixedSizeScheduler<2>; 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(); let channel = peripherals.rmt.channel0; let pins = peripherals.pins; let mut chip_id: [u8; 8] = [0; 8]; unsafe { esp_efuse_mac_get_default(&mut chip_id as *mut u8); } log::info!("Setting up for chip ID {:?}", chip_id); let (pixmap, target) = match chip_id { [72, 202, 67, 89, 145, 204, 0, 0] => { (StrideMapping::new_panel(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap()) }, [140, 170, 181, 131, 95, 116, 0, 0] => { (StrideMapping::new_jar(), FastWs2812Esp32Rmt::new(channel, pins.gpio14).unwrap()) }, _ => { (StrideMapping::new(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap()) } }; const POWER_VOLTS : u32 = 5; const POWER_MA : u32 = 500; const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; let output = StrideOutput::new( Pixbuf::new(), pixmap, target, MAX_POWER_MW ); Esp32Board { output: Some(output), modem: Some(peripherals.modem), sys_loop: sys_loop.clone(), surfaces: BufferedSurfacePool::new() } } fn output(&mut self) -> Self::Output { self.output.take().unwrap() } fn surfaces(&mut self) -> Self::Surfaces { self.surfaces.clone() } 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(self.surfaces.clone()) ]) } } impl Debug for WifiTask { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("WifiTask").finish() } } #[derive(Debug, PartialEq, Clone, Copy)] enum WifiState { Stopped, Disconnected, Connecting, Connected } struct WifiTask { wifi: EspWifi<'static>, ntp: EspSntp<'static>, connection_check: Periodically, state: Arc>, last_state: WifiState, sys_loop: EspSystemEventLoop, wifi_sub: Option>, ip_sub: Option> } impl WifiTask { fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition) -> Self { let mut wifi = EspWifi::new( modem, sys_loop.clone(), Some(nvs.clone()) ).unwrap(); let wifi_config = Configuration::Client(ClientConfiguration { ssid: "The Frequency".try_into().unwrap(), bssid: None, auth_method: AuthMethod::WPA2Personal, password: "thepasswordkenneth".try_into().unwrap(), channel: None, ..Default::default() }); wifi.set_configuration(&wifi_config).unwrap(); WifiTask { wifi, ntp: EspSntp::new_default().unwrap(), connection_check: Periodically::new_every_n_seconds(1), state: Arc::new(Mutex::new(WifiState::Stopped)), last_state: WifiState::Stopped, sys_loop, wifi_sub: None, ip_sub: None } } fn connect(&mut self) { log::info!("Connecting wifi"); self.wifi.start().unwrap(); self.wifi.connect().unwrap(); } fn disconnect(&mut self) { log::info!("Disconnecting wifi"); self.wifi.disconnect().unwrap(); self.wifi.stop().unwrap(); } } impl Task for WifiTask { fn start(&mut self) { log::info!("Starting wifi!"); log::info!("waiting for loop"); let wifi_state = self.state.clone(); self.wifi_sub = Some(self.sys_loop.subscribe::( move |evt| { log::warn!("wifi event {:?}", evt); let next_state = match evt { WifiEvent::StaStopped => Some(WifiState::Disconnected), WifiEvent::StaDisconnected => Some(WifiState::Disconnected), WifiEvent::StaStarted => Some(WifiState::Connecting), WifiEvent::StaConnected => Some(WifiState::Connecting), _ => None }; match next_state { Some(s) => { let mut state = wifi_state.lock().unwrap(); *state = s }, None => () } }).unwrap()); let ip_state = self.state.clone(); self.ip_sub = Some(self.sys_loop.subscribe::(move |evt| { log::warn!("ip event {:?}", evt); match evt { IpEvent::DhcpIpAssigned(_) => { let mut state = ip_state.lock().unwrap(); *state = WifiState::Connected; }, _ => () } }).unwrap()); self.connect(); } fn tick(&mut self ) { if self.connection_check.tick() { let cur_state = *self.state.lock().unwrap(); if self.last_state != cur_state { match cur_state { WifiState::Connected => log::info!("Wifi connected!"), WifiState::Connecting => log::info!("Connecting!"), WifiState::Stopped => log::info!("Stopped!"), WifiState::Disconnected => log::info!("Disconnected!") } log::info!("online: {:?}", cur_state); self.last_state = cur_state; } let now: DateTime = std::time::SystemTime::now().into(); log::info!("Current time: {} status={:?}", now.format("%d/%m/%Y %T"), self.ntp.get_sync_status()); } } fn stop(&mut self) { log::info!("Stopping wifi"); self.disconnect(); } }