diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index b91f5b8..97219c3 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -347,62 +347,6 @@ impl Esp<'_> { } } - pub(crate) async fn wifi_ap(&mut self, spawner: Spawner) -> FatResult> { - let ssid = match self.load_config().await { - Ok(config) => config.network.ap_ssid.as_str().to_string(), - Err(_) => "PlantCtrl Emergency Mode".to_string(), - }; - - let device = self - .interface_ap - .take() - .context("AP interface already taken")?; - let gw_ip_addr = Ipv4Addr::new(192, 168, 71, 1); - - let config = embassy_net::Config::ipv4_static(StaticConfigV4 { - address: Ipv4Cidr::new(gw_ip_addr, 24), - gateway: Some(gw_ip_addr), - dns_servers: Default::default(), - }); - - 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 = - Config::AccessPoint(AccessPointConfig::default().with_ssid(ssid.clone())); - self.controller.lock().await.set_config(&client_config)?; - - println!("start net task"); - spawner.spawn(net_task(runner)?); - println!("run dhcp"); - spawner.spawn(run_dhcp(*stack, gw_ip_addr)?); - - loop { - if stack.is_link_up() { - break; - } - Timer::after(Duration::from_millis(500)).await; - } - while !stack.is_config_up() { - Timer::after(Duration::from_millis(100)).await - } - println!("Connect to the AP `${ssid}` and point your browser to http://{gw_ip_addr}/"); - stack - .config_v4() - .inspect(|c| println!("ipv4 config: {c:?}")); - - Ok(*stack) - } - pub(crate) async fn wifi( &mut self, network_config: &NetworkConfig, diff --git a/rust/src/main.rs b/rust/src/main.rs index 446119a..8a1d2e3 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -17,7 +17,7 @@ use esp_backtrace as _; use hal::PROGRESS_ACTIVE; use crate::config::{NetworkConfig, PlantConfig}; -use crate::fat_error::FatResult; +use crate::fat_error::{ContextExt, FatResult}; use crate::log::log; use crate::tank::{determine_tank_state, TankError, TankState, WATER_FROZEN_THRESH}; @@ -195,7 +195,12 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { { info!("No wifi configured, starting initial config mode"); - let stack = board.board_hal.get_esp().wifi_ap(spawner).await?; + let esp = board.board_hal.get_esp(); + let ssid = esp.load_config().await + .map(|config| config.network.ap_ssid.to_string()) + .unwrap_or_else(|_| String::from("PlantCtrl Emergency Mode")); + let device = esp.interface_ap.take().context("AP interface already taken")?; + let stack = network::wifi_ap(ssid, device, &esp.controller, &mut esp.rng, spawner).await?; let reboot_now = Arc::new(AtomicBool::new(false)); println!("starting webserver"); @@ -219,7 +224,17 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { let res = { let esp = board.board_hal.get_esp(); - esp.wifi_ap(spawner).await + let ssid = esp.load_config().await + .map(|config| config.network.ap_ssid.to_string()) + .unwrap_or_else(|_| String::from("PlantCtrl Emergency Mode")); + let device = match esp.interface_ap.take() { + Some(d) => d, + None => { + use crate::fat_error::FatError; + return Err(FatError::String { error: "AP interface already taken".to_string() }); + } + }; + network::wifi_ap(ssid, device, &esp.controller, &mut esp.rng, spawner).await }; match res { Ok(ap_stack) => { diff --git a/rust/src/network.rs b/rust/src/network.rs index 789392d..4b1311a 100644 --- a/rust/src/network.rs +++ b/rust/src/network.rs @@ -1,11 +1,15 @@ use crate::bail; use crate::fat_error::{ContextExt, FatError, FatResult}; use alloc::string::String; +use alloc::sync::Arc; use chrono::{DateTime, Utc}; use core::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; +use embassy_executor::Spawner; use embassy_net::dns::DnsQueryType; use embassy_net::udp::{PacketMetadata, UdpSocket}; -use embassy_net::{Runner, Stack}; +use embassy_net::{Runner, Stack, StackResources, StaticConfigV4}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::Mutex; use embassy_time::{Duration, Timer, WithTimeout}; use edge_dhcp::{ io::{self, DEFAULT_SERVER_PORT}, @@ -13,8 +17,10 @@ use edge_dhcp::{ }; use edge_nal::UdpBind; use edge_nal_embassy::{Udp, UdpBuffers}; +use esp_hal::rng::Rng; use esp_println::println; -use esp_radio::wifi::Interface; +use esp_radio::wifi::ap::AccessPointConfig; +use esp_radio::wifi::{Config, Interface}; use log::{info, warn, error}; use serde::Serialize; use sntpc::{NtpContext, NtpTimestampGenerator, NtpUdpSocket, get_time}; @@ -184,3 +190,64 @@ pub(crate) async fn run_dhcp(stack: Stack<'static>, ip: Ipv4Addr) { Timer::after(Duration::from_millis(500)).await; } } + +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + +pub async fn wifi_ap( + ssid: String, + interface_ap: Interface<'static>, + controller: &Arc>>, + rng: &mut Rng, + spawner: Spawner, +) -> FatResult> { + let gw_ip_addr = Ipv4Addr::new(192, 168, 71, 1); + + let config = embassy_net::Config::ipv4_static(StaticConfigV4 { + address: embassy_net::Ipv4Cidr::new(gw_ip_addr, 24), + gateway: Some(gw_ip_addr), + dns_servers: Default::default(), + }); + + let seed = (rng.random() as u64) << 32 | rng.random() as u64; + + println!("init secondary stack"); + let (stack, runner) = embassy_net::new( + interface_ap, + config, + mk_static!(StackResources<4>, StackResources::<4>::new()), + seed, + ); + let stack = mk_static!(Stack, stack); + + let client_config = + Config::AccessPoint(AccessPointConfig::default().with_ssid(ssid.clone())); + controller.lock().await.set_config(&client_config)?; + + println!("start net task"); + spawner.spawn(net_task(runner)?); + println!("run dhcp"); + spawner.spawn(run_dhcp(*stack, gw_ip_addr)?); + + loop { + if stack.is_link_up() { + break; + } + Timer::after(Duration::from_millis(500)).await; + } + while !stack.is_config_up() { + Timer::after(Duration::from_millis(100)).await + } + println!("Connect to the AP `${ssid}` and point your browser to http://{gw_ip_addr}/"); + stack + .config_v4() + .inspect(|c| println!("ipv4 config: {c:?}")); + + Ok(*stack) +}