From 372e45dabc0cfd3eb495e902665bb752a67aa804 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 4 Sep 2024 18:47:26 +0200 Subject: [PATCH] Add context run task --- embassy-net-nrf91/src/context.rs | 86 +++++++++++++++++- embassy-net-nrf91/src/lib.rs | 5 +- examples/nrf9160/src/bin/modem_tcp_client.rs | 93 +++++++++++--------- 3 files changed, 137 insertions(+), 47 deletions(-) diff --git a/embassy-net-nrf91/src/context.rs b/embassy-net-nrf91/src/context.rs index 954830417..f73719224 100644 --- a/embassy-net-nrf91/src/context.rs +++ b/embassy-net-nrf91/src/context.rs @@ -5,6 +5,7 @@ use core::str::FromStr; use at_commands::builder::CommandBuilder; use at_commands::parser::CommandParser; use heapless::Vec; +use embassy_time::{Timer, Duration}; /// Provides a higher level API for controlling a given context. pub struct Control<'a> { @@ -23,6 +24,8 @@ pub struct Config<'a> { } /// Authentication protocol. +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum AuthProt { /// No authentication. @@ -84,7 +87,7 @@ impl<'a> Control<'a> { } /// Configures the modem with the provided config. - pub async fn configure(&self, config: Config<'_>) -> Result<(), Error> { + pub async fn configure(&self, config: &Config<'_>) -> Result<(), Error> { let mut cmd: [u8; 256] = [0; 256]; let mut buf: [u8; 256] = [0; 256]; @@ -118,9 +121,64 @@ impl<'a> Control<'a> { let n = self.control.at_command(op, &mut buf).await; CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; + let op = CommandBuilder::create_set(&mut cmd, true) + .named("%XPDNCFG") + .with_int_parameter(1) + .finish() + .map_err(|_| Error::BufferTooSmall)?; + let n = self.control.at_command(op, &mut buf).await; + CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; + + + Ok(()) } + /// Attach to the PDN + pub async fn attach(&self) -> Result<(), Error> { + let mut cmd: [u8; 256] = [0; 256]; + let mut buf: [u8; 256] = [0; 256]; + let op = CommandBuilder::create_set(&mut cmd, true) + .named("+CGATT") + .with_int_parameter(1) + .finish() + .map_err(|_| Error::BufferTooSmall)?; + let n = self.control.at_command(op, &mut buf).await; + CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; + Ok(()) + } + + /// Read current connectivity status for modem. + pub async fn detach(&self) -> Result<(), Error> { + let mut cmd: [u8; 256] = [0; 256]; + let mut buf: [u8; 256] = [0; 256]; + let op = CommandBuilder::create_set(&mut cmd, true) + .named("+CGATT") + .with_int_parameter(0) + .finish() + .map_err(|_| Error::BufferTooSmall)?; + let n = self.control.at_command(op, &mut buf).await; + CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; + Ok(()) + } + + async fn attached(&self) -> Result { + let mut cmd: [u8; 256] = [0; 256]; + let mut buf: [u8; 256] = [0; 256]; + + let op = CommandBuilder::create_query(&mut cmd, true) + .named("+CGATT") + .finish() + .map_err(|_| Error::BufferTooSmall)?; + let n = self.control.at_command(op, &mut buf).await; + let (res,) = CommandParser::parse(&buf[..n]) + .expect_identifier(b"+CGATT: ") + .expect_int_parameter() + .expect_identifier(b"\r\nOK") + .finish()?; + Ok(res == 1) + } + /// Read current connectivity status for modem. pub async fn status(&self) -> Result { let mut cmd: [u8; 256] = [0; 256]; @@ -162,7 +220,6 @@ impl<'a> Control<'a> { let ip = if let Some(ip) = ip1 { let ip = IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?; - self.control.open_raw_socket().await; Some(ip) } else { None @@ -219,4 +276,29 @@ impl<'a> Control<'a> { dns, }) } + + /// Run a control loop for this context, ensuring that reaattach is handled. + pub async fn run(&self, config: &Config<'_>, reattach: F) -> Result<(), Error> { + self.configure(config).await?; + while !self.attached().await? { + Timer::after(Duration::from_secs(1)).await; + } + let status = self.status().await?; + let mut fd = self.control.open_raw_socket().await; + reattach(&status); + + loop { + if !self.attached().await? { + // TODO: self.control.close_raw_socket(fd).await; + self.attach().await?; + while !self.attached().await? { + Timer::after(Duration::from_secs(1)).await; + } + let status = self.status().await?; + // TODO: let mut fd = self.control.open_raw_socket().await; + reattach(&status); + } + Timer::after(Duration::from_secs(10)).await; + } + } } diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index a60e27d97..ab3c6f327 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs @@ -844,7 +844,7 @@ impl<'a> Control<'a> { /// Open the raw socket used for sending/receiving IP packets. /// /// This must be done after `AT+CFUN=1` (?) - async fn open_raw_socket(&self) { + async fn open_raw_socket(&self) -> u32 { let mut msg: Message = unsafe { mem::zeroed() }; msg.channel = 2; // data msg.id = 0x7001_0004; // open socket @@ -867,7 +867,8 @@ impl<'a> Control<'a> { assert_eq!(status, 0); assert_eq!(msg.param_len, 16); let fd = u32::from_le_bytes(msg.param[12..16].try_into().unwrap()); - debug!("got FD: {}", fd); + trace!("got FD: {}", fd); + fd } } diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index c65e6e153..a6f42eb3b 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs @@ -49,6 +49,43 @@ async fn net_task(stack: &'static Stack>) stack.run().await } +#[embassy_executor::task] +async fn control_task( + control: &'static context::Control<'static>, + config: context::Config<'static>, + stack: &'static Stack>, +) { + unwrap!( + control + .run(&config, |status| { + let Some(IpAddr::V4(addr)) = status.ip else { + panic!("Unexpected IP address"); + }; + let addr = Ipv4Address(addr.octets()); + + let gateway = if let Some(IpAddr::V4(addr)) = status.gateway { + Some(Ipv4Address(addr.octets())) + } else { + None + }; + + let mut dns_servers = Vec::new(); + for dns in status.dns.iter() { + if let IpAddr::V4(ip) = dns { + unwrap!(dns_servers.push(Ipv4Address(ip.octets()))); + } + } + + stack.set_config_v4(embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { + address: Ipv4Cidr::new(addr, 32), + gateway, + dns_servers, + })); + }) + .await + ); +} + #[embassy_executor::task] async fn blink_task(pin: AnyPin) { let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); @@ -117,50 +154,20 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(net_task(stack))); - let control = context::Control::new(control, 0).await; + static CONTROL: StaticCell> = StaticCell::new(); + let control = CONTROL.init(context::Control::new(control, 0).await); - unwrap!( - control - .configure(context::Config { - apn: "iot.nat.es", - auth_prot: context::AuthProt::Pap, - auth: Some(("orange", "orange")), - }) - .await - ); + unwrap!(spawner.spawn(control_task( + control, + context::Config { + apn: "iot.nat.es", + auth_prot: context::AuthProt::Pap, + auth: Some(("orange", "orange")), + }, + stack + ))); - info!("waiting for attach..."); - - let mut status = unwrap!(control.status().await); - while !status.attached && status.ip.is_none() { - Timer::after_millis(1000).await; - status = unwrap!(control.status().await); - info!("STATUS: {:?}", status); - } - - let Some(IpAddr::V4(addr)) = status.ip else { - panic!("Unexpected IP address"); - }; - let addr = Ipv4Address(addr.octets()); - - let gateway = if let Some(IpAddr::V4(addr)) = status.gateway { - Some(Ipv4Address(addr.octets())) - } else { - None - }; - - let mut dns_servers = Vec::new(); - for dns in status.dns { - if let IpAddr::V4(ip) = dns { - unwrap!(dns_servers.push(Ipv4Address(ip.octets()))); - } - } - - stack.set_config_v4(embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { - address: Ipv4Cidr::new(addr, 32), - gateway, - dns_servers, - })); + stack.wait_config_up().await; let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; @@ -172,7 +179,7 @@ async fn main(spawner: Spawner) { let host_addr = embassy_net::Ipv4Address::from_str("45.79.112.203").unwrap(); if let Err(e) = socket.connect((host_addr, 4242)).await { warn!("connect error: {:?}", e); - Timer::after_secs(1).await; + Timer::after_secs(10).await; continue; } info!("Connected to {:?}", socket.remote_endpoint());