Files
creaturedex/src/network/wifi.rs
T

145 lines
4.4 KiB
Rust

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<WifiCredentials> = 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<WifiStoreError> for WifiError {
fn from(error: WifiStoreError) -> WifiError {
WifiError::Store(error)
}
}
impl From<esp_radio::wifi::WifiError> 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<Self, WifiError> {
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<WifiCredentials, WifiError> {
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<Mutex<Nvs>>,
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<Outputs>, 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");
}
}