use crate::Mutex; use crate::alloc::string::ToString; use crate::Signal; use crate::navigation::outputs::Outputs; use crate::peripherals::Peripherals; #[cfg(not(feature = "wokwi"))] use crate::peripherals::storage::wifistore::WifiStore; use crate::peripherals::storage::wifistore::WifiStoreError; use alloc::string::String; use core::error::Error; use core::fmt::Display; use esp_radio::wifi::WifiController; use esp_radio::*; #[derive(Debug)] pub struct WifiCredentials { pub ssid: String, pub pass: String, } pub static WIFI_CREDENTIALS: Signal = Signal::new(); #[derive(Debug)] pub struct Wifi { pub controller: WifiController<'static>, pub interfaces: wifi::Interfaces<'static>, pub credentials: WifiCredentials, } #[derive(Debug)] pub enum WifiError { Store(WifiStoreError), Radio(esp_radio::wifi::WifiError), NoCredentialsStored, } impl Error for WifiError {} impl Display for WifiError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { WifiError::Store(e) => e.fmt(f), WifiError::Radio(e) => e.fmt(f), WifiError::NoCredentialsStored => write!(f, "No WiFi credentials stored in flash"), } } } impl From for WifiError { fn from(error: WifiStoreError) -> WifiError { WifiError::Store(error) } } impl From for WifiError { fn from(error: esp_radio::wifi::WifiError) -> WifiError { WifiError::Radio(error) } } impl Wifi { pub fn new( wifi: esp_hal::peripherals::WIFI<'static>, credentials: WifiCredentials ) -> Result { let wifi = wifi::new(wifi, Default::default())?; Ok(Self { controller: wifi.0, interfaces: wifi.1, credentials }) } pub async fn initialize_credentials( wifi_store: &WifiStore, ) -> Result { let stored_credentials = wifi_store.load_credentials().await?; let ssid = option_env!("WIFI_SSID").map_or(stored_credentials.ssid, |v| v.to_string()); let pass = option_env!("WIFI_PASS").map_or(stored_credentials.pass, |v| v.to_string()); log::info!("initial wifi credentials:\nssid:{ssid}, pass:{pass}"); Ok(WifiCredentials {ssid, pass}) } #[cfg(feature = "wokwi")] pub async fn setup( wifi: esp_hal::peripherals::WIFI<'static>, nvs: Arc>, spawner: &Spawner, ) { log::info!("Wokwi mode active: pre-seeding WiFi Manager with Wokwi credentials"); const WIFIMANAGER_NAMESPACE: Key = Key::from_str("wifimanager"); const WIFIMANAGER_NVS_KEY: Key = Key::from_str("WIFI_SETUP"); if let Err(e) = nvs.lock().await.set( &WIFIMANAGER_NAMESPACE, &WIFIMANAGER_NVS_KEY, "{\"ssid\":\"Wokwi-GUEST\",\"psk\":\"\",\"data\":{}}", ) { log::error!("Failed to store Wokwi WiFi config in NVS: {e:?}"); return; } // Preload connection data so init_wm can connect immediately without AP setup. let nvs = esp_hal_wifimanager::Nvs::new_from_nvs(nvs, 0, 0); let mut wm_settings = esp_hal_wifimanager::WmSettings::default(); // Wokwi simulation uses a default open network called "Wokwi-GUEST" wm_settings.ssid.clear(); _ = core::fmt::write(&mut wm_settings.ssid, format_args!("Wokwi-GUEST")); wm_settings.wifi_conn_timeout = 30000; // Initialize Wi-Fi connection through WiFi Manager using the pre-seeded NVS data. let wifi_res = esp_hal_wifimanager::init_wm(wm_settings, spawner, Some(&nvs), wifi, None).await; log::info!("Wokwi wifi_res: {wifi_res:?}"); } } #[embassy_executor::task] pub async fn wait_for_wifi_credentials(_outputs: &'static Mutex, peripherals: &'static Peripherals) { loop { log::info!("Waiting for new WiFi credentials"); let credentials = WIFI_CREDENTIALS.wait().await; log::info!("Received new WiFi credentials:\nssid: {}, pass: {}", credentials.ssid, credentials.pass); if let Err(err) = peripherals.store.lock().await.wifi_store.save_credentials(&credentials).await { log::error!("Error storing credentials: {err}"); continue; } log::info!("Saved new WiFi credentials. Restarting WiFi"); } }