diff options
| -rw-r--r-- | embassy-net-nrf91/src/context.rs | 86 | ||||
| -rw-r--r-- | embassy-net-nrf91/src/lib.rs | 5 | ||||
| -rw-r--r-- | 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; | |||
| 5 | use at_commands::builder::CommandBuilder; | 5 | use at_commands::builder::CommandBuilder; |
| 6 | use at_commands::parser::CommandParser; | 6 | use at_commands::parser::CommandParser; |
| 7 | use heapless::Vec; | 7 | use heapless::Vec; |
| 8 | use embassy_time::{Timer, Duration}; | ||
| 8 | 9 | ||
| 9 | /// Provides a higher level API for controlling a given context. | 10 | /// Provides a higher level API for controlling a given context. |
| 10 | pub struct Control<'a> { | 11 | pub struct Control<'a> { |
| @@ -23,6 +24,8 @@ pub struct Config<'a> { | |||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | /// Authentication protocol. | 26 | /// Authentication protocol. |
| 27 | #[derive(Clone, Copy, PartialEq, Debug)] | ||
| 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | #[repr(u8)] | 29 | #[repr(u8)] |
| 27 | pub enum AuthProt { | 30 | pub enum AuthProt { |
| 28 | /// No authentication. | 31 | /// No authentication. |
| @@ -84,7 +87,7 @@ impl<'a> Control<'a> { | |||
| 84 | } | 87 | } |
| 85 | 88 | ||
| 86 | /// Configures the modem with the provided config. | 89 | /// Configures the modem with the provided config. |
| 87 | pub async fn configure(&self, config: Config<'_>) -> Result<(), Error> { | 90 | pub async fn configure(&self, config: &Config<'_>) -> Result<(), Error> { |
| 88 | let mut cmd: [u8; 256] = [0; 256]; | 91 | let mut cmd: [u8; 256] = [0; 256]; |
| 89 | let mut buf: [u8; 256] = [0; 256]; | 92 | let mut buf: [u8; 256] = [0; 256]; |
| 90 | 93 | ||
| @@ -118,9 +121,64 @@ impl<'a> Control<'a> { | |||
| 118 | let n = self.control.at_command(op, &mut buf).await; | 121 | let n = self.control.at_command(op, &mut buf).await; |
| 119 | CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; | 122 | CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; |
| 120 | 123 | ||
| 124 | let op = CommandBuilder::create_set(&mut cmd, true) | ||
| 125 | .named("%XPDNCFG") | ||
| 126 | .with_int_parameter(1) | ||
| 127 | .finish() | ||
| 128 | .map_err(|_| Error::BufferTooSmall)?; | ||
| 129 | let n = self.control.at_command(op, &mut buf).await; | ||
| 130 | CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; | ||
| 131 | |||
| 132 | |||
| 133 | |||
| 134 | Ok(()) | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Attach to the PDN | ||
| 138 | pub async fn attach(&self) -> Result<(), Error> { | ||
| 139 | let mut cmd: [u8; 256] = [0; 256]; | ||
| 140 | let mut buf: [u8; 256] = [0; 256]; | ||
| 141 | let op = CommandBuilder::create_set(&mut cmd, true) | ||
| 142 | .named("+CGATT") | ||
| 143 | .with_int_parameter(1) | ||
| 144 | .finish() | ||
| 145 | .map_err(|_| Error::BufferTooSmall)?; | ||
| 146 | let n = self.control.at_command(op, &mut buf).await; | ||
| 147 | CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; | ||
| 148 | Ok(()) | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Read current connectivity status for modem. | ||
| 152 | pub async fn detach(&self) -> Result<(), Error> { | ||
| 153 | let mut cmd: [u8; 256] = [0; 256]; | ||
| 154 | let mut buf: [u8; 256] = [0; 256]; | ||
| 155 | let op = CommandBuilder::create_set(&mut cmd, true) | ||
| 156 | .named("+CGATT") | ||
| 157 | .with_int_parameter(0) | ||
| 158 | .finish() | ||
| 159 | .map_err(|_| Error::BufferTooSmall)?; | ||
| 160 | let n = self.control.at_command(op, &mut buf).await; | ||
| 161 | CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; | ||
| 121 | Ok(()) | 162 | Ok(()) |
| 122 | } | 163 | } |
| 123 | 164 | ||
| 165 | async fn attached(&self) -> Result<bool, Error> { | ||
| 166 | let mut cmd: [u8; 256] = [0; 256]; | ||
| 167 | let mut buf: [u8; 256] = [0; 256]; | ||
| 168 | |||
| 169 | let op = CommandBuilder::create_query(&mut cmd, true) | ||
| 170 | .named("+CGATT") | ||
| 171 | .finish() | ||
| 172 | .map_err(|_| Error::BufferTooSmall)?; | ||
| 173 | let n = self.control.at_command(op, &mut buf).await; | ||
| 174 | let (res,) = CommandParser::parse(&buf[..n]) | ||
| 175 | .expect_identifier(b"+CGATT: ") | ||
| 176 | .expect_int_parameter() | ||
| 177 | .expect_identifier(b"\r\nOK") | ||
| 178 | .finish()?; | ||
| 179 | Ok(res == 1) | ||
| 180 | } | ||
| 181 | |||
| 124 | /// Read current connectivity status for modem. | 182 | /// Read current connectivity status for modem. |
| 125 | pub async fn status(&self) -> Result<Status, Error> { | 183 | pub async fn status(&self) -> Result<Status, Error> { |
| 126 | let mut cmd: [u8; 256] = [0; 256]; | 184 | let mut cmd: [u8; 256] = [0; 256]; |
| @@ -162,7 +220,6 @@ impl<'a> Control<'a> { | |||
| 162 | 220 | ||
| 163 | let ip = if let Some(ip) = ip1 { | 221 | let ip = if let Some(ip) = ip1 { |
| 164 | let ip = IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?; | 222 | let ip = IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?; |
| 165 | self.control.open_raw_socket().await; | ||
| 166 | Some(ip) | 223 | Some(ip) |
| 167 | } else { | 224 | } else { |
| 168 | None | 225 | None |
| @@ -219,4 +276,29 @@ impl<'a> Control<'a> { | |||
| 219 | dns, | 276 | dns, |
| 220 | }) | 277 | }) |
| 221 | } | 278 | } |
| 279 | |||
| 280 | /// Run a control loop for this context, ensuring that reaattach is handled. | ||
| 281 | pub async fn run<F: Fn(&Status)>(&self, config: &Config<'_>, reattach: F) -> Result<(), Error> { | ||
| 282 | self.configure(config).await?; | ||
| 283 | while !self.attached().await? { | ||
| 284 | Timer::after(Duration::from_secs(1)).await; | ||
| 285 | } | ||
| 286 | let status = self.status().await?; | ||
| 287 | let mut fd = self.control.open_raw_socket().await; | ||
| 288 | reattach(&status); | ||
| 289 | |||
| 290 | loop { | ||
| 291 | if !self.attached().await? { | ||
| 292 | // TODO: self.control.close_raw_socket(fd).await; | ||
| 293 | self.attach().await?; | ||
| 294 | while !self.attached().await? { | ||
| 295 | Timer::after(Duration::from_secs(1)).await; | ||
| 296 | } | ||
| 297 | let status = self.status().await?; | ||
| 298 | // TODO: let mut fd = self.control.open_raw_socket().await; | ||
| 299 | reattach(&status); | ||
| 300 | } | ||
| 301 | Timer::after(Duration::from_secs(10)).await; | ||
| 302 | } | ||
| 303 | } | ||
| 222 | } | 304 | } |
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> { | |||
| 844 | /// Open the raw socket used for sending/receiving IP packets. | 844 | /// Open the raw socket used for sending/receiving IP packets. |
| 845 | /// | 845 | /// |
| 846 | /// This must be done after `AT+CFUN=1` (?) | 846 | /// This must be done after `AT+CFUN=1` (?) |
| 847 | async fn open_raw_socket(&self) { | 847 | async fn open_raw_socket(&self) -> u32 { |
| 848 | let mut msg: Message = unsafe { mem::zeroed() }; | 848 | let mut msg: Message = unsafe { mem::zeroed() }; |
| 849 | msg.channel = 2; // data | 849 | msg.channel = 2; // data |
| 850 | msg.id = 0x7001_0004; // open socket | 850 | msg.id = 0x7001_0004; // open socket |
| @@ -867,7 +867,8 @@ impl<'a> Control<'a> { | |||
| 867 | assert_eq!(status, 0); | 867 | assert_eq!(status, 0); |
| 868 | assert_eq!(msg.param_len, 16); | 868 | assert_eq!(msg.param_len, 16); |
| 869 | let fd = u32::from_le_bytes(msg.param[12..16].try_into().unwrap()); | 869 | let fd = u32::from_le_bytes(msg.param[12..16].try_into().unwrap()); |
| 870 | debug!("got FD: {}", fd); | 870 | trace!("got FD: {}", fd); |
| 871 | fd | ||
| 871 | } | 872 | } |
| 872 | } | 873 | } |
| 873 | 874 | ||
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 | |||
| @@ -50,6 +50,43 @@ async fn net_task(stack: &'static Stack<embassy_net_nrf91::NetDriver<'static>>) | |||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | #[embassy_executor::task] | 52 | #[embassy_executor::task] |
| 53 | async fn control_task( | ||
| 54 | control: &'static context::Control<'static>, | ||
| 55 | config: context::Config<'static>, | ||
| 56 | stack: &'static Stack<embassy_net_nrf91::NetDriver<'static>>, | ||
| 57 | ) { | ||
| 58 | unwrap!( | ||
| 59 | control | ||
| 60 | .run(&config, |status| { | ||
| 61 | let Some(IpAddr::V4(addr)) = status.ip else { | ||
| 62 | panic!("Unexpected IP address"); | ||
| 63 | }; | ||
| 64 | let addr = Ipv4Address(addr.octets()); | ||
| 65 | |||
| 66 | let gateway = if let Some(IpAddr::V4(addr)) = status.gateway { | ||
| 67 | Some(Ipv4Address(addr.octets())) | ||
| 68 | } else { | ||
| 69 | None | ||
| 70 | }; | ||
| 71 | |||
| 72 | let mut dns_servers = Vec::new(); | ||
| 73 | for dns in status.dns.iter() { | ||
| 74 | if let IpAddr::V4(ip) = dns { | ||
| 75 | unwrap!(dns_servers.push(Ipv4Address(ip.octets()))); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | stack.set_config_v4(embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { | ||
| 80 | address: Ipv4Cidr::new(addr, 32), | ||
| 81 | gateway, | ||
| 82 | dns_servers, | ||
| 83 | })); | ||
| 84 | }) | ||
| 85 | .await | ||
| 86 | ); | ||
| 87 | } | ||
| 88 | |||
| 89 | #[embassy_executor::task] | ||
| 53 | async fn blink_task(pin: AnyPin) { | 90 | async fn blink_task(pin: AnyPin) { |
| 54 | let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); | 91 | let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); |
| 55 | loop { | 92 | loop { |
| @@ -117,50 +154,20 @@ async fn main(spawner: Spawner) { | |||
| 117 | 154 | ||
| 118 | unwrap!(spawner.spawn(net_task(stack))); | 155 | unwrap!(spawner.spawn(net_task(stack))); |
| 119 | 156 | ||
| 120 | let control = context::Control::new(control, 0).await; | 157 | static CONTROL: StaticCell<context::Control<'static>> = StaticCell::new(); |
| 158 | let control = CONTROL.init(context::Control::new(control, 0).await); | ||
| 121 | 159 | ||
| 122 | unwrap!( | 160 | unwrap!(spawner.spawn(control_task( |
| 123 | control | 161 | control, |
| 124 | .configure(context::Config { | 162 | context::Config { |
| 125 | apn: "iot.nat.es", | 163 | apn: "iot.nat.es", |
| 126 | auth_prot: context::AuthProt::Pap, | 164 | auth_prot: context::AuthProt::Pap, |
| 127 | auth: Some(("orange", "orange")), | 165 | auth: Some(("orange", "orange")), |
| 128 | }) | 166 | }, |
| 129 | .await | 167 | stack |
| 130 | ); | 168 | ))); |
| 131 | 169 | ||
| 132 | info!("waiting for attach..."); | 170 | stack.wait_config_up().await; |
| 133 | |||
| 134 | let mut status = unwrap!(control.status().await); | ||
| 135 | while !status.attached && status.ip.is_none() { | ||
| 136 | Timer::after_millis(1000).await; | ||
| 137 | status = unwrap!(control.status().await); | ||
| 138 | info!("STATUS: {:?}", status); | ||
| 139 | } | ||
| 140 | |||
| 141 | let Some(IpAddr::V4(addr)) = status.ip else { | ||
| 142 | panic!("Unexpected IP address"); | ||
| 143 | }; | ||
| 144 | let addr = Ipv4Address(addr.octets()); | ||
| 145 | |||
| 146 | let gateway = if let Some(IpAddr::V4(addr)) = status.gateway { | ||
| 147 | Some(Ipv4Address(addr.octets())) | ||
| 148 | } else { | ||
| 149 | None | ||
| 150 | }; | ||
| 151 | |||
| 152 | let mut dns_servers = Vec::new(); | ||
| 153 | for dns in status.dns { | ||
| 154 | if let IpAddr::V4(ip) = dns { | ||
| 155 | unwrap!(dns_servers.push(Ipv4Address(ip.octets()))); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | stack.set_config_v4(embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { | ||
| 160 | address: Ipv4Cidr::new(addr, 32), | ||
| 161 | gateway, | ||
| 162 | dns_servers, | ||
| 163 | })); | ||
| 164 | 171 | ||
| 165 | let mut rx_buffer = [0; 4096]; | 172 | let mut rx_buffer = [0; 4096]; |
| 166 | let mut tx_buffer = [0; 4096]; | 173 | let mut tx_buffer = [0; 4096]; |
| @@ -172,7 +179,7 @@ async fn main(spawner: Spawner) { | |||
| 172 | let host_addr = embassy_net::Ipv4Address::from_str("45.79.112.203").unwrap(); | 179 | let host_addr = embassy_net::Ipv4Address::from_str("45.79.112.203").unwrap(); |
| 173 | if let Err(e) = socket.connect((host_addr, 4242)).await { | 180 | if let Err(e) = socket.connect((host_addr, 4242)).await { |
| 174 | warn!("connect error: {:?}", e); | 181 | warn!("connect error: {:?}", e); |
| 175 | Timer::after_secs(1).await; | 182 | Timer::after_secs(10).await; |
| 176 | continue; | 183 | continue; |
| 177 | } | 184 | } |
| 178 | info!("Connected to {:?}", socket.remote_endpoint()); | 185 | info!("Connected to {:?}", socket.remote_endpoint()); |
