diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index ee4a595..ffe01a0 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -1,6 +1,6 @@ use crate::bail; use crate::config::{NetworkConfig, PlantControllerConfig}; -use crate::hal::PLANT_COUNT; +use crate::hal::{PLANT_COUNT, TIME_ACCESS}; use crate::log::{LogMessage, LOG_ACCESS}; use chrono::{DateTime, Utc}; use serde::Serialize; @@ -14,8 +14,11 @@ use alloc::borrow::ToOwned; use core::marker::PhantomData; use core::net::{IpAddr, Ipv4Addr}; use core::str::FromStr; +use core::sync::atomic::Ordering; +use core::sync::atomic::Ordering::Relaxed; +use edge_dhcp::io::server::run; use embassy_executor::Spawner; -use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4}; +use embassy_net::{DhcpConfig, Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; use embassy_time::{Duration, Timer}; @@ -28,13 +31,11 @@ use esp_hal::rtc_cntl::sleep::RtcSleepConfig; use esp_hal::system::software_reset; use esp_println::println; use esp_storage::FlashStorage; -use esp_wifi::wifi::{ - AccessPointConfiguration, AccessPointInfo, Configuration, Interfaces, ScanConfig, - ScanTypeConfig, WifiController, WifiDevice, -}; +use esp_wifi::wifi::{AccessPointConfiguration, AccessPointInfo, AuthMethod, ClientConfiguration, Configuration, Interfaces, ScanConfig, ScanTypeConfig, WifiController, WifiDevice, WifiState}; use littlefs2::fs::Filesystem; use littlefs2_core::{FileType, PathBuf, SeekFrom}; use log::info; +use portable_atomic::AtomicBool; #[esp_hal::ram(rtc_fast, persistent)] static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT]; @@ -75,7 +76,8 @@ pub struct Esp<'a> { pub fs: Arc>>, pub rng: Rng, //first starter (ap or sta will take these) - pub interfaces: Option>, + pub interface_sta: Option>, + pub interface_ap: Option>, pub controller: Arc>>, //only filled, if a useable mqtt client with working roundtrip could be established @@ -288,7 +290,7 @@ impl Esp<'_> { } } - pub(crate) async fn wifi_ap(&mut self) -> FatResult> { + pub(crate) async fn wifi_ap(&mut self, fallback: bool) -> FatResult> { let ssid = match self.load_config().await { Ok(config) => config.network.ap_ssid.as_str().to_string(), Err(_) => "PlantCtrl Emergency Mode".to_string(), @@ -296,7 +298,7 @@ impl Esp<'_> { let spawner = Spawner::for_current_executor().await; - let device = self.interfaces.take().unwrap().ap; + let device = self.interface_ap.take().unwrap(); let gw_ip_addr_str = "192.168.71.1"; let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip"); @@ -308,6 +310,86 @@ impl Esp<'_> { let seed = (self.rng.random() as u64) << 32 | self.rng.random() as u64; + println!("init secondary stack"); + // Init network stack + let (stack, runner) = embassy_net::new( + device, + config, + mk_static!(StackResources<4>, StackResources::<4>::new()), + seed, + ); + let stack = mk_static!(Stack, stack); + + let client_config = Configuration::AccessPoint(AccessPointConfiguration { + ssid: ssid.clone(), + ..Default::default() + }); + + println!("stop old"); + self.controller.lock().await.stop()?; + + self.controller + .lock() + .await + .set_configuration(&client_config)?; + + println!("start new"); + self.controller.lock().await.start()?; + println!("start net task"); + spawner.spawn(net_task(runner)).ok(); + println!("run dhcp"); + spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok(); + + + loop { + println!("waiting for wifi ap link up"); + if stack.is_link_up() { + break; + } + Timer::after(Duration::from_millis(500)).await; + } + while !stack.is_config_up() { + println!("waiting for wifi ap config up"); + Timer::after(Duration::from_millis(100)).await + } + println!( + "Connect to the AP `${ssid}` and point your browser to http://{gw_ip_addr_str}/" + ); + stack + .config_v4() + .inspect(|c| println!("ipv4 config: {c:?}")); + + Ok(stack.clone()) + } + + pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> FatResult> { + esp_wifi::wifi_set_log_verbose(); + let ssid = network_config.ssid.clone(); + match &ssid { + Some(ssid) => { + if ssid.is_empty() { + bail!("Wifi ssid was empty") + } + } + None => { + bail!("Wifi ssid was empty") + } + } + let ssid = ssid.unwrap().to_string(); + info!("attempting to connect wifi {ssid}"); + let password = match network_config.password { + Some(ref password) => password.to_string(), + None => "".to_string(), + }; + let max_wait = network_config.max_wait; + + let spawner = Spawner::for_current_executor().await; + + let device = self.interface_sta.take().unwrap(); + let config = embassy_net::Config::dhcpv4(DhcpConfig::default()); + + let seed = (self.rng.random() as u64) << 32 | self.rng.random() as u64; + // Init network stack let (stack, runner) = embassy_net::new( device, @@ -317,28 +399,58 @@ impl Esp<'_> { ); let stack = mk_static!(Stack, stack); - spawner - .spawn(connection(self.controller.clone(), ssid.to_owned())) - .ok(); + let client_config = Configuration::Client(ClientConfiguration { + ssid, + bssid: None, + auth_method: AuthMethod::None, + password, + channel: None, + }); + self.controller + .lock() + .await + .set_configuration(&client_config)?; spawner.spawn(net_task(runner)).ok(); - spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok(); + self.controller.lock().await.start_async().await?; + let timeout = TIME_ACCESS.get().await.current_time_us() + max_wait as u64 * 1000; loop { - if stack.is_link_up() { - break; + let state = esp_wifi::wifi::ap_state(); + println!("waiting wifi sta ready {:?}", state); + match state{ + WifiState::StaStarted => { + self.controller.lock().await.connect()?; + break; + } + WifiState::StaConnected => {} + WifiState::StaDisconnected => {} + WifiState::StaStopped => {} + WifiState::ApStarted => {} + WifiState::ApStopped => {} + WifiState::Invalid => {} } + if TIME_ACCESS.get().await.current_time_us() > timeout { + bail!("Timeout waiting for wifi link up") + } + } + + + + + while !stack.is_link_up() { + if TIME_ACCESS.get().await.current_time_us() > timeout { + bail!("Timeout waiting for wifi link up") + } + println!("waiting for wifi link up"); Timer::after(Duration::from_millis(500)).await; } while !stack.is_config_up() { + if TIME_ACCESS.get().await.current_time_us() > timeout { + bail!("Timeout waiting for wifi config up") + } + println!("waiting for wifi config up"); Timer::after(Duration::from_millis(100)).await } - println!( - "Connect to the AP `${ssid}` and point your browser to http://{gw_ip_addr_str}/" - ); - stack - .config_v4() - .inspect(|c| println!("ipv4 config: {c:?}")); - Ok(stack.clone()) } @@ -370,76 +482,6 @@ impl Esp<'_> { //}; } - pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> FatResult { - let _ssid = network_config.ssid.clone(); - match &_ssid { - Some(ssid) => { - if ssid.is_empty() { - bail!("Wifi ssid was empty") - } - } - None => { - bail!("Wifi ssid was empty") - } - } - let _ssid = _ssid.unwrap(); - let _password = network_config.password.clone(); - let _max_wait = network_config.max_wait; - bail!("todo") - // match password { - // Some(pw) => { - // //TODO expect error due to invalid pw or similar! //call this during configuration and check if works, revert to config mode if not - // self.wifi_driver.set_configuration(&Configuration::Client( - // ClientConfiguration { - // ssid, - // password: pw, - // ..Default::default() - // }, - // ))?; - // } - // None => { - // self.wifi_driver.set_configuration(&Configuration::Client( - // ClientConfiguration { - // ssid, - // auth_method: AuthMethod::None, - // ..Default::default() - // }, - // ))?; - // } - // } - // - // self.wifi_driver.start()?; - // self.wifi_driver.connect()?; - // - // let delay = Delay::new_default(); - // let mut counter = 0_u32; - // while !self.wifi_driver.is_connected()? { - // delay.delay_ms(250); - // counter += 250; - // if counter > max_wait { - // //ignore these errors, Wi-Fi will not be used this - // self.wifi_driver.disconnect().unwrap_or(()); - // self.wifi_driver.stop().unwrap_or(()); - // bail!("Did not manage wifi connection within timeout"); - // } - // } - // log::info!("Should be connected now, waiting for link to be up"); - // - // while !self.wifi_driver.is_up()? { - // delay.delay_ms(250); - // counter += 250; - // if counter > max_wait { - // //ignore these errors, Wi-Fi will not be used this - // self.wifi_driver.disconnect().unwrap_or(()); - // self.wifi_driver.stop().unwrap_or(()); - // bail!("Did not manage wifi connection within timeout"); - // } - // } - // //update freertos registers ;) - // let address = self.wifi_driver.sta_netif().get_ip_info()?; - // log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}")); - // anyhow::Ok(address) - } pub(crate) async fn load_config(&mut self) -> FatResult { let cfg = PathBuf::try_from(CONFIG_FILE).unwrap(); let data = self.fs.lock().await.read::<4096>(&cfg)?; @@ -796,25 +838,8 @@ async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) { } } -#[embassy_executor::task] -async fn connection( - controller: Arc>>, - ssid: String, -) { - let client_config = Configuration::AccessPoint(AccessPointConfiguration { - ssid: ssid.clone(), - ..Default::default() - }); - controller - .lock() - .await - .set_configuration(&client_config) - .unwrap(); - controller.lock().await.start_async().await.unwrap(); - println!("Wifi started!"); -} -#[embassy_executor::task] +#[embassy_executor::task(pool_size = 2)] async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) { runner.run().await } diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index 1f502e5..afeabea 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -381,11 +381,14 @@ impl PlantHal { lfs2Filesystem::mount(alloc, lfs2filesystem).expect("Could not mount lfs2 filesystem"), )); + let ap = interfaces.ap; + let sta = interfaces.sta; let mut esp = Esp { fs, rng, controller: Arc::new(Mutex::new(controller)), - interfaces: Some(interfaces), + interface_sta : Some(sta), + interface_ap : Some(ap), boot_button, mqtt_client: None, ota, diff --git a/rust/src/main.rs b/rust/src/main.rs index 1fa5e75..b03c79a 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -234,7 +234,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { { info!("No wifi configured, starting initial config mode"); - let stack = board.board_hal.get_esp().wifi_ap().await?; + let stack = board.board_hal.get_esp().wifi_ap(false).await?; let reboot_now = Arc::new(AtomicBool::new(false)); println!("starting webserver"); @@ -243,8 +243,6 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { wait_infinity(board, WaitType::MissingConfig, reboot_now.clone()).await; } - info!("attempting to connect wifi "); - let mut stack = Option::None; let network_mode = if board.board_hal.get_config().network.ssid.is_some() { try_connect_wifi_sntp_mqtt(&mut board, *&mut stack).await @@ -261,7 +259,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { let res = { let esp = board.board_hal.get_esp(); - esp.wifi_ap().await + esp.wifi_ap(true).await }; match res { @@ -898,10 +896,16 @@ async fn publish_firmware_info( let _ = esp.mqtt_publish("/state", "online".as_bytes()).await; } -async fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>, stack_store:Option>) -> NetworkMode { +async fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>, mut stack_store:Option>) -> NetworkMode { let nw_conf = &board.board_hal.get_config().network.clone(); match board.board_hal.get_esp().wifi(nw_conf).await { - Ok(ip_info) => { + Ok(stack) => { + stack_store = Some(stack); + + loop { + println!("wifi stuff"); + Timer::after_millis(1000).await; + } let sntp_mode: SntpMode = match board.board_hal.get_esp().sntp(1000 * 10).await { Ok(new_time) => { info!("Using time from sntp"); @@ -932,7 +936,7 @@ async fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<'static, CriticalSect NetworkMode::WIFI { sntp: sntp_mode, mqtt: mqtt_connected, - ip_address: ip_info.ip.to_string(), + ip_address: stack.hardware_address().to_string(), } } Err(err) => {