From e1122a16317c150ac8c334b14834d5a04263e536 Mon Sep 17 00:00:00 2001 From: Gustav Toft Date: Wed, 21 Aug 2024 13:30:20 +0200 Subject: [PATCH 1/3] Implemented DNS server configuration: Necessary for captive portal + splash page to work. --- src/asynch/control.rs | 14 ++++++++++++++ src/asynch/network.rs | 27 ++++++++++++++++++++++---- src/connection.rs | 4 +--- src/network.rs | 14 ++++++++++++++ src/options.rs | 44 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/asynch/control.rs b/src/asynch/control.rs index df35972..50cb41d 100644 --- a/src/asynch/control.rs +++ b/src/asynch/control.rs @@ -362,6 +362,15 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> }) .await?; } + // Network Primary DNS + if let Some(dns) = options.dns { + (&self.at_client) + .send_retry(&SetWifiAPConfig { + ap_config_id: AccessPointId::Id0, + ap_config_param: AccessPointConfig::PrimaryDNS(dns), + }) + .await?; + } (&self.at_client) .send_retry(&SetWifiAPConfig { @@ -445,11 +454,16 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> }) .await?; + self.state_ch.set_should_connect(true); + Ok(()) } /// Closes access point. pub async fn close_ap(&self) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + self.state_ch.set_should_connect(false); + (&self.at_client) .send_retry(&WifiAPAction { ap_config_id: AccessPointId::Id0, diff --git a/src/asynch/network.rs b/src/asynch/network.rs index 79532b0..b709e34 100644 --- a/src/asynch/network.rs +++ b/src/asynch/network.rs @@ -109,8 +109,14 @@ where } }) } - Urc::WifiAPUp(_) => warn!("Not yet implemented [WifiAPUp]"), - Urc::WifiAPDown(_) => warn!("Not yet implemented [WifiAPDown]"), + Urc::WifiAPUp(_) => self.ch.update_connection_with(|con| { + con.wifi_state = WiFiState::Connected; + con.network.replace(WifiNetwork::new_ap()); + }), + Urc::WifiAPDown(_) => self.ch.update_connection_with(|con| { + con.network.take(); + con.wifi_state = WiFiState::Inactive; + }), Urc::WifiAPStationConnected(_) => warn!("Not yet implemented [WifiAPStationConnected]"), Urc::WifiAPStationDisconnected(_) => { warn!("Not yet implemented [WifiAPStationDisconnected]") @@ -118,7 +124,16 @@ where Urc::EthernetLinkUp(_) => warn!("Not yet implemented [EthernetLinkUp]"), Urc::EthernetLinkDown(_) => warn!("Not yet implemented [EthernetLinkDown]"), Urc::NetworkUp(NetworkUp { interface_id }) => { - self.network_status_callback(interface_id).await?; + //self.network_status_callback(interface_id).await?; + self.ch.update_connection_with(|con| { + con.ipv6_link_local_up = true; + con.ipv4_up = true; + + #[cfg(feature = "ipv6")] + { + con.ipv6_up = ipv6_up + } + }); } Urc::NetworkDown(NetworkDown { interface_id }) => { self.network_status_callback(interface_id).await?; @@ -139,7 +154,11 @@ where // also ok. let NetworkStatusResponse { status: - NetworkStatus::InterfaceType(InterfaceType::WifiStation | InterfaceType::Unknown), + NetworkStatus::InterfaceType( + InterfaceType::WifiStation + | InterfaceType::Unknown + | InterfaceType::WifiAccessPoint, + ), .. } = self .at_client diff --git a/src/connection.rs b/src/connection.rs index b5f7292..070c152 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -50,7 +50,6 @@ impl WifiConnection { } } - #[allow(dead_code)] pub fn is_station(&self) -> bool { self.network .as_ref() @@ -58,7 +57,6 @@ impl WifiConnection { .unwrap_or_default() } - #[allow(dead_code)] pub fn is_access_point(&self) -> bool { !self.is_station() } @@ -82,6 +80,6 @@ impl WifiConnection { } pub fn is_connected(&self) -> bool { - self.is_config_up() && self.wifi_state == WiFiState::Connected + (self.is_config_up() || self.is_access_point()) && self.wifi_state == WiFiState::Connected } } diff --git a/src/network.rs b/src/network.rs index 0aaedfe..038a762 100644 --- a/src/network.rs +++ b/src/network.rs @@ -44,6 +44,20 @@ impl WifiNetwork { mode: WifiMode::Station, } } + + pub fn new_ap() -> Self { + Self { + bssid: Bytes::new(), + op_mode: OperationMode::Infrastructure, + ssid: String::new(), + channel: 0, + rssi: 1, + authentication_suites: 0, + unicast_ciphers: 0, + group_ciphers: 0, + mode: WifiMode::AccessPoint, + } + } } impl TryFrom for WifiNetwork { diff --git a/src/options.rs b/src/options.rs index c679e04..b7de6b5 100644 --- a/src/options.rs +++ b/src/options.rs @@ -88,6 +88,8 @@ pub struct ConnectionOptions<'a> { pub subnet: Option, #[cfg_attr(feature = "defmt", defmt(Debug2Format))] pub gateway: Option, + #[cfg_attr(feature = "defmt", defmt(Debug2Format))] + pub dns: Option, } impl<'a> ConnectionOptions<'a> { @@ -121,6 +123,12 @@ impl<'a> ConnectionOptions<'a> { } else { Some(Ipv4Addr::new(192, 168, 2, 1)) }; + self.dns = if let Some(dns) = self.dns { + Some(dns) + } else { + Some(Ipv4Addr::new(192, 168, 2, 1)) + }; + self } @@ -138,6 +146,11 @@ impl<'a> ConnectionOptions<'a> { } else { Some(Ipv4Addr::new(192, 168, 2, 1)) }; + self.dns = if let Some(dns) = self.dns { + Some(dns) + } else { + Some(Ipv4Addr::new(192, 168, 2, 1)) + }; self } @@ -156,6 +169,37 @@ impl<'a> ConnectionOptions<'a> { } else { Some(Ipv4Addr::new(192, 168, 2, 1)) }; + + self.dns = if let Some(dns) = self.dns { + Some(dns) + } else { + Some(Ipv4Addr::new(192, 168, 2, 1)) + }; + + self + } + + pub fn dns_server(mut self, dns_serv: Ipv4Addr) -> Self { + self.dns = Some(dns_serv); + + self.subnet = if let Some(subnet) = self.subnet { + Some(subnet) + } else { + Some(Ipv4Addr::new(255, 255, 255, 0)) + }; + + self.ip = if let Some(ip) = self.ip { + Some(ip) + } else { + Some(Ipv4Addr::new(192, 168, 2, 1)) + }; + + self.gateway = if let Some(gateway) = self.gateway { + Some(gateway) + } else { + Some(Ipv4Addr::new(192, 168, 2, 1)) + }; + self } } From f53334739e75389ecb4535278596ae1e6d0b0923 Mon Sep 17 00:00:00 2001 From: Gustav Toft Date: Tue, 17 Sep 2024 12:25:58 +0200 Subject: [PATCH 2/3] Fixed breaking of STA due to something in AP implementation. Implemented UART for AP commands. --- src/asynch/control.rs | 48 ++++++++++------ src/asynch/network.rs | 95 +++++++++++++++++++++++--------- src/command/network/mod.rs | 10 ++++ src/command/network/responses.rs | 8 +++ src/command/network/types.rs | 33 +++++++++++ src/connection.rs | 2 +- src/options.rs | 80 ++------------------------- 7 files changed, 157 insertions(+), 119 deletions(-) diff --git a/src/asynch/control.rs b/src/asynch/control.rs index 50cb41d..c350a3f 100644 --- a/src/asynch/control.rs +++ b/src/asynch/control.rs @@ -121,6 +121,7 @@ impl<'a, const INGRESS_BUF_SIZE: usize> atat::asynch::AtatClient .wait_response(Duration::from_millis(Cmd::MAX_TIMEOUT_MS.into())) .await?; let response: &atat::Response = &response.borrow(); + info!("response: {:?}", defmt::Debug2Format(&response)); cmd.parse(response.into()) } } @@ -182,7 +183,7 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> Ok(mac.to_be_bytes()[2..].try_into().unwrap()) } - async fn get_wifi_status(&self) -> Result { + pub async fn get_wifi_status(&self) -> Result { match (&self.at_client) .send_retry(&GetWifiStatus { status_id: StatusId::Status, @@ -333,41 +334,56 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> ap_config_param: AccessPointConfig::IPv4Mode(IPv4Mode::Static), }) .await?; - } - // Network IP address - if let Some(ip) = options.ip { (&self.at_client) .send_retry(&SetWifiAPConfig { ap_config_id: AccessPointId::Id0, - ap_config_param: AccessPointConfig::IPv4Address(ip), + ap_config_param: AccessPointConfig::IPv4Address( + options.ip.unwrap_or(Ipv4Addr::new(192, 168, 2, 1)), + ), }) .await?; - } - // Network Subnet mask - if let Some(subnet) = options.subnet { + (&self.at_client) .send_retry(&SetWifiAPConfig { ap_config_id: AccessPointId::Id0, - ap_config_param: AccessPointConfig::SubnetMask(subnet), + ap_config_param: AccessPointConfig::SubnetMask( + options.subnet.unwrap_or(Ipv4Addr::new(255, 255, 255, 0)), + ), }) .await?; - } - // Network Default gateway - if let Some(gateway) = options.gateway { + (&self.at_client) .send_retry(&SetWifiAPConfig { ap_config_id: AccessPointId::Id0, - ap_config_param: AccessPointConfig::DefaultGateway(gateway), + ap_config_param: AccessPointConfig::DefaultGateway( + options.gateway.unwrap_or(Ipv4Addr::new(192, 168, 2, 1)), + ), }) .await?; } - // Network Primary DNS - if let Some(dns) = options.dns { + + // Network Primary + Secondary DNS + let primary = match options.dns.as_slice() { + &[primary] => Some(primary), + &[primary, secondary] => { + (&self.at_client) + .send_retry(&SetWifiAPConfig { + ap_config_id: AccessPointId::Id0, + ap_config_param: AccessPointConfig::SecondaryDNS(secondary), + }) + .await?; + + Some(primary) + } + _ => None, + }; + + if let Some(primary) = primary { (&self.at_client) .send_retry(&SetWifiAPConfig { ap_config_id: AccessPointId::Id0, - ap_config_param: AccessPointConfig::PrimaryDNS(dns), + ap_config_param: AccessPointConfig::PrimaryDNS(primary), }) .await?; } diff --git a/src/asynch/network.rs b/src/asynch/network.rs index b709e34..8dec528 100644 --- a/src/asynch/network.rs +++ b/src/asynch/network.rs @@ -1,6 +1,5 @@ -use core::str::FromStr as _; - use atat::{asynch::AtatClient, UrcChannel, UrcSubscription}; +use core::str::FromStr as _; use embassy_time::{with_timeout, Duration, Timer}; use embedded_hal::digital::OutputPin as _; use no_std_net::{Ipv4Addr, Ipv6Addr}; @@ -8,14 +7,14 @@ use no_std_net::{Ipv4Addr, Ipv6Addr}; use crate::{ command::{ network::{ - responses::NetworkStatusResponse, - types::{InterfaceType, NetworkStatus, NetworkStatusParameter}, + responses::{APStatusResponse, NetworkStatusResponse}, + types::{APStatusParameter, InterfaceType, NetworkStatus, NetworkStatusParameter}, urc::{NetworkDown, NetworkUp}, - GetNetworkStatus, + GetAPStatus, GetNetworkStatus, }, system::{RebootDCE, StoreCurrentConfig}, wifi::{ - types::DisconnectReason, + types::{AccessPointStatus, DisconnectReason}, urc::{WifiLinkConnected, WifiLinkDisconnected}, }, Urc, @@ -88,12 +87,16 @@ where connection_id: _, bssid, channel, - }) => self.ch.update_connection_with(|con| { - con.wifi_state = WiFiState::Connected; - con.network - .replace(WifiNetwork::new_station(bssid, channel)); - }), + }) => { + info!("wifi link connected"); + self.ch.update_connection_with(|con| { + con.wifi_state = WiFiState::Connected; + con.network + .replace(WifiNetwork::new_station(bssid, channel)); + }) + } Urc::WifiLinkDisconnected(WifiLinkDisconnected { reason, .. }) => { + info!("Wifi link disconnected"); self.ch.update_connection_with(|con| { con.wifi_state = match reason { DisconnectReason::NetworkDisabled => { @@ -124,19 +127,18 @@ where Urc::EthernetLinkUp(_) => warn!("Not yet implemented [EthernetLinkUp]"), Urc::EthernetLinkDown(_) => warn!("Not yet implemented [EthernetLinkDown]"), Urc::NetworkUp(NetworkUp { interface_id }) => { - //self.network_status_callback(interface_id).await?; - self.ch.update_connection_with(|con| { - con.ipv6_link_local_up = true; - con.ipv4_up = true; - - #[cfg(feature = "ipv6")] - { - con.ipv6_up = ipv6_up - } - }); + if interface_id > 10 { + self.ap_status_callback().await?; + } else { + self.network_status_callback(interface_id).await?; + } } Urc::NetworkDown(NetworkDown { interface_id }) => { - self.network_status_callback(interface_id).await?; + if interface_id > 10 { + self.ap_status_callback().await?; + } else { + self.network_status_callback(interface_id).await?; + } } Urc::NetworkError(_) => warn!("Not yet implemented [NetworkError]"), _ => {} @@ -152,13 +154,10 @@ where // credentials have been restored from persistent memory. This although // the wifi station has been started. So we assume that this type is // also ok. + info!("Entered network_status_callback"); let NetworkStatusResponse { status: - NetworkStatus::InterfaceType( - InterfaceType::WifiStation - | InterfaceType::Unknown - | InterfaceType::WifiAccessPoint, - ), + NetworkStatus::InterfaceType(InterfaceType::WifiStation | InterfaceType::Unknown), .. } = self .at_client @@ -170,6 +169,7 @@ where else { return Err(Error::Network); }; + info!("Network status changed"); let NetworkStatusResponse { status: NetworkStatus::IPv4Address(ipv4), @@ -184,12 +184,17 @@ where else { return Err(Error::Network); }; + info!( + "Network status callback ipv4: {:?}", + core::str::from_utf8(&ipv4).ok() + ); let ipv4_up = core::str::from_utf8(ipv4.as_slice()) .ok() .and_then(|s| Ipv4Addr::from_str(s).ok()) .map(|ip| !ip.is_unspecified()) .unwrap_or_default(); + info!("Network status callback ipv4: {:?}", ipv4_up); #[cfg(feature = "ipv6")] let ipv6_up = { @@ -227,6 +232,10 @@ where else { return Err(Error::Network); }; + info!( + "Network status callback ipv6: {:?}", + core::str::from_utf8(&ipv6_link_local).ok() + ); let ipv6_link_local_up = core::str::from_utf8(ipv6_link_local.as_slice()) .ok() @@ -234,6 +243,8 @@ where .map(|ip| !ip.is_unspecified()) .unwrap_or_default(); + info!("Network status callback ipv6: {:?}", ipv6_link_local_up); + // Use `ipv4_addr` & `ipv6_addr` to determine link state self.ch.update_connection_with(|con| { con.ipv6_link_local_up = ipv6_link_local_up; @@ -248,6 +259,36 @@ where Ok(()) } + async fn ap_status_callback(&mut self) -> Result<(), Error> { + let APStatusResponse { + status_val: AccessPointStatus::Status(ap_status), + .. + } = self + .at_client + .send_retry(&GetAPStatus { + status_id: APStatusParameter::Status, + }) + .await? + else { + return Err(Error::Network); + }; + info!("AP status callback Status: {:?}", ap_status); + + let ap_status = ap_status.into(); + + self.ch.update_connection_with(|con| { + con.ipv6_link_local_up = ap_status; + con.ipv4_up = ap_status; + + #[cfg(feature = "ipv6")] + { + con.ipv6_up = ap_status; + } + }); + + Ok(()) + } + async fn wait_startup(&mut self, timeout: Duration) -> Result<(), Error> { let fut = async { loop { diff --git a/src/command/network/mod.rs b/src/command/network/mod.rs index 81d4c8a..615fb8d 100644 --- a/src/command/network/mod.rs +++ b/src/command/network/mod.rs @@ -9,6 +9,16 @@ use types::*; use super::NoResponse; +/// 7.10 Wi-Fi Acess point status +UWAPSTAT +/// +/// Read status of Wi-Fi interface id. +#[derive(Clone, AtatCmd)] +#[at_cmd("+UWAPSTAT", APStatusResponse, attempts = 3, timeout_ms = 1000)] +pub struct GetAPStatus { + #[at_arg(position = 0)] + pub status_id: APStatusParameter, +} + /// 10.1 Network host name +UNHN /// /// Sets a new host name. diff --git a/src/command/network/responses.rs b/src/command/network/responses.rs index 20a80dd..1ab67ce 100644 --- a/src/command/network/responses.rs +++ b/src/command/network/responses.rs @@ -1,7 +1,15 @@ //! Responses for Network Commands +use crate::command::wifi::types::AccessPointStatus; + use super::types::*; use atat::atat_derive::AtatResp; +/// 7.10 WiFi AP status +UWAPSTAT +#[derive(Clone, AtatResp)] +pub struct APStatusResponse { + pub status_val: AccessPointStatus, +} + /// 10.2 Network status +UNSTAT #[derive(Clone, AtatResp)] pub struct NetworkStatusResponse { diff --git a/src/command/network/types.rs b/src/command/network/types.rs index 97c8a4a..d103fde 100644 --- a/src/command/network/types.rs +++ b/src/command/network/types.rs @@ -7,6 +7,39 @@ use heapless::String; use crate::command::OnOff; +#[derive(Clone, PartialEq, AtatEnum)] +pub enum APStatus { + // 0: The is the currently used SSID. + #[at_arg(value = 0)] + SSID(String<64>), + // 1: The is the currently used BSSID. + #[at_arg(value = 1)] + BSSID(#[at_arg(len = 20)] Bytes<20>), + // 2: The is the currently used channel. + #[at_arg(value = 2)] + Channel(u8), + // 3: The is the current status of the access point. + // - 0: disabled + // - 1: enabled + #[at_arg(value = 3)] + Status(OnOff), +} + +#[derive(Clone, PartialEq, AtatEnum)] +#[repr(u8)] +pub enum APStatusParameter { + // 0: The is the currently used SSID. + SSID = 0, + // 1: The is the currently used BSSID. + BSSID = 1, + // 2: The is the currently used channel. + Channel = 2, + // 3: The is the current status of the access point. + // • 0: disabled + // • 1: enabled + Status = 3, +} + #[derive(Clone, PartialEq, AtatEnum)] pub enum NetworkStatus { /// 0: The is the interface hardware address (displayed only if applicable). diff --git a/src/connection.rs b/src/connection.rs index 070c152..a1b44d0 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -80,6 +80,6 @@ impl WifiConnection { } pub fn is_connected(&self) -> bool { - (self.is_config_up() || self.is_access_point()) && self.wifi_state == WiFiState::Connected + self.is_config_up() && self.wifi_state == WiFiState::Connected } } diff --git a/src/options.rs b/src/options.rs index b7de6b5..c51bdc2 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,3 +1,4 @@ +use heapless::Vec; use no_std_net::Ipv4Addr; #[allow(dead_code)] @@ -75,7 +76,7 @@ impl<'a> From<&'a str> for WifiAuthentication<'a> { } } -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ConnectionOptions<'a> { @@ -89,7 +90,7 @@ pub struct ConnectionOptions<'a> { #[cfg_attr(feature = "defmt", defmt(Debug2Format))] pub gateway: Option, #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub dns: Option, + pub dns: Vec, } impl<'a> ConnectionOptions<'a> { @@ -112,94 +113,23 @@ impl<'a> ConnectionOptions<'a> { pub fn ip_address(mut self, ip_addr: Ipv4Addr) -> Self { self.ip = Some(ip_addr); - self.subnet = if let Some(subnet) = self.subnet { - Some(subnet) - } else { - Some(Ipv4Addr::new(255, 255, 255, 0)) - }; - - self.gateway = if let Some(gateway) = self.gateway { - Some(gateway) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - self.dns = if let Some(dns) = self.dns { - Some(dns) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - self } pub fn subnet_address(mut self, subnet_addr: Ipv4Addr) -> Self { self.subnet = Some(subnet_addr); - self.ip = if let Some(ip) = self.ip { - Some(ip) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - - self.gateway = if let Some(gateway) = self.gateway { - Some(gateway) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - self.dns = if let Some(dns) = self.dns { - Some(dns) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - self } pub fn gateway_address(mut self, gateway_addr: Ipv4Addr) -> Self { self.gateway = Some(gateway_addr); - self.subnet = if let Some(subnet) = self.subnet { - Some(subnet) - } else { - Some(Ipv4Addr::new(255, 255, 255, 0)) - }; - - self.ip = if let Some(ip) = self.ip { - Some(ip) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - - self.dns = if let Some(dns) = self.dns { - Some(dns) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - self } - pub fn dns_server(mut self, dns_serv: Ipv4Addr) -> Self { - self.dns = Some(dns_serv); - - self.subnet = if let Some(subnet) = self.subnet { - Some(subnet) - } else { - Some(Ipv4Addr::new(255, 255, 255, 0)) - }; - - self.ip = if let Some(ip) = self.ip { - Some(ip) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - - self.gateway = if let Some(gateway) = self.gateway { - Some(gateway) - } else { - Some(Ipv4Addr::new(192, 168, 2, 1)) - }; - + pub fn dns_server(mut self, dns_serv: Vec) -> Self { + self.dns = dns_serv; self } } From c067761adcff5a1aff6f9fa243274e5bfa732f99 Mon Sep 17 00:00:00 2001 From: Gustav Toft Date: Tue, 1 Oct 2024 08:05:18 +0200 Subject: [PATCH 3/3] Fixed acording to review comments. --- src/asynch/control.rs | 1 - src/asynch/network.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/asynch/control.rs b/src/asynch/control.rs index c350a3f..b059467 100644 --- a/src/asynch/control.rs +++ b/src/asynch/control.rs @@ -121,7 +121,6 @@ impl<'a, const INGRESS_BUF_SIZE: usize> atat::asynch::AtatClient .wait_response(Duration::from_millis(Cmd::MAX_TIMEOUT_MS.into())) .await?; let response: &atat::Response = &response.borrow(); - info!("response: {:?}", defmt::Debug2Format(&response)); cmd.parse(response.into()) } } diff --git a/src/asynch/network.rs b/src/asynch/network.rs index 8dec528..6f28b9e 100644 --- a/src/asynch/network.rs +++ b/src/asynch/network.rs @@ -169,7 +169,6 @@ where else { return Err(Error::Network); }; - info!("Network status changed"); let NetworkStatusResponse { status: NetworkStatus::IPv4Address(ipv4),