diff --git a/examples/rpi-pico/src/bin/embassy-perf.rs b/examples/rpi-pico/src/bin/embassy-perf.rs index 0376907..630415a 100644 --- a/examples/rpi-pico/src/bin/embassy-perf.rs +++ b/examples/rpi-pico/src/bin/embassy-perf.rs @@ -90,7 +90,7 @@ async fn main(spawner: Spawner) { let net_device = runner.ppp_stack(PPP_STATE.init(embassy_net_ppp::State::new())); // Generate random seed - let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. + let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guaranteed to be random. // Init network stack static STACK: StaticCell>> = StaticCell::new(); diff --git a/examples/rpi-pico/src/bin/embassy-smoltcp-ppp.rs b/examples/rpi-pico/src/bin/embassy-smoltcp-ppp.rs index 21c9416..e25d51e 100644 --- a/examples/rpi-pico/src/bin/embassy-smoltcp-ppp.rs +++ b/examples/rpi-pico/src/bin/embassy-smoltcp-ppp.rs @@ -109,7 +109,7 @@ async fn main(spawner: Spawner) { let net_device = runner.ppp_stack(PPP_STATE.init(embassy_net_ppp::State::new())); // Generate random seed - let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. + let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guaranteed to be random. // Init network stack static STACK: StaticCell>> = StaticCell::new(); diff --git a/src/asynch/at_udp_socket.rs b/src/asynch/at_udp_socket.rs index 6dd777d..5b3f87f 100644 --- a/src/asynch/at_udp_socket.rs +++ b/src/asynch/at_udp_socket.rs @@ -14,7 +14,6 @@ impl<'a> embedded_io_async::ErrorType for &AtUdpSocket<'a> { impl<'a> Read for &AtUdpSocket<'a> { async fn read(&mut self, buf: &mut [u8]) -> Result { let (len, _) = self.0.recv_from(buf).await.unwrap(); - debug!("READ {} bytes: {=[u8]:a}", len, &buf[..len]); Ok(len) } } @@ -40,7 +39,6 @@ impl<'a> embedded_io_async::ErrorType for AtUdpSocket<'a> { impl<'a> Read for AtUdpSocket<'a> { async fn read(&mut self, buf: &mut [u8]) -> Result { let (len, _) = self.0.recv_from(buf).await.unwrap(); - debug!("READ {} bytes: {=[u8]:a}", len, &buf[..len]); Ok(len) } } diff --git a/src/asynch/control.rs b/src/asynch/control.rs index 5bc4ed0..031d59d 100644 --- a/src/asynch/control.rs +++ b/src/asynch/control.rs @@ -1,99 +1,135 @@ -use core::future::poll_fn; -use core::task::Poll; - -use atat::{ - asynch::{AtatClient, SimpleClient}, - UrcChannel, UrcSubscription, -}; -use embassy_net::{ - udp::{PacketMetadata, UdpSocket}, - Ipv4Address, +use atat::{asynch::AtatClient, response_slot::ResponseSlotGuard, UrcChannel}; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Sender}; +use embassy_time::{with_timeout, Duration, Timer}; +use heapless::Vec; + +use crate::command::gpio::responses::ReadGPIOResponse; +use crate::command::gpio::types::GPIOMode; +use crate::command::gpio::ConfigureGPIO; +use crate::command::wifi::{ExecWifiStationAction, GetWifiStatus, SetWifiStationConfig}; +use crate::command::OnOff; +use crate::command::{ + gpio::ReadGPIO, + wifi::{ + types::{ + AccessPointAction, Authentication, SecurityMode, SecurityModePSK, StatusId, + WifiStationAction, WifiStationConfig, WifiStatus, WifiStatusVal, + }, + WifiAPAction, + }, }; -use embassy_time::{with_timeout, Duration}; - -use crate::command::gpio::{ - types::{GPIOId, GPIOValue}, - WriteGPIO, +use crate::command::{ + gpio::{ + types::{GPIOId, GPIOValue}, + WriteGPIO, + }, + wifi::SetWifiAPConfig, }; -use crate::command::network::SetNetworkHostName; -use crate::command::system::{RebootDCE, ResetToFactoryDefaults}; -use crate::command::wifi::types::{ - Authentication, StatusId, WifiStationAction, WifiStationConfig, WifiStatus, WifiStatusVal, +use crate::command::{network::SetNetworkHostName, wifi::types::AccessPointConfig}; +use crate::command::{ + system::{RebootDCE, ResetToFactoryDefaults}, + wifi::types::AccessPointId, }; -use crate::command::wifi::{ExecWifiStationAction, GetWifiStatus, SetWifiStationConfig}; -use crate::command::OnOff; use crate::error::Error; +use super::runner::{MAX_CMD_LEN, URC_SUBSCRIBERS}; use super::state::LinkState; -use super::{at_udp_socket::AtUdpSocket, runner::URC_SUBSCRIBERS}; use super::{state, UbloxUrc}; const CONFIG_ID: u8 = 0; -const MAX_COMMAND_LEN: usize = 128; - -// TODO: Can this be made in a more intuitive way? -pub struct ControlResources { - rx_meta: [PacketMetadata; 1], - tx_meta: [PacketMetadata; 1], - socket_rx_buf: [u8; 32], - socket_tx_buf: [u8; 32], - at_buf: [u8; MAX_COMMAND_LEN], +pub(crate) struct ProxyClient<'a, const INGRESS_BUF_SIZE: usize> { + pub(crate) req_sender: Sender<'a, NoopRawMutex, Vec, 1>, + pub(crate) res_slot: &'a atat::ResponseSlot, + cooldown_timer: Option, } -impl ControlResources { - pub const fn new() -> Self { +impl<'a, const INGRESS_BUF_SIZE: usize> ProxyClient<'a, INGRESS_BUF_SIZE> { + pub fn new( + req_sender: Sender<'a, NoopRawMutex, Vec, 1>, + res_slot: &'a atat::ResponseSlot, + ) -> Self { Self { - rx_meta: [PacketMetadata::EMPTY; 1], - tx_meta: [PacketMetadata::EMPTY; 1], - socket_rx_buf: [0u8; 32], - socket_tx_buf: [0u8; 32], - at_buf: [0u8; MAX_COMMAND_LEN], + req_sender, + res_slot, + cooldown_timer: None, + } + } + + async fn wait_response<'guard>( + &'guard mut self, + timeout: Duration, + ) -> Result, atat::Error> { + with_timeout(timeout, self.res_slot.get()) + .await + .map_err(|_| atat::Error::Timeout) + } +} + +impl<'a, const INGRESS_BUF_SIZE: usize> atat::asynch::AtatClient + for ProxyClient<'a, INGRESS_BUF_SIZE> +{ + async fn send(&mut self, cmd: &Cmd) -> Result { + let mut buf = [0u8; MAX_CMD_LEN]; + let len = cmd.write(&mut buf); + + if len < 50 { + debug!( + "Sending command: {:?}", + atat::helpers::LossyStr(&buf[..len]) + ); + } else { + debug!("Sending command with long payload ({} bytes)", len); + } + + if let Some(cooldown) = self.cooldown_timer.take() { + cooldown.await + } + + // TODO: Guard against race condition! + self.req_sender + .send(Vec::try_from(&buf[..len]).unwrap()) + .await; + + self.cooldown_timer = Some(Timer::after(Duration::from_millis(20))); + + if !Cmd::EXPECTS_RESPONSE_CODE { + cmd.parse(Ok(&[])) + } else { + let response = self + .wait_response(Duration::from_millis(Cmd::MAX_TIMEOUT_MS.into())) + .await?; + let response: &atat::Response = &response.borrow(); + cmd.parse(response.into()) } } } -pub struct Control<'a, 'r, const URC_CAPACITY: usize> { +pub struct Control<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> { state_ch: state::Runner<'a>, - at_client: SimpleClient<'r, AtUdpSocket<'r>, atat::DefaultDigester>, - _urc_subscription: UrcSubscription<'a, UbloxUrc, URC_CAPACITY, URC_SUBSCRIBERS>, + at_client: ProxyClient<'a, INGRESS_BUF_SIZE>, + _urc_channel: &'a UrcChannel, } -impl<'a, 'r, const URC_CAPACITY: usize> Control<'a, 'r, URC_CAPACITY> { - pub(crate) fn new( +impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> + Control<'a, INGRESS_BUF_SIZE, URC_CAPACITY> +{ + pub(crate) fn new( state_ch: state::Runner<'a>, urc_channel: &'a UrcChannel, - resources: &'r mut ControlResources, - stack: &'r embassy_net::Stack, + req_sender: Sender<'a, NoopRawMutex, Vec, 1>, + res_slot: &'a atat::ResponseSlot, ) -> Self { - let mut socket = UdpSocket::new( - stack, - &mut resources.rx_meta, - &mut resources.socket_rx_buf, - &mut resources.tx_meta, - &mut resources.socket_tx_buf, - ); - - info!("Socket bound!"); - socket - .bind((Ipv4Address::new(172, 30, 0, 252), AtUdpSocket::PPP_AT_PORT)) - .unwrap(); - - let at_client = SimpleClient::new( - AtUdpSocket(socket), - atat::AtDigester::::new(), - &mut resources.at_buf, - atat::Config::default(), - ); - Self { state_ch, - at_client, - _urc_subscription: urc_channel.subscribe().unwrap(), + at_client: ProxyClient::new(req_sender, res_slot), + _urc_channel: urc_channel, } } pub async fn set_hostname(&mut self, hostname: &str) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + self.at_client .send(&SetNetworkHostName { host_name: hostname, @@ -131,13 +167,141 @@ impl<'a, 'r, const URC_CAPACITY: usize> Control<'a, 'r, URC_CAPACITY> { } pub async fn factory_reset(&mut self) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + self.at_client.send(&ResetToFactoryDefaults).await?; self.at_client.send(&RebootDCE).await?; Ok(()) } + pub async fn start_ap(&mut self, ssid: &str) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + + // Deactivate network id 0 + self.at_client + .send(&WifiAPAction { + ap_config_id: AccessPointId::Id0, + ap_action: AccessPointAction::Deactivate, + }) + .await?; + + self.at_client + .send(&WifiAPAction { + ap_config_id: AccessPointId::Id0, + ap_action: AccessPointAction::Reset, + }) + .await?; + + // // Disable DHCP Server (static IP address will be used) + // if options.ip.is_some() || options.subnet.is_some() || options.gateway.is_some() { + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::IPv4Mode(IPv4Mode::Static), + // }) + // .await?; + // } + + // // Network IP address + // if let Some(ip) = options.ip { + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::IPv4Address(ip), + // }) + // .await?; + // } + // // Network Subnet mask + // if let Some(subnet) = options.subnet { + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::SubnetMask(subnet), + // }) + // .await?; + // } + // // Network Default gateway + // if let Some(gateway) = options.gateway { + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::DefaultGateway(gateway), + // }) + // .await?; + // } + + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::DHCPServer(true.into()), + // }) + // .await?; + + // Wifi part + // Set the Network SSID to connect to + self.at_client + .send(&SetWifiAPConfig { + ap_config_id: AccessPointId::Id0, + ap_config_param: AccessPointConfig::SSID( + heapless::String::try_from(ssid).map_err(|_| Error::Overflow)?, + ), + }) + .await?; + + // if let Some(pass) = options.password.clone() { + // // Use WPA2 as authentication type + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::SecurityMode( + // SecurityMode::Wpa2AesCcmp, + // SecurityModePSK::PSK, + // ), + // }) + // .await?; + + // // Input passphrase + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::PSKPassphrase(PasskeyR::Passphrase(pass)), + // }) + // .await?; + // } else { + self.at_client + .send(&SetWifiAPConfig { + ap_config_id: AccessPointId::Id0, + ap_config_param: AccessPointConfig::SecurityMode( + SecurityMode::Open, + SecurityModePSK::Open, + ), + }) + .await?; + // } + + // if let Some(channel) = configuration.channel { + // self.at_client + // .send(&SetWifiAPConfig { + // ap_config_id: AccessPointId::Id0, + // ap_config_param: AccessPointConfig::Channel(channel as u8), + // }) + // .await?; + // } + + self.at_client + .send(&WifiAPAction { + ap_config_id: AccessPointId::Id0, + ap_action: AccessPointAction::Activate, + }) + .await?; + + Ok(()) + } + pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + if matches!(self.get_wifi_status().await?, WifiStatusVal::Connected) { // Wifi already connected. Check if the SSID is the same let current_ssid = self.get_connected_ssid().await?; @@ -193,6 +357,8 @@ impl<'a, 'r, const URC_CAPACITY: usize> Control<'a, 'r, URC_CAPACITY> { } pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + if matches!(self.get_wifi_status().await?, WifiStatusVal::Connected) { // Wifi already connected. Check if the SSID is the same let current_ssid = self.get_connected_ssid().await?; @@ -257,6 +423,8 @@ impl<'a, 'r, const URC_CAPACITY: usize> Control<'a, 'r, URC_CAPACITY> { } pub async fn disconnect(&mut self) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + match self.get_wifi_status().await? { WifiStatusVal::Disabled => {} WifiStatusVal::Disconnected | WifiStatusVal::Connected => { @@ -269,24 +437,20 @@ impl<'a, 'r, const URC_CAPACITY: usize> Control<'a, 'r, URC_CAPACITY> { } } - let wait_for_disconnect = poll_fn(|cx| match self.state_ch.link_state(cx) { - LinkState::Up => Poll::Pending, - LinkState::Down => Poll::Ready(()), - }); - - with_timeout(Duration::from_secs(10), wait_for_disconnect) - .await - .map_err(|_| Error::Timeout)?; + with_timeout( + Duration::from_secs(10), + self.state_ch.wait_for_link_state(LinkState::Down), + ) + .await + .map_err(|_| Error::Timeout)?; Ok(()) } async fn wait_for_join(&mut self, ssid: &str) -> Result<(), Error> { - poll_fn(|cx| match self.state_ch.link_state(cx) { - LinkState::Down => Poll::Pending, - LinkState::Up => Poll::Ready(()), - }) - .await; + // TODO: Handle returning error in case of security problems + + self.state_ch.wait_for_link_state(LinkState::Up).await; // Check that SSID matches let current_ssid = self.get_connected_ssid().await?; @@ -297,44 +461,65 @@ impl<'a, 'r, const URC_CAPACITY: usize> Control<'a, 'r, URC_CAPACITY> { Ok(()) } - pub async fn gpio_set(&mut self, id: GPIOId, value: GPIOValue) -> Result<(), Error> { - self.at_client.send(&WriteGPIO { id, value }).await?; + pub async fn gpio_configure(&mut self, id: GPIOId, mode: GPIOMode) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; + self.at_client.send(&ConfigureGPIO { id, mode }).await?; Ok(()) } - // FIXME: This could probably be improved - #[cfg(feature = "internal-network-stack")] - pub async fn import_credentials( - &mut self, - data_type: SecurityDataType, - name: &str, - data: &[u8], - md5_sum: Option<&str>, - ) -> Result<(), atat::Error> { - assert!(name.len() < 16); - - info!("Importing {:?} bytes as {:?}", data.len(), name); + pub async fn gpio_set(&mut self, id: GPIOId, value: bool) -> Result<(), Error> { + self.state_ch.wait_for_initialized().await; - self.at_client - .send(&PrepareSecurityDataImport { - data_type, - data_size: data.len(), - internal_name: name, - password: None, - }) - .await?; + let value = if value { + GPIOValue::High + } else { + GPIOValue::Low + }; - let import_data = self - .at_client - .send(&SendSecurityDataImport { - data: atat::serde_bytes::Bytes::new(data), - }) - .await?; + self.at_client.send(&WriteGPIO { id, value }).await?; + Ok(()) + } - if let Some(hash) = md5_sum { - assert_eq!(import_data.md5_string.as_str(), hash); - } + pub async fn gpio_get(&mut self, id: GPIOId) -> Result { + self.state_ch.wait_for_initialized().await; - Ok(()) + let ReadGPIOResponse { value, .. } = self.at_client.send(&ReadGPIO { id }).await?; + Ok(value as u8 != 0) } + + // FIXME: This could probably be improved + // #[cfg(feature = "internal-network-stack")] + // pub async fn import_credentials( + // &mut self, + // data_type: SecurityDataType, + // name: &str, + // data: &[u8], + // md5_sum: Option<&str>, + // ) -> Result<(), atat::Error> { + // assert!(name.len() < 16); + + // info!("Importing {:?} bytes as {:?}", data.len(), name); + + // self.at_client + // .send(&PrepareSecurityDataImport { + // data_type, + // data_size: data.len(), + // internal_name: name, + // password: None, + // }) + // .await?; + + // let import_data = self + // .at_client + // .send(&SendSecurityDataImport { + // data: atat::serde_bytes::Bytes::new(data), + // }) + // .await?; + + // if let Some(hash) = md5_sum { + // assert_eq!(import_data.md5_string.as_str(), hash); + // } + + // Ok(()) + // } } diff --git a/src/asynch/network.rs b/src/asynch/network.rs index 5b7bcae..5d73e1e 100644 --- a/src/asynch/network.rs +++ b/src/asynch/network.rs @@ -7,7 +7,6 @@ use no_std_net::{Ipv4Addr, Ipv6Addr}; use crate::{ command::{ - data_mode::{types::PeerConfigParameter, SetPeerConfiguration}, general::SoftwareVersion, network::{ responses::NetworkStatusResponse, @@ -15,12 +14,13 @@ use crate::{ urc::{NetworkDown, NetworkUp}, GetNetworkStatus, }, - system::{RebootDCE, StoreCurrentConfig}, + system::{types::EchoOn, RebootDCE, SetEcho, StoreCurrentConfig}, wifi::{ - types::DisconnectReason, + types::{DisconnectReason, PowerSaveMode, WifiConfig as WifiConfigParam}, urc::{WifiLinkConnected, WifiLinkDisconnected}, + SetWifiConfig, }, - Urc, + OnOff, Urc, }, connection::WiFiState, error::Error, @@ -28,9 +28,9 @@ use crate::{ WifiConfig, }; -use super::{runner::URC_SUBSCRIBERS, state, UbloxUrc}; +use super::{runner::URC_SUBSCRIBERS, state, LinkState, UbloxUrc}; -pub struct NetDevice<'a, 'b, C, A, const URC_CAPACITY: usize> { +pub(crate) struct NetDevice<'a, 'b, C, A, const URC_CAPACITY: usize> { ch: &'b state::Runner<'a>, config: &'b mut C, at_client: A, @@ -57,33 +57,58 @@ where } pub(crate) async fn init(&mut self) -> Result<(), Error> { - // Initilize a new ublox device to a known state (set RS232 settings) + // Initialize a new ublox device to a known state (set RS232 settings) debug!("Initializing module"); // Hard reset module self.reset().await?; self.at_client.send(&SoftwareVersion).await?; + self.at_client.send(&SetEcho { on: EchoOn::Off }).await?; + self.at_client + .send(&SetWifiConfig { + config_param: WifiConfigParam::DropNetworkOnLinkLoss(OnOff::On), + }) + .await?; + + // Disable all power savings for now + self.at_client + .send(&SetWifiConfig { + config_param: WifiConfigParam::PowerSaveMode(PowerSaveMode::ActiveMode), + }) + .await?; + #[cfg(feature = "internal-network-stack")] if let Some(size) = C::TLS_IN_BUFFER_SIZE { self.at_client - .send(&SetPeerConfiguration { - parameter: PeerConfigParameter::TlsInBuffer(size), + .send(&crate::command::data_mode::SetPeerConfiguration { + parameter: crate::command::data_mode::types::PeerConfigParameter::TlsInBuffer( + size, + ), }) .await?; } + #[cfg(feature = "internal-network-stack")] if let Some(size) = C::TLS_OUT_BUFFER_SIZE { self.at_client - .send(&SetPeerConfiguration { - parameter: PeerConfigParameter::TlsOutBuffer(size), + .send(&crate::command::data_mode::SetPeerConfiguration { + parameter: crate::command::data_mode::types::PeerConfigParameter::TlsOutBuffer( + size, + ), }) .await?; } + self.ch.mark_initialized(); + Ok(()) } - pub async fn run(&mut self) -> ! { + pub async fn run(&mut self) -> Result<(), Error> { + if self.ch.link_state(None) == LinkState::Uninitialized { + self.init().await?; + } + loop { let event = self.urc_subscription.next_message_pure().await; @@ -92,11 +117,11 @@ where continue; }; - self.handle_urc(event).await; + self.handle_urc(event).await?; } } - async fn handle_urc(&mut self, event: Urc) { + async fn handle_urc(&mut self, event: Urc) -> Result<(), Error> { debug!("GOT URC event"); match event { Urc::StartUp => { @@ -110,44 +135,56 @@ where con.wifi_state = WiFiState::Connected; con.network .replace(WifiNetwork::new_station(bssid, channel)); - con.activated = true; }), Urc::WifiLinkDisconnected(WifiLinkDisconnected { reason, .. }) => { - self.ch.update_connection_with(|con| match reason { - DisconnectReason::NetworkDisabled => { - con.wifi_state = WiFiState::Inactive; - } - DisconnectReason::SecurityProblems => { - error!("Wifi Security Problems"); - con.wifi_state = WiFiState::NotConnected; - } - _ => { - con.wifi_state = WiFiState::NotConnected; + self.ch.update_connection_with(|con| { + con.wifi_state = match reason { + DisconnectReason::NetworkDisabled => { + con.network.take(); + warn!("Wifi network disabled!"); + WiFiState::Inactive + } + DisconnectReason::SecurityProblems => { + error!("Wifi Security Problems"); + WiFiState::SecurityProblems + } + _ => WiFiState::NotConnected, } }) } - Urc::WifiAPUp(_) => todo!(), - Urc::WifiAPDown(_) => todo!(), - Urc::WifiAPStationConnected(_) => todo!(), - Urc::WifiAPStationDisconnected(_) => todo!(), - Urc::EthernetLinkUp(_) => todo!(), - Urc::EthernetLinkDown(_) => todo!(), + Urc::WifiAPUp(_) => warn!("Not yet implemented [WifiAPUp]"), + Urc::WifiAPDown(_) => warn!("Not yet implemented [WifiAPDown]"), + Urc::WifiAPStationConnected(_) => warn!("Not yet implemented [WifiAPStationConnected]"), + Urc::WifiAPStationDisconnected(_) => { + warn!("Not yet implemented [WifiAPStationDisconnected]") + } + Urc::EthernetLinkUp(_) => warn!("Not yet implemented [EthernetLinkUp]"), + Urc::EthernetLinkDown(_) => warn!("Not yet implemented [EthernetLinkDown]"), Urc::NetworkUp(NetworkUp { interface_id }) => { drop(event); - self.network_status_callback(interface_id).await.ok(); + self.network_status_callback(interface_id).await?; } Urc::NetworkDown(NetworkDown { interface_id }) => { drop(event); - self.network_status_callback(interface_id).await.ok(); + self.network_status_callback(interface_id).await?; } - Urc::NetworkError(_) => todo!(), + Urc::NetworkError(_) => warn!("Not yet implemented [NetworkError]"), _ => {} } + + Ok(()) } async fn network_status_callback(&mut self, interface_id: u8) -> Result<(), Error> { + // Normally a check for this interface type being + // `InterfaceType::WifiStation`` should be made but there is a bug in + // uConnect which gives the type `InterfaceType::Unknown` when the + // credentials have been restored from persistent memory. This although + // the wifi station has been started. So we assume that this type is + // also ok. let NetworkStatusResponse { - status: NetworkStatus::InterfaceType(InterfaceType::WifiStation), + status: + NetworkStatus::InterfaceType(InterfaceType::WifiStation | InterfaceType::Unknown), .. } = self .at_client @@ -241,6 +278,7 @@ where Ok(()) } + #[allow(dead_code)] pub async fn restart(&mut self, store: bool) -> Result<(), Error> { warn!("Soft resetting Ublox Short Range"); if store { @@ -266,7 +304,11 @@ where let fut = async { loop { // Ignore AT results until we are successful in EDM mode - if let Ok(_) = self.at_client.send(SwitchToEdmCommand).await { + if let Ok(_) = self + .at_client + .send(&crate::command::edm::SwitchToEdmCommand) + .await + { // After executing the data mode command or the extended data // mode command, a delay of 50 ms is required before start of // data transmission. @@ -281,12 +323,6 @@ where .await .map_err(|_| Error::Timeout)?; - self.at_client - .send(crate::command::system::SetEcho { - on: crate::command::system::types::EchoOn::Off, - }) - .await?; - Ok(()) } } diff --git a/src/asynch/resources.rs b/src/asynch/resources.rs index 90cbe50..f115962 100644 --- a/src/asynch/resources.rs +++ b/src/asynch/resources.rs @@ -1,38 +1,38 @@ use atat::{ResponseSlot, UrcChannel}; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; -use super::{runner::URC_SUBSCRIBERS, state, UbloxUrc}; +use super::{ + runner::{MAX_CMD_LEN, URC_SUBSCRIBERS}, + state, UbloxUrc, +}; -pub struct Resources< - const CMD_BUF_SIZE: usize, - const INGRESS_BUF_SIZE: usize, - const URC_CAPACITY: usize, -> { +pub struct Resources { pub(crate) ch: state::State, pub(crate) res_slot: ResponseSlot, + pub(crate) req_slot: Channel, 1>, pub(crate) urc_channel: UrcChannel, - pub(crate) cmd_buf: [u8; CMD_BUF_SIZE], pub(crate) ingress_buf: [u8; INGRESS_BUF_SIZE], } -impl Default - for Resources +impl Default + for Resources { fn default() -> Self { Self::new() } } -impl - Resources +impl + Resources { pub fn new() -> Self { Self { ch: state::State::new(), res_slot: ResponseSlot::new(), + req_slot: Channel::new(), urc_channel: UrcChannel::new(), - cmd_buf: [0; CMD_BUF_SIZE], ingress_buf: [0; INGRESS_BUF_SIZE], } } diff --git a/src/asynch/runner.rs b/src/asynch/runner.rs index 335d809..e255e14 100644 --- a/src/asynch/runner.rs +++ b/src/asynch/runner.rs @@ -1,31 +1,60 @@ use super::{ - control::{Control, ControlResources}, + control::Control, network::NetDevice, - state, Resources, UbloxUrc, + state::{self, LinkState}, + Resources, UbloxUrc, }; -#[cfg(feature = "edm")] -use crate::command::edm::SwitchToEdmCommand; use crate::{ - asynch::at_udp_socket::AtUdpSocket, - command::{ - data_mode::{self, ChangeMode}, - Urc, - }, + asynch::control::ProxyClient, + command::data_mode::{self, ChangeMode}, WifiConfig, }; use atat::{ asynch::{AtatClient, SimpleClient}, AtatIngress as _, UrcChannel, }; -use embassy_futures::select::Either; -use embassy_net::{ - udp::{PacketMetadata, UdpSocket}, - Ipv4Address, -}; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use embassy_time::{Duration, Instant, Timer}; use embedded_io_async::{BufRead, Read, Write}; +#[cfg(feature = "ppp")] +pub(crate) const URC_SUBSCRIBERS: usize = 2; +#[cfg(feature = "ppp")] +type Digester = atat::AtDigester; + +#[cfg(feature = "internal-network-stack")] pub(crate) const URC_SUBSCRIBERS: usize = 3; +#[cfg(feature = "internal-network-stack")] +type Digester = crate::command::custom_digest::EdmDigester; + +pub(crate) const MAX_CMD_LEN: usize = 256; + +async fn at_bridge<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>( + mut sink: impl Write, + source: impl Read, + req_slot: &Channel, 1>, + ingress: &mut atat::Ingress< + 'a, + Digester, + UbloxUrc, + INGRESS_BUF_SIZE, + URC_CAPACITY, + URC_SUBSCRIBERS, + >, +) -> ! { + ingress.clear(); + + let tx_fut = async { + loop { + let msg = req_slot.receive().await; + let _ = sink.write_all(&msg).await; + } + }; + + embassy_futures::join::join(tx_fut, ingress.read_from(source)).await; + + unreachable!() +} /// Background runner for the Ublox Module. /// @@ -36,18 +65,12 @@ pub struct Runner<'a, R, W, C, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY ch: state::Runner<'a>, config: C, - pub urc_channel: &'a UrcChannel, + pub urc_channel: &'a UrcChannel, - pub ingress: atat::Ingress< - 'a, - atat::AtDigester, - Urc, - INGRESS_BUF_SIZE, - URC_CAPACITY, - URC_SUBSCRIBERS, - >, - pub cmd_buf: &'a mut [u8], + pub ingress: + atat::Ingress<'a, Digester, UbloxUrc, INGRESS_BUF_SIZE, URC_CAPACITY, URC_SUBSCRIBERS>, pub res_slot: &'a atat::ResponseSlot, + pub req_slot: &'a Channel, 1>, #[cfg(feature = "ppp")] ppp_runner: Option>, @@ -60,15 +83,15 @@ where W: Write, C: WifiConfig<'a> + 'a, { - pub fn new( + pub fn new( iface: (R, W), - resources: &'a mut Resources, + resources: &'a mut Resources, config: C, ) -> Self { let ch_runner = state::Runner::new(&mut resources.ch); let ingress = atat::Ingress::new( - atat::AtDigester::new(), + Digester::new(), &mut resources.ingress_buf, &resources.res_slot, &resources.urc_channel, @@ -82,20 +105,21 @@ where urc_channel: &resources.urc_channel, ingress, - cmd_buf: &mut resources.cmd_buf, res_slot: &resources.res_slot, + req_slot: &resources.req_slot, #[cfg(feature = "ppp")] ppp_runner: None, } } - pub fn control<'r, D: embassy_net::driver::Driver>( - &self, - resources: &'r mut ControlResources, - stack: &'r embassy_net::Stack, - ) -> Control<'a, 'r, URC_CAPACITY> { - Control::new(self.ch.clone(), &self.urc_channel, resources, stack) + pub fn control(&self) -> Control<'a, INGRESS_BUF_SIZE, URC_CAPACITY> { + Control::new( + self.ch.clone(), + &self.urc_channel, + self.req_slot.sender(), + &self.res_slot, + ) } #[cfg(feature = "ppp")] @@ -109,191 +133,239 @@ where } #[cfg(feature = "internal-network-stack")] - pub fn internal_stack(&mut self) -> state::Device { - state::Device { - shared: &self.ch.shared, - urc_subscription: self.urc_channel.subscribe().unwrap(), + pub fn internal_stack( + &mut self, + ) -> super::ublox_stack::Device<'a, INGRESS_BUF_SIZE, URC_CAPACITY> { + super::ublox_stack::Device { + state_ch: self.ch.clone(), + at_client: core::cell::RefCell::new(ProxyClient::new( + self.req_slot.sender(), + &self.res_slot, + )), + urc_channel: &self.urc_channel, } } - pub async fn run(mut self, stack: &embassy_net::Stack) -> ! { - #[cfg(feature = "ppp")] - let mut ppp_runner = self.ppp_runner.take().unwrap(); - - let at_config = atat::Config::default(); - loop { - // Run the cellular device from full power down to the - // `DataEstablished` state, handling power on, module configuration, - // network registration & operator selection and PDP context - // activation along the way. - // - // This is all done directly on the serial line, before setting up - // virtual channels through multiplexing. - { - let at_client = atat::asynch::Client::new( - &mut self.iface.1, - self.res_slot, - self.cmd_buf, - at_config, + #[cfg(feature = "internal-network-stack")] + pub async fn run(&mut self) -> ! { + let device_fut = async { + loop { + let mut device = NetDevice::new( + &self.ch, + &mut self.config, + ProxyClient::new(self.req_slot.sender(), &self.res_slot), + self.urc_channel, ); - let mut wifi_device = - NetDevice::new(&self.ch, &mut self.config, at_client, self.urc_channel); - // Clean up and start from completely powered off state. Ignore URCs in the process. - self.ingress.clear(); + if let Err(e) = device.init().await { + error!("WiFi init failed {:?}", e); + continue; + }; - match embassy_futures::select::select( - self.ingress.read_from(&mut self.iface.0), - wifi_device.init(), - ) - .await - { - Either::First(_) => { - // This has return type never (`-> !`) - unreachable!() - } - Either::Second(Err(_)) => { - // Reboot the wifi module and try again! - continue; - } - Either::Second(Ok(_)) => { - // All good! We are now ready to start communication services! - } - } + let _ = device.run().await; } + }; + + embassy_futures::join::join( + device_fut, + at_bridge( + &mut self.iface.1, + &mut self.iface.0, + &self.req_slot, + &mut self.ingress, + ), + ) + .await; + + unreachable!() + } - #[cfg(feature = "ppp")] - let ppp_fut = async { + #[cfg(feature = "ppp")] + pub async fn run( + &mut self, + stack: &embassy_net::Stack, + ) -> ! { + let at_config = atat::Config::default(); + + let network_fut = async { + loop { + // Allow control to send/receive AT commands directly on the + // UART, until we are ready to establish connection using PPP + + // Send "+++" to escape data mode, and enter command mode + // warn!("Escaping to command mode!"); + // Timer::after_secs(5).await; + // self.iface.1.write_all(b"+++").await.ok(); + // Timer::after_secs(1).await; let mut iface = super::ReadWriteAdapter(&mut self.iface.0, &mut self.iface.1); + iface.write_all(b"+++").await.ok(); + let mut buf = [0u8; 8]; - let mut fails = 0; - let mut last_start = None; - - loop { - if let Some(last_start) = last_start { - Timer::at(last_start + Duration::from_secs(10)).await; - // Do not attempt to start too fast. - - // If was up stably for at least 1 min, reset fail counter. - if Instant::now() > last_start + Duration::from_secs(60) { - fails = 0; - } else { - fails += 1; - if fails == 10 { - warn!("modem: PPP failed too much, rebooting modem."); - break; - } - } + let _ = embassy_time::with_timeout(Duration::from_millis(500), async { + loop { + iface.read(&mut buf).await.ok(); } - last_start = Some(Instant::now()); - - { - let mut buf = [0u8; 64]; - - let mut at_client = SimpleClient::new( - &mut iface, - atat::AtDigester::::new(), - &mut buf, - at_config, - ); - - // Send AT command `ATO3` to enter PPP mode - let res = at_client - .send(&ChangeMode { - mode: data_mode::types::Mode::PPPMode, + }) + .await; + + let _ = embassy_futures::select::select( + at_bridge( + &mut self.iface.1, + &mut self.iface.0, + &self.req_slot, + &mut self.ingress, + ), + self.ch.wait_for_link_state(LinkState::Up), + ) + .await; + + #[cfg(feature = "ppp")] + let ppp_fut = async { + let mut iface = super::ReadWriteAdapter(&mut self.iface.0, &mut self.iface.1); + + loop { + self.ch.wait_for_link_state(LinkState::Up).await; + + { + let mut buf = [0u8; 8]; + let mut at_client = SimpleClient::new( + &mut iface, + atat::AtDigester::::new(), + &mut buf, + at_config, + ); + + // Send AT command `ATO3` to enter PPP mode + let res = at_client + .send(&ChangeMode { + mode: data_mode::types::Mode::PPPMode, + }) + .await; + + if let Err(e) = res { + warn!("ppp dial failed {:?}", e); + continue; + } + + // Drain the UART + let _ = embassy_time::with_timeout(Duration::from_millis(500), async { + loop { + iface.read(&mut buf).await.ok(); + } }) .await; - - if let Err(e) = res { - warn!("ppp dial failed {:?}", e); - continue; } - drop(at_client); - - // Drain the UART - let _ = embassy_time::with_timeout(Duration::from_secs(2), async { - loop { - iface.read(&mut buf).await.ok(); - } - }) - .await; + info!("RUNNING PPP"); + let res = self + .ppp_runner + .as_mut() + .unwrap() + .run(&mut iface, C::PPP_CONFIG, |ipv4| { + let Some(addr) = ipv4.address else { + warn!("PPP did not provide an IP address."); + return; + }; + let mut dns_servers = heapless::Vec::new(); + for s in ipv4.dns_servers.iter().flatten() { + let _ = dns_servers + .push(embassy_net::Ipv4Address::from_bytes(&s.0)); + } + let config = + embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { + address: embassy_net::Ipv4Cidr::new( + embassy_net::Ipv4Address::from_bytes(&addr.0), + 0, + ), + gateway: None, + dns_servers, + }); + + stack.set_config_v4(config); + }) + .await; - Timer::after(Duration::from_millis(100)).await; + info!("ppp failed: {:?}", res); } - - info!("RUNNING PPP"); - let res = ppp_runner - .run(&mut iface, C::PPP_CONFIG, |ipv4| { - let Some(addr) = ipv4.address else { - warn!("PPP did not provide an IP address."); - return; - }; - let mut dns_servers = heapless::Vec::new(); - for s in ipv4.dns_servers.iter().flatten() { - let _ = - dns_servers.push(embassy_net::Ipv4Address::from_bytes(&s.0)); + }; + + let at_fut = async { + use crate::asynch::at_udp_socket::AtUdpSocket; + use embassy_net::udp::{PacketMetadata, UdpSocket}; + + let mut rx_meta = [PacketMetadata::EMPTY; 1]; + let mut tx_meta = [PacketMetadata::EMPTY; 1]; + let mut socket_rx_buf = [0u8; 64]; + let mut socket_tx_buf = [0u8; 64]; + let mut socket = UdpSocket::new( + stack, + &mut rx_meta, + &mut socket_rx_buf, + &mut tx_meta, + &mut socket_tx_buf, + ); + + socket.bind(AtUdpSocket::PPP_AT_PORT).unwrap(); + let at_socket = AtUdpSocket(socket); + + at_bridge(&at_socket, &at_socket, &self.req_slot, &mut self.ingress).await; + }; + + let break_fut = async { + let mut fails = 0; + let mut last_start = None; + + self.ch.wait_for_initialized().await; + + loop { + if let Some(last_start) = last_start { + info!("LINK DOWN! Attempt: {}", fails); + Timer::at(last_start + Duration::from_secs(10)).await; + // Do not attempt to start too fast. + + // If was up, and stable for at least 1 min, reset fail counter. + if Instant::now() > last_start + Duration::from_secs(60) { + fails = 0; + } else { + fails += 1; + if fails == 2 { + warn!("modem: Link down too much, rebooting modem."); + break; + } } - let config = - embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { - address: embassy_net::Ipv4Cidr::new( - embassy_net::Ipv4Address::from_bytes(&addr.0), - 0, - ), - gateway: None, - dns_servers, - }); - - stack.set_config_v4(config); - }) - .await; - - info!("ppp failed: {:?}", res); - } - }; - - let network_fut = async { - stack.wait_config_up().await; - - let mut rx_meta = [PacketMetadata::EMPTY; 1]; - let mut tx_meta = [PacketMetadata::EMPTY; 1]; - let mut socket_rx_buf = [0u8; 64]; - let mut socket_tx_buf = [0u8; 64]; - let mut socket = UdpSocket::new( - stack, - &mut rx_meta, - &mut socket_rx_buf, - &mut tx_meta, - &mut socket_tx_buf, - ); + } + last_start = Some(Instant::now()); - let endpoint = stack.config_v4().unwrap(); + self.ch.wait_for_link_state(LinkState::Down).await; + } + }; - info!("Socket bound!"); - socket - .bind((Ipv4Address::new(172, 30, 0, 252), AtUdpSocket::PPP_AT_PORT)) - .unwrap(); + embassy_futures::select::select3(ppp_fut, at_fut, break_fut).await; - let at_socket = AtUdpSocket(socket); + warn!("Breaking WiFi network loop"); + } + }; + + let device_fut = async { + loop { + let mut device = NetDevice::new( + &self.ch, + &mut self.config, + ProxyClient::new(self.req_slot.sender(), &self.res_slot), + self.urc_channel, + ); - let at_client = - atat::asynch::Client::new(&at_socket, self.res_slot, self.cmd_buf, at_config); + if let Err(e) = device.init().await { + error!("WiFi init failed {:?}", e); + continue; + }; - let mut wifi_device = - NetDevice::new(&self.ch, &mut self.config, at_client, self.urc_channel); + let _ = device.run().await; + } + }; - embassy_futures::join::join(self.ingress.read_from(&at_socket), wifi_device.run()) - .await; - }; + embassy_futures::join::join(device_fut, network_fut).await; - match embassy_futures::select::select(ppp_fut, network_fut).await { - Either::First(_) => { - warn!("Breaking to reboot module from PPP"); - } - Either::Second(_) => { - warn!("Breaking to reboot module from network runner"); - } - } - } + unreachable!() } } diff --git a/src/asynch/state.rs b/src/asynch/state.rs index 8ec431e..1f9e376 100644 --- a/src/asynch/state.rs +++ b/src/asynch/state.rs @@ -8,27 +8,29 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_sync::waitqueue::WakerRegistration; -use crate::connection::{WiFiState, WifiConnection}; +use crate::connection::WifiConnection; /// The link state of a network device. #[derive(PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum LinkState { + /// Device is not yet initialized. + Uninitialized, /// The link is down. Down, /// The link is up. Up, } -pub struct State { +pub(crate) struct State { shared: Mutex>, } impl State { - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self { shared: Mutex::new(RefCell::new(Shared { - link_state: LinkState::Down, + link_state: LinkState::Uninitialized, wifi_connection: WifiConnection::new(), state_waker: WakerRegistration::new(), connection_waker: WakerRegistration::new(), @@ -38,7 +40,7 @@ impl State { } /// State of the LinkState -pub struct Shared { +pub(crate) struct Shared { link_state: LinkState, wifi_connection: WifiConnection, state_waker: WakerRegistration, @@ -46,34 +48,64 @@ pub struct Shared { } #[derive(Clone)] -pub struct Runner<'d> { +pub(crate) struct Runner<'d> { shared: &'d Mutex>, } impl<'d> Runner<'d> { - pub fn new(state: &'d mut State) -> Self { + pub(crate) fn new(state: &'d mut State) -> Self { Self { shared: &state.shared, } } - pub fn set_link_state(&mut self, state: LinkState) { + pub(crate) fn mark_initialized(&self) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.link_state = state; + s.link_state = LinkState::Down; s.state_waker.wake(); - }); + }) } - pub fn link_state(&mut self, cx: &mut Context) -> LinkState { + pub(crate) async fn wait_for_initialized(&self) { + if self.link_state(None) != LinkState::Uninitialized { + return; + } + + poll_fn(|cx| { + if self.link_state(Some(cx)) != LinkState::Uninitialized { + return Poll::Ready(()); + } + Poll::Pending + }) + .await + } + + pub(crate) fn link_state(&self, cx: Option<&mut Context>) -> LinkState { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.state_waker.register(cx.waker()); + if let Some(cx) = cx { + s.state_waker.register(cx.waker()); + } s.link_state }) } - pub fn update_connection_with(&self, f: impl FnOnce(&mut WifiConnection)) { + pub(crate) async fn wait_for_link_state(&self, ls: LinkState) { + if self.link_state(None) == ls { + return; + } + + poll_fn(|cx| { + if self.link_state(Some(cx)) == ls { + return Poll::Ready(()); + } + Poll::Pending + }) + .await + } + + pub(crate) fn update_connection_with(&self, f: impl FnOnce(&mut WifiConnection)) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); f(&mut s.wifi_connection); @@ -82,20 +114,18 @@ impl<'d> Runner<'d> { s.wifi_connection.is_connected() ); - if s.wifi_connection.network_up - && matches!(s.wifi_connection.wifi_state, WiFiState::Connected) - { - s.link_state = LinkState::Up; + s.link_state = if s.wifi_connection.is_connected() { + LinkState::Up } else { - s.link_state = LinkState::Down; - } + LinkState::Down + }; s.state_waker.wake(); s.connection_waker.wake(); }) } - pub fn is_connected(&self, cx: Option<&mut Context>) -> bool { + pub(crate) fn is_connected(&self, cx: Option<&mut Context>) -> bool { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); if let Some(cx) = cx { @@ -105,15 +135,13 @@ impl<'d> Runner<'d> { }) } - pub async fn wait_connection_change(&mut self) -> bool { - let old_state = self - .shared - .lock(|s| s.borrow().wifi_connection.is_connected()); + pub(crate) async fn wait_connection_change(&self) -> bool { + let old_state = self.is_connected(None); poll_fn(|cx| { - let current_state = self.is_connected(Some(cx)); - if current_state != old_state { - return Poll::Ready(current_state); + let new_state = self.is_connected(Some(cx)); + if new_state != old_state { + return Poll::Ready(new_state); } Poll::Pending }) diff --git a/src/command/custom_digest.rs b/src/command/custom_digest.rs index 1db8831..2b4b537 100644 --- a/src/command/custom_digest.rs +++ b/src/command/custom_digest.rs @@ -10,6 +10,12 @@ use super::edm::types::{AUTOCONNECTMESSAGE, STARTUPMESSAGE}; #[derive(Debug, Default)] pub struct EdmDigester; +impl EdmDigester { + pub fn new() -> Self { + Self + } +} + impl Digester for EdmDigester { fn digest<'a>(&mut self, buf: &'a [u8]) -> (DigestResult<'a>, usize) { // TODO: Handle module restart, tests and set default startupmessage in client, and optimize this! @@ -215,7 +221,7 @@ impl Digester for EdmDigester { // assert_eq!(urc_c.read(), None); // } -// /// Regular response with traling regular response.. +// /// Regular response with trailing regular response.. // #[test] // fn at_urc() { // let mut at_pars: Ingress< diff --git a/src/command/data_mode/mod.rs b/src/command/data_mode/mod.rs index 616b03d..ca9cdfb 100644 --- a/src/command/data_mode/mod.rs +++ b/src/command/data_mode/mod.rs @@ -27,7 +27,7 @@ pub struct ChangeMode { /// Connects to an enabled service on a remote device. When the host connects to a /// service on a remote device, it implicitly registers to receive the "Connection Closed" /// event. -#[cfg(feature = "ublox-sockets")] +#[cfg(feature = "internal-network-stack")] #[derive(Clone, AtatCmd)] #[at_cmd("+UDCP", ConnectPeerResponse, timeout_ms = 5000)] pub struct ConnectPeer<'a> { @@ -38,12 +38,12 @@ pub struct ConnectPeer<'a> { /// 5.3 Close peer connection +UDCPC /// /// Closes an existing peer connection. -#[cfg(feature = "ublox-sockets")] +#[cfg(feature = "internal-network-stack")] #[derive(Clone, AtatCmd)] #[at_cmd("+UDCPC", NoResponse, timeout_ms = 1000)] pub struct ClosePeerConnection { #[at_arg(position = 0, len = 1)] - pub peer_handle: PeerHandle, + pub peer_handle: ublox_sockets::PeerHandle, } /// 5.4 Default remote peer +UDDRP @@ -66,7 +66,7 @@ pub struct SetDefaultRemotePeer<'a> { /// 5.5 Peer list +UDLP /// /// This command reads the connected peers (peer handle). -#[cfg(feature = "ublox-sockets")] +#[cfg(feature = "internal-network-stack")] #[derive(Clone, AtatCmd)] #[at_cmd("+UDLP?", PeerListResponse, timeout_ms = 1000)] pub struct PeerList; @@ -129,7 +129,7 @@ pub struct SetWatchdogSettings { /// /// Writes peer configuration. /// -/// Suported parameter tags | Software Version +/// Supported parameter tags | Software Version /// ------------------------|------------------ /// 0,1 | All versions /// 2 | >= 4.0.0 diff --git a/src/command/data_mode/responses.rs b/src/command/data_mode/responses.rs index 03354b0..fddac87 100644 --- a/src/command/data_mode/responses.rs +++ b/src/command/data_mode/responses.rs @@ -2,7 +2,7 @@ use atat::atat_derive::AtatResp; /// 5.2 Connect peer +UDCP -#[cfg(feature = "ublox-sockets")] +#[cfg(feature = "internal-network-stack")] #[derive(Clone, AtatResp)] pub struct ConnectPeerResponse { #[at_arg(position = 0)] @@ -10,7 +10,7 @@ pub struct ConnectPeerResponse { } /// 5.5 Peer list +UDLP -#[cfg(feature = "ublox-sockets")] +#[cfg(feature = "internal-network-stack")] #[derive(Clone, AtatResp)] pub struct PeerListResponse { #[at_arg(position = 0)] diff --git a/src/command/data_mode/urc.rs b/src/command/data_mode/urc.rs index 4ec7f75..b768654 100644 --- a/src/command/data_mode/urc.rs +++ b/src/command/data_mode/urc.rs @@ -3,7 +3,7 @@ use super::types::*; /// 5.10 Peer connected +UUDPC -#[cfg(feature = "ublox-sockets")] +#[cfg(feature = "internal-network-stack")] #[derive(Debug, PartialEq, Clone, atat::atat_derive::AtatResp)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PeerConnected { @@ -30,7 +30,7 @@ pub struct PeerConnected { } /// 5.11 Peer disconnected +UUDPD -#[cfg(feature = "ublox-sockets")] +#[cfg(feature = "internal-network-stack")] #[derive(Debug, PartialEq, Clone, atat::atat_derive::AtatResp)] pub struct PeerDisconnected { #[at_arg(position = 0)] diff --git a/src/command/edm/mod.rs b/src/command/edm/mod.rs index 691c47b..61694fb 100644 --- a/src/command/edm/mod.rs +++ b/src/command/edm/mod.rs @@ -69,7 +69,7 @@ impl atat::AtatCmd for EdmAtCmdWrapper { return Err(atat::InternalError::InvalidResponse); } - // Recieved OK response code in EDM response? + // Received OK response code in EDM response? match resp .windows(b"\r\nOK".len()) .position(|window| window == b"\r\nOK") diff --git a/src/command/edm/urc.rs b/src/command/edm/urc.rs index 4b78503..ef629e6 100644 --- a/src/command/edm/urc.rs +++ b/src/command/edm/urc.rs @@ -73,7 +73,7 @@ impl AtatUrc for EdmEvent { }; let payload_len = calc_payload_len(resp); if resp.len() != payload_len + EDM_OVERHEAD { - error!("[Parse URC lenght Error] {:?}", LossyStr(resp)); + error!("[Parse URC length Error] {:?}", LossyStr(resp)); return None; } diff --git a/src/command/general/mod.rs b/src/command/general/mod.rs index 66e1b65..08ce8d8 100644 --- a/src/command/general/mod.rs +++ b/src/command/general/mod.rs @@ -61,8 +61,8 @@ pub struct SerialNumber2; /// /// Identificationinformation. #[derive(Clone, AtatCmd)] -#[at_cmd("I0", IdentificationInfomationTypeCodeResponse, timeout_ms = 1000)] -pub struct IdentificationInfomationTypeCode; +#[at_cmd("I0", IdentificationInformationTypeCodeResponse, timeout_ms = 1000)] +pub struct IdentificationInformationTypeCode; /// 3.9 Identification information I9 /// @@ -70,17 +70,17 @@ pub struct IdentificationInfomationTypeCode; #[derive(Clone, AtatCmd)] #[at_cmd( "I9", - IdentificationInfomationSoftwareVersionResponse, + IdentificationInformationSoftwareVersionResponse, timeout_ms = 1000 )] -pub struct IdentificationInfomationSoftwareVersion; +pub struct IdentificationInformationSoftwareVersion; /// 3.9 Identification information I10 /// /// Identificationinformation. #[derive(Clone, AtatCmd)] -#[at_cmd("I10", IdentificationInfomationMCUIDResponse, timeout_ms = 1000)] -pub struct IdentificationInfomationMCUID; +#[at_cmd("I10", IdentificationInformationMCUIDResponse, timeout_ms = 1000)] +pub struct IdentificationInformationMCUID; /// 3.11 Set greeting text +CSGT /// diff --git a/src/command/general/responses.rs b/src/command/general/responses.rs index aea6181..155c8d3 100644 --- a/src/command/general/responses.rs +++ b/src/command/general/responses.rs @@ -37,7 +37,7 @@ pub struct SerialNumberResponse { /// 3.10 Identification information I0 #[derive(Clone, AtatResp)] -pub struct IdentificationInfomationTypeCodeResponse { +pub struct IdentificationInformationTypeCodeResponse { /// Text string that identifies the serial number. #[at_arg(position = 0)] pub serial_number: String<64>, @@ -45,7 +45,7 @@ pub struct IdentificationInfomationTypeCodeResponse { /// 3.10 Identification information I9 #[derive(Clone, AtatResp)] -pub struct IdentificationInfomationSoftwareVersionResponse { +pub struct IdentificationInformationSoftwareVersionResponse { /// Text string that identifies the firmware version. #[at_arg(position = 0)] pub version: String<64>, @@ -53,7 +53,7 @@ pub struct IdentificationInfomationSoftwareVersionResponse { /// 3.10 Identification information I10 #[derive(Clone, AtatResp)] -pub struct IdentificationInfomationMCUIDResponse { +pub struct IdentificationInformationMCUIDResponse { /// Text string that identifies the serial number. #[at_arg(position = 0)] pub serial_number: String<64>, diff --git a/src/command/gpio/responses.rs b/src/command/gpio/responses.rs index e51e308..9a4bbac 100644 --- a/src/command/gpio/responses.rs +++ b/src/command/gpio/responses.rs @@ -6,7 +6,7 @@ use atat::atat_derive::AtatResp; #[derive(Clone, PartialEq, AtatResp)] pub struct ReadGPIOResponse { #[at_arg(position = 0)] - id: GPIOId, + pub id: GPIOId, #[at_arg(position = 1)] - value: GPIOValue, + pub value: GPIOValue, } diff --git a/src/command/mod.rs b/src/command/mod.rs index 6b2b680..e237b39 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -30,11 +30,11 @@ pub enum Urc { #[at_urc("+STARTUP")] StartUp, /// 5.10 Peer connected +UUDPC - #[cfg(feature = "ublox-sockets")] + #[cfg(feature = "internal-network-stack")] #[at_urc("+UUDPC")] PeerConnected(data_mode::urc::PeerConnected), /// 5.11 Peer disconnected +UUDPD - #[cfg(feature = "ublox-sockets")] + #[cfg(feature = "internal-network-stack")] #[at_urc("+UUDPD")] PeerDisconnected(data_mode::urc::PeerDisconnected), /// 7.15 Wi-Fi Link connected +UUWLE diff --git a/src/command/ping/types.rs b/src/command/ping/types.rs index 16fdaa8..3999593 100644 --- a/src/command/ping/types.rs +++ b/src/command/ping/types.rs @@ -18,12 +18,12 @@ use atat::atat_derive::AtatEnum; /// provides the TTL value received in the incoming packet. /// - Range: 1-255 /// - Default value: 32 -// pub type TTL = (u8, Option); +// pub type TTL = (u8, Option); /// The time in milliseconds to wait after an echo reply response before sending the next /// echo request. /// - Range: 0-60000 /// - Default value: 1000 -// pub type Inteval = u16; +// pub type Interval = u16; #[derive(Debug, PartialEq, Clone, Copy, AtatEnum)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/src/command/system/types.rs b/src/command/system/types.rs index dbec961..b00e393 100644 --- a/src/command/system/types.rs +++ b/src/command/system/types.rs @@ -40,7 +40,7 @@ pub enum DSRAssertMode { /// DSR line when no remote peers are connected. See Connect Peer +UDCP and Default /// remote peer +UDDRP for definition of the remote peer. This applies to both incoming /// and outgoing connections. - WhenPeersConected = 2, + WhenPeersConnected = 2, } /// Echo on diff --git a/src/command/wifi/types.rs b/src/command/wifi/types.rs index 5772825..ccad35d 100644 --- a/src/command/wifi/types.rs +++ b/src/command/wifi/types.rs @@ -106,7 +106,7 @@ pub enum WifiStationConfigParameter { /// is the Wi-Fi beacon listen interval in units of beacon /// interval. The factory default value is 0, listen on all beacons. /// - Valid values 0-16 - WiFiBeaconListenInteval = 300, + WiFiBeaconListenInterval = 300, /// Enables DTIM in power save. If the DTIM is enabled and the /// module is in power save, the access point sends an indication when new /// data is available. If disabled, the module polls for data every beacon @@ -244,7 +244,7 @@ pub enum WifiStationConfig { /// interval. The factory default value is 0, listen on all beacons. /// - Valid values 0-16 #[at_arg(value = 300)] - WiFiBeaconListenInteval(u8), + WiFiBeaconListenInterval(u8), /// Enables DTIM in power save. If the DTIM is enabled and the /// module is in power save, the access point sends an indication when new /// data is available. If disabled, the module polls for data every beacon @@ -384,7 +384,7 @@ pub enum WifiStationConfigR { /// interval. The factory default value is 0, listen on all beacons. /// - Valid values 0-16 #[at_arg(value = 300)] - WiFiBeaconListenInteval(u8), + WiFiBeaconListenInterval(u8), /// Enables DTIM in power save. If the DTIM is enabled and the /// module is in power save, the access point sends an indication when new /// data is available. If disabled, the module polls for data every beacon