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::task::Task; use crate::buffers::{Pixbuf, SurfacePool}; use crate::mappings::StrideMapping; use crate::platform::smart_leds_lib::StrideOutput; use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt; use crate::time::Periodically; #[cfg(feature="threads")] use crate::buffers::SharedSurface as SurfaceType; #[cfg(not(feature="threads"))] use crate::buffers::SimpleSurface as SurfaceType; pub struct Esp32Board<'a> { output: Option<::Output>, surfaces: Option>, tasks: Option>> } impl<'a> Board for Esp32Board<'a> { type Output = StrideOutput<[Rgb; 310], FastWs2812Esp32Rmt<'a>>; type Surfaces = SurfacePool; fn new(peripherals: Peripherals) -> Self { let sys_loop = EspSystemEventLoop::take().unwrap(); let nvs = EspDefaultNvsPartition::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()) } }; let tasks: Vec> = vec![ Box::new(WifiTask::new(peripherals.modem, sys_loop.clone(), &nvs)), ]; 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 { surfaces: Some(SurfacePool::new()), output: Some(output), tasks: Some(tasks), } } fn output(&mut self) -> Self::Output { self.output.take().unwrap() } fn surfaces(&mut self) -> Self::Surfaces { self.surfaces.take().unwrap() } fn system_tasks(&mut self) -> Vec> { self.tasks.take().unwrap() } } 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(); } }