diff --git a/Cargo.toml b/Cargo.toml index 521c679..d4825d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,38 +15,50 @@ name = "ublox_short_range" doctest = false [dependencies] -atat = { version = "0.21", features = ["derive", "bytes"] } +# atat = { version = "0.21", features = ["derive", "bytes"] } +atat = { path = "../atat/atat", features = ["derive", "bytes"] } + # atat = { git = "https://github.com/BlackbirdHQ/atat", rev = "70283be", features = ["derive", "defmt", "bytes"] } heapless = { version = "^0.8", features = ["serde"] } no-std-net = { version = "0.6", features = ["serde"] } serde = { version = "^1", default-features = false, features = ["derive"] } # ublox-sockets = { version = "0.5", features = ["edm"], optional = true } -ublox-sockets = { git = "https://github.com/BlackbirdHQ/ublox-sockets", rev = "9f7fe54", features = ["edm"], optional = true } +ublox-sockets = { git = "https://github.com/BlackbirdHQ/ublox-sockets", rev = "9f7fe54", optional = true } postcard = "1.0.4" -portable-atomic = "1.5" +portable-atomic = "1.6" -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } +log = { version = "^0.4", default-features = false, optional = true } +defmt = { version = "^0.3", optional = true } embedded-hal = "1.0" embassy-time = "0.3" embassy-sync = "0.5" embassy-futures = "0.1" -embassy-net-driver = "0.2" embedded-nal-async = { version = "0.7" } -futures = { version = "0.3.17", default-features = false, features = [ - "async-await", -] } +futures-util = { version = "0.3.29", default-features = false } embedded-io = "0.6" embedded-io-async = "0.6" +embassy-net-ppp = { version = "0.1", optional = true } +embassy-net = { version = "0.4", features = [ + "proto-ipv4", + "medium-ip", +], optional = true } + + [features] -default = ["odin_w2xx", "ublox-sockets", "socket-tcp", "socket-udp"] +default = ["socket-tcp", "socket-udp", "odin_w2xx"] + +internal-network-stack = ["dep:ublox-sockets", "edm"] +edm = ["ublox-sockets?/edm"] +# PPP mode requires UDP sockets enabled, to be able to do AT commands over UDP port 23 +ppp = ["dep:embassy-net-ppp", "dep:embassy-net", "socket-udp"] -std = [] +socket-tcp = ["ublox-sockets?/socket-tcp", "embassy-net?/tcp"] +socket-udp = ["ublox-sockets?/socket-udp", "embassy-net?/udp"] defmt = [ "dep:defmt", @@ -54,7 +66,10 @@ defmt = [ "heapless/defmt-03", "atat/defmt", "ublox-sockets?/defmt", + "embassy-net-ppp?/defmt", + "embassy-net?/defmt", ] +log = ["dep:log", "ublox-sockets?/log", "atat/log"] odin_w2xx = [] nina_w1xx = [] @@ -63,15 +78,6 @@ anna_b1xx = [] nina_b2xx = [] nina_b3xx = [] -socket-tcp = [ - "ublox-sockets?/socket-tcp", - # "smoltcp?/socket-tcp" -] -socket-udp = [ - "ublox-sockets?/socket-udp", - # "smoltcp?/socket-udp" -] - [workspace] members = [] default-members = ["."] @@ -80,4 +86,4 @@ exclude = ["examples"] [patch.crates-io] no-std-net = { git = "https://github.com/rushmorem/no-std-net", branch = "issue-15" } -atat = { path = "../atat/atat" } \ No newline at end of file +atat = { path = "../atat/atat" } diff --git a/examples/linux.rs b/examples/linux.rs deleted file mode 100644 index 52804e9..0000000 --- a/examples/linux.rs +++ /dev/null @@ -1,152 +0,0 @@ -// use std::sync::{Arc, Mutex}; -// use std::thread; -// use std::time::Duration; - -// use linux_embedded_hal::Serial; -// use serial::{self, core::SerialPort}; - -// extern crate at_rs as at; -// extern crate env_logger; -// extern crate nb; - -// // Note this useful idiom: importing names from outer (for mod tests) scope. -// use ublox_short_range::command::*; -// use ublox_short_range::prelude::*; -// use ublox_short_range::wifi; - -// use heapless::{consts::*, spsc::Queue, String}; -// #[allow(unused_imports)] -// use defmt::{error, info, warn}; - -// #[derive(Clone, Copy)] -// struct MilliSeconds(u32); - -// trait U32Ext { -// fn s(self) -> MilliSeconds; -// fn ms(self) -> MilliSeconds; -// } - -// impl U32Ext for u32 { -// fn s(self) -> MilliSeconds { -// MilliSeconds(self / 1000) -// } -// fn ms(self) -> MilliSeconds { -// MilliSeconds(self) -// } -// } - -// struct Timer; - -// impl embedded_hal::timer::CountDown for Timer { -// type Time = MilliSeconds; -// fn start(&mut self, _duration: T) -// where -// T: Into, -// { -// // let dur = duration.into(); -// // self.timeout_time = Instant::now().checked_add(Duration::from_millis(dur.0.into())).expect(""); -// } - -// fn wait(&mut self) -> ::nb::Result<(), void::Void> { -// // if self.timeout_time - Instant::now() < Duration::from_secs(0) { -// // Ok(()) -// // } else { -// Err(nb::Error::WouldBlock) -// // } -// } -// } - -// impl embedded_hal::timer::Cancel for Timer { -// type Error = (); -// fn cancel(&mut self) -> Result<(), Self::Error> { -// Ok(()) -// } -// } - -// type SerialRxBufferLen = U4096; -// type ATRequestQueueLen = U5; -// type ATResponseQueueLen = U5; - -// static mut WIFI_REQ_Q: Option> = None; -// static mut WIFI_RES_Q: Option, ATResponseQueueLen, u8>> = -// None; - -// fn main() { -// env_logger::builder() -// .filter_level(defmt::LevelFilter::Trace) -// .init(); - -// // Serial port settings -// let settings = serial::PortSettings { -// baud_rate: serial::Baud115200, -// char_size: serial::Bits8, -// parity: serial::ParityNone, -// stop_bits: serial::Stop1, -// flow_control: serial::FlowNone, -// }; - -// // Open serial port -// let mut port = serial::open("/dev/ttyACM0").expect("Could not open serial port"); -// port.configure(&settings) -// .expect("Could not configure serial port"); - -// port.set_timeout(Duration::from_millis(2)) -// .expect("Could not set serial port timeout"); - -// unsafe { WIFI_REQ_Q = Some(Queue::u8()) }; -// unsafe { WIFI_RES_Q = Some(Queue::u8()) }; - -// let (wifi_client, parser) = at::new::<_, _, _, SerialRxBufferLen, _, _>( -// unsafe { (WIFI_REQ_Q.as_mut().unwrap(), WIFI_RES_Q.as_mut().unwrap()) }, -// Serial(port), -// Timer, -// 1000.ms(), -// ); - -// let ublox = ublox_short_range::UbloxClient::new(wifi_client); - -// let at_parser_arc = Arc::new(Mutex::new(parser)); - -// let at_parser = at_parser_arc.clone(); -// let serial_irq = thread::Builder::new() -// .name("serial_irq".to_string()) -// .spawn(move || loop { -// thread::sleep(Duration::from_millis(1)); -// if let Ok(mut at) = at_parser.lock() { -// at.handle_irq() -// } -// }) -// .unwrap(); - -// let serial_loop = thread::Builder::new() -// .name("serial_loop".to_string()) -// .spawn(move || loop { -// thread::sleep(Duration::from_millis(100)); -// if let Ok(mut at) = at_parser_arc.lock() { -// at.spin() -// } -// }) -// .unwrap(); - -// let main_loop = thread::Builder::new() -// .name("main_loop".to_string()) -// .spawn(move || { -// // let networks = wifi_client.scan().unwrap(); -// // networks.iter().for_each(|n| info!("{:?}", n.ssid)); - -// let options = wifi::options::ConnectionOptions::new() -// .ssid(String::from("E-NET1")) -// .password(String::from("pakhus47")); - -// // Attempt to connect to a wifi -// let connection = ublox.connect(options).expect("Cannot connect!"); -// info!("Connected! {:?}", connection.network); -// }) -// .unwrap(); - -// // needed otherwise it does not block till -// // the threads actually have been run -// serial_irq.join().unwrap(); -// serial_loop.join().unwrap(); -// main_loop.join().unwrap(); -// } diff --git a/examples/rpi-pico/.cargo/config.toml b/examples/rpi-pico/.cargo/config.toml index 3217579..f7e22c1 100644 --- a/examples/rpi-pico/.cargo/config.toml +++ b/examples/rpi-pico/.cargo/config.toml @@ -1,6 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# runner = "probe-rs-cli run --chip RP2040" -runner = "probe-run --chip RP2040" +runner = "probe-rs run --chip RP2040" [build] target = "thumbv6m-none-eabi" diff --git a/examples/rpi-pico/.vscode/settings.json b/examples/rpi-pico/.vscode/settings.json new file mode 100644 index 0000000..e786a02 --- /dev/null +++ b/examples/rpi-pico/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "editor.formatOnSave": true, + "[toml]": { + "editor.formatOnSave": false + }, + "rust-analyzer.cargo.features": [ + "ppp", + ], + "rust-analyzer.cargo.target": "thumbv6m-none-eabi", + "rust-analyzer.check.allTargets": false, + "rust-analyzer.linkedProjects": [], + "rust-analyzer.server.extraEnv": { + "WIFI_NETWORK": "foo", + "WIFI_PASSWORD": "foo", + } +} \ No newline at end of file diff --git a/examples/rpi-pico/Cargo.toml b/examples/rpi-pico/Cargo.toml index 0839378..196424c 100644 --- a/examples/rpi-pico/Cargo.toml +++ b/examples/rpi-pico/Cargo.toml @@ -5,10 +5,24 @@ edition = "2021" [dependencies] -ublox-short-range-rs = { path = "../../", features = ["odin_w2xx", "ublox-sockets", "socket-tcp"] } -embassy-executor = { version = "0.5", features = ["defmt", "integrated-timers", "nightly"] } -embassy-time = { version = "0.3", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-pac", "time-driver"] } +ublox-short-range-rs = { path = "../../", features = ["odin_w2xx", "defmt"] } +embassy-executor = { version = "0.5", features = [ + "defmt", + "integrated-timers", + "nightly", + "arch-cortex-m", + "executor-thread", +] } +embassy-time = { version = "0.3", features = [ + "defmt", + "defmt-timestamp-uptime", +] } +embassy-sync = { version = "0.5" } +embassy-rp = { version = "0.1.0", features = [ + "defmt", + "unstable-pac", + "time-driver", +] } embassy-futures = { version = "0.1.0" } no-std-net = { version = "0.6", features = ["serde"] } @@ -19,11 +33,31 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", + "cfg-target-has-atomic", + "unstable", +] } embedded-io-async = { version = "0.6" } heapless = "0.8" +portable-atomic = { version = "*", features = ["unsafe-assume-single-core"] } +embassy-net = { version = "0.4", optional = true, features = [ + "defmt", + "proto-ipv4", + "medium-ip", + "tcp", + "udp", +] } +embassy-net-ppp = { version = "0.1", optional = true, features = ["defmt"] } +embedded-tls = { path = "../../../embedded-tls", default-features = false, features = [ + "defmt", +] } + +[features] +internal-network-stack = ["ublox-short-range-rs/internal-network-stack"] +ppp = ["dep:embassy-net", "dep:embassy-net-ppp", "ublox-short-range-rs/ppp"] [patch.crates-io] # embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } @@ -34,13 +68,15 @@ heapless = "0.8" # embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-executor = { path = "../../../embassy/embassy-executor" } -embassy-hal-internal = { path = "../../../embassy/embassy-hal-internal" } -embassy-time = { path = "../../../embassy/embassy-time" } -embassy-futures = { path = "../../../embassy/embassy-futures" } -embassy-sync = { path = "../../../embassy/embassy-sync" } -embassy-rp = { path = "../../../embassy/embassy-rp" } -embassy-net-driver = { path = "../../../embassy/embassy-net-driver" } +# embassy-executor = { path = "../../../embassy/embassy-executor" } +# embassy-hal-internal = { path = "../../../embassy/embassy-hal-internal" } +# embassy-time = { path = "../../../embassy/embassy-time" } +# embassy-futures = { path = "../../../embassy/embassy-futures" } +# embassy-sync = { path = "../../../embassy/embassy-sync" } +# embassy-rp = { path = "../../../embassy/embassy-rp" } +# embassy-net-driver = { path = "../../../embassy/embassy-net-driver" } +# embassy-net = { path = "../../../embassy/embassy-net" } +# embassy-net-ppp = { path = "../../../embassy/embassy-net-ppp" } atat = { path = "../../../atat/atat" } ublox-sockets = { path = "../../../ublox-sockets" } no-std-net = { path = "../../../no-std-net" } diff --git a/examples/rpi-pico/rust-toolchain.toml b/examples/rpi-pico/rust-toolchain.toml new file mode 100644 index 0000000..4e3b270 --- /dev/null +++ b/examples/rpi-pico/rust-toolchain.toml @@ -0,0 +1,7 @@ +[toolchain] +channel = "nightly-2024-01-17" +components = [ "rust-src", "rustfmt", "llvm-tools" ] +targets = [ + "thumbv6m-none-eabi", + "thumbv7em-none-eabihf" +] diff --git a/examples/rpi-pico/src/bin/embassy-async.rs b/examples/rpi-pico/src/bin/embassy-async.rs index 7ef8b7a..81090c4 100644 --- a/examples/rpi-pico/src/bin/embassy-async.rs +++ b/examples/rpi-pico/src/bin/embassy-async.rs @@ -1,18 +1,16 @@ +#![cfg(feature = "internal-network-stack")] #![no_std] #![no_main] #![feature(type_alias_impl_trait)] #![feature(async_fn_in_trait)] #![allow(incomplete_features)] -#[path = "../common.rs"] -mod common; - use core::fmt::Write as _; use embassy_executor::Spawner; use embassy_futures::select::{select, Either}; -use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_26, UART1}; -use embassy_rp::uart::BufferedInterruptHandler; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUartTx}; use embassy_rp::{bind_interrupts, uart}; use embassy_time::{Duration, Timer}; use embedded_io_async::Write; @@ -22,25 +20,33 @@ use ublox_short_range::asynch::runner::Runner; use ublox_short_range::asynch::ublox_stack::dns::DnsSocket; use ublox_short_range::asynch::ublox_stack::tcp::TcpSocket; use ublox_short_range::asynch::ublox_stack::{StackResources, UbloxStack}; -use ublox_short_range::asynch::{new, State}; +use ublox_short_range::asynch::{new, Resources, State}; use ublox_short_range::atat::{self, AtatIngress}; use ublox_short_range::command::custom_digest::EdmDigester; use ublox_short_range::command::edm::urc::EdmEvent; use ublox_short_range::embedded_nal_async::AddrType; use {defmt_rtt as _, panic_probe as _}; -const RX_BUF_LEN: usize = 4096; -const URC_CAPACITY: usize = 3; +const CMD_BUF_SIZE: usize = 128; +const INGRESS_BUF_SIZE: usize = 1024; +const URC_CAPACITY: usize = 2; type AtClient = ublox_short_range::atat::asynch::Client< 'static, uart::BufferedUartTx<'static, UART1>, - RX_BUF_LEN, + INGRESS_BUF_SIZE, >; #[embassy_executor::task] async fn wifi_task( - runner: Runner<'static, AtClient, Output<'static, PIN_26>, 8, URC_CAPACITY>, + runner: InternalRunner< + 'a, + BufferedUartRx<'static, UART1>, + BufferedUartTx<'static, UART1>, + Output<'static, AnyPin>, + INGRESS_BUF_SIZE, + URC_CAPACITY, + >, ) -> ! { runner.run().await } @@ -120,14 +126,6 @@ async fn echo_task( } } -#[embassy_executor::task] -async fn ingress_task( - mut ingress: atat::Ingress<'static, EdmDigester, EdmEvent, RX_BUF_LEN, URC_CAPACITY, 2>, - mut rx: uart::BufferedUartRx<'static, UART1>, -) -> ! { - ingress.read_from(&mut rx).await -} - bind_interrupts!(struct Irqs { UART1_IRQ => BufferedInterruptHandler; }); @@ -141,46 +139,50 @@ async fn main(spawner: Spawner) { let rst = Output::new(p.PIN_26, Level::High); let mut btn = Input::new(p.PIN_27, Pull::Up); - let (tx_pin, rx_pin, rts_pin, cts_pin, uart) = - (p.PIN_24, p.PIN_25, p.PIN_23, p.PIN_22, p.UART1); + static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new(); + static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new(); - let tx_buf = &mut make_static!([0u8; 64])[..]; - let rx_buf = &mut make_static!([0u8; 64])[..]; let uart = uart::BufferedUart::new_with_rtscts( - uart, + p.UART1, Irqs, - tx_pin, - rx_pin, - rts_pin, - cts_pin, - tx_buf, - rx_buf, + p.PIN_24, + p.PIN_25, + p.PIN_23, + p.PIN_22, + TX_BUF.init([0; 16]), + RX_BUF.init([0; 16]), uart::Config::default(), ); - let (rx, tx) = uart.split(); + let (uart_rx, uart_tx) = uart.split(); + + static RESOURCES: StaticCell< + Resources, CMD_BUF_SIZE, INGRESS_BUF_SIZE, URC_CAPACITY>, + > = StaticCell::new(); + + let (net_device, mut control, runner) = ublox_short_range::asynch::new_internal( + uart_rx, + uart_tx, + RESOURCES.init(Resources::new()), + rst, + ); - let buffers = &*make_static!(atat::Buffers::new()); - let (ingress, client) = buffers.split(tx, EdmDigester::default(), atat::Config::new()); - defmt::unwrap!(spawner.spawn(ingress_task(ingress, rx))); + // Init network stack + static STACK: StaticCell>> = StaticCell::new(); + static STACK_RESOURCES: StaticCell> = StaticCell::new(); - let state = make_static!(State::new(client)); - let (net_device, mut control, runner) = new(state, &buffers.urc_channel, rst).await; + let stack = &*STACK.init(UbloxStack::new( + net_device, + STACK_RESOURCES.init(StackResources::new()), + )); - defmt::unwrap!(spawner.spawn(wifi_task(runner))); + spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(wifi_task(runner)).unwrap(); control .set_hostname("Factbird-duo-wifi-test") .await .unwrap(); - // Init network stack - let stack = &*make_static!(UbloxStack::new( - net_device, - make_static!(StackResources::<4>::new()), - )); - - defmt::unwrap!(spawner.spawn(net_task(stack))); - // And now we can use it! info!("Device initialized!"); diff --git a/examples/rpi-pico/src/bin/embassy-perf.rs b/examples/rpi-pico/src/bin/embassy-perf.rs index 796fd84..f990655 100644 --- a/examples/rpi-pico/src/bin/embassy-perf.rs +++ b/examples/rpi-pico/src/bin/embassy-perf.rs @@ -4,9 +4,6 @@ #![feature(async_fn_in_trait)] #![allow(incomplete_features)] -#[path = "../common.rs"] -mod common; - use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::gpio::{Level, Output}; diff --git a/examples/rpi-pico/src/bin/embassy-smoltcp-ppp.rs b/examples/rpi-pico/src/bin/embassy-smoltcp-ppp.rs new file mode 100644 index 0000000..f89cdb9 --- /dev/null +++ b/examples/rpi-pico/src/bin/embassy-smoltcp-ppp.rs @@ -0,0 +1,109 @@ +#![cfg(feature = "ppp")] +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::{Stack, StackResources}; +use embassy_rp::gpio::{AnyPin, Level, Output, Pin}; +use embassy_rp::peripherals::UART1; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart}; +use embassy_rp::{bind_interrupts, uart}; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use ublox_short_range::asynch::{PPPRunner, Resources}; +use {defmt_rtt as _, panic_probe as _}; + +const CMD_BUF_SIZE: usize = 128; +const INGRESS_BUF_SIZE: usize = 512; +const URC_CAPACITY: usize = 2; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::task] +async fn ppp_task( + mut runner: PPPRunner<'static, Output<'static, AnyPin>, INGRESS_BUF_SIZE, URC_CAPACITY>, + interface: BufferedUart<'static, UART1>, + stack: &'static embassy_net::Stack>, +) -> ! { + runner.run(interface, stack).await +} + +bind_interrupts!(struct Irqs { + UART1_IRQ => BufferedInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let rst = Output::new(p.PIN_26.degrade(), Level::High); + + static TX_BUF: StaticCell<[u8; 32]> = StaticCell::new(); + static RX_BUF: StaticCell<[u8; 32]> = StaticCell::new(); + let wifi_uart = uart::BufferedUart::new_with_rtscts( + p.UART1, + Irqs, + p.PIN_24, + p.PIN_25, + p.PIN_23, + p.PIN_22, + TX_BUF.init([0; 32]), + RX_BUF.init([0; 32]), + uart::Config::default(), + ); + + static RESOURCES: StaticCell> = + StaticCell::new(); + + let (net_device, mut control, runner) = + ublox_short_range::asynch::new_ppp(RESOURCES.init(Resources::new()), rst); + + // Generate random seed + let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. + + // Init network stack + static STACK: StaticCell>> = StaticCell::new(); + static STACK_RESOURCES: StaticCell> = StaticCell::new(); + + let stack = &*STACK.init(Stack::new( + net_device, + embassy_net::Config::default(), + STACK_RESOURCES.init(StackResources::new()), + seed, + )); + + spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(ppp_task(runner, wifi_uart, &stack)).unwrap(); + + stack.wait_config_up().await; + + Timer::after(Duration::from_secs(1)).await; + + control + .set_hostname("Factbird-duo-wifi-test") + .await + .unwrap(); + + control.join_open("Test").await; + + // // Then we can use it! + // let mut rx_buffer = [0; 4096]; + // let mut tx_buffer = [0; 4096]; + // let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + + // socket.set_timeout(Some(Duration::from_secs(10))); + + // let remote_endpoint = (Ipv4Address::new(93, 184, 216, 34), 12345); + // info!("connecting to {:?}...", remote_endpoint); + // let r = socket.connect(remote_endpoint).await; + // if let Err(e) = r { + // warn!("connect error: {:?}", e); + // return; + // } + // info!("TCP connected!"); +} diff --git a/examples/rpi-pico/src/common.rs b/examples/rpi-pico/src/common.rs deleted file mode 100644 index e341bb1..0000000 --- a/examples/rpi-pico/src/common.rs +++ /dev/null @@ -1,56 +0,0 @@ -use embassy_rp::uart; -use ublox_short_range::atat; - -// pub struct TxWrap(pub TX); - -// impl embedded_io::Io for TxWrap { -// type Error = ::Error; -// } - -// impl embedded_io::asynch::Write for TxWrap { -// async fn write(&mut self, buf: &[u8]) -> Result { -// self.0.write(buf).await -// } -// } - -// impl atat::UartExt for TxWrap> { -// type Error = (); - -// fn set_baudrate(&mut self, baud: u32) -> Result<(), Self::Error> { -// let r = T::regs(); - -// let clk_base = 125_000_000; - -// let baud_rate_div = (8 * clk_base) / baud; -// let mut baud_ibrd = baud_rate_div >> 7; -// let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; - -// if baud_ibrd == 0 { -// baud_ibrd = 1; -// baud_fbrd = 0; -// } else if baud_ibrd >= 65535 { -// baud_ibrd = 65535; -// baud_fbrd = 0; -// } - -// r.uartcr().modify(|m| { -// m.set_uarten(false); -// }); - -// // Load PL011's baud divisor registers -// r.uartibrd() -// .write_value(embassy_rp::pac::uart::regs::Uartibrd(baud_ibrd)); -// r.uartfbrd() -// .write_value(embassy_rp::pac::uart::regs::Uartfbrd(baud_fbrd)); - -// // PL011 needs a (dummy) line control register write to latch in the -// // divisors. We don't want to actually change LCR contents here. -// r.uartlcr_h().modify(|_| {}); - -// r.uartcr().modify(|m| { -// m.set_uarten(true); -// }); - -// Ok(()) -// } -// } diff --git a/src/asynch/control.rs b/src/asynch/control.rs index e826bf5..fd658c6 100644 --- a/src/asynch/control.rs +++ b/src/asynch/control.rs @@ -36,59 +36,9 @@ impl<'a, AT: AtatClient> Control<'a, AT> { Self { state_ch, at } } - pub(crate) async fn init(&mut self) -> Result<(), Error> { - debug!("Initalizing ublox control"); - // read MAC addr. - // let mut resp = self.at.send_edm(GetWifiMac).await?; - // self.state_ch.set_ethernet_address( - // hex::from_hex(resp.mac_addr.as_mut_slice()) - // .unwrap() - // .try_into() - // .unwrap(), - // ); - - // let country = countries::WORLD_WIDE_XX; - // let country_info = CountryInfo { - // country_abbrev: [country.code[0], country.code[1], 0, 0], - // country_code: [country.code[0], country.code[1], 0, 0], - // rev: if country.rev == 0 { - // -1 - // } else { - // country.rev as _ - // }, - // }; - // self.set_iovar("country", &country_info.to_bytes()).await; - - // // set country takes some time, next ioctls fail if we don't wait. - // Timer::after(Duration::from_millis(100)).await; - - // // Set antenna to chip antenna - // self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; - - // self.set_iovar_u32("bus:txglom", 0).await; - // Timer::after(Duration::from_millis(100)).await; - // //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? - // //Timer::after(Duration::from_millis(100)).await; - // self.set_iovar_u32("ampdu_ba_wsize", 8).await; - // Timer::after(Duration::from_millis(100)).await; - // self.set_iovar_u32("ampdu_mpdu", 4).await; - // Timer::after(Duration::from_millis(100)).await; - // //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes - - // // set wifi up - // self.ioctl(ControlType::Set, IOCTL_CMD_UP, 0, &mut []).await; - - // Timer::after(Duration::from_millis(100)).await; - - // self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto - // self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any - - Ok(()) - } - pub async fn set_hostname(&mut self, hostname: &str) -> Result<(), Error> { self.at - .send_edm(SetNetworkHostName { + .send(SetNetworkHostName { host_name: hostname, }) .await?; @@ -98,7 +48,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { async fn get_wifi_status(&mut self) -> Result { match self .at - .send_edm(GetWifiStatus { + .send(GetWifiStatus { status_id: StatusId::Status, }) .await? @@ -112,7 +62,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { async fn get_connected_ssid(&mut self) -> Result, Error> { match self .at - .send_edm(GetWifiStatus { + .send(GetWifiStatus { status_id: StatusId::SSID, }) .await? @@ -135,14 +85,14 @@ impl<'a, AT: AtatClient> Control<'a, AT> { } self.at - .send_edm(SetWifiStationConfig { + .send(SetWifiStationConfig { config_id: CONFIG_ID, config_param: WifiStationConfig::ActiveOnStartup(OnOff::Off), }) .await?; self.at - .send_edm(SetWifiStationConfig { + .send(SetWifiStationConfig { config_id: CONFIG_ID, config_param: WifiStationConfig::SSID( heapless::String::try_from(ssid).map_err(|_| Error::Overflow)?, @@ -151,14 +101,14 @@ impl<'a, AT: AtatClient> Control<'a, AT> { .await?; self.at - .send_edm(SetWifiStationConfig { + .send(SetWifiStationConfig { config_id: CONFIG_ID, config_param: WifiStationConfig::Authentication(Authentication::Open), }) .await?; self.at - .send_edm(ExecWifiStationAction { + .send(ExecWifiStationAction { config_id: CONFIG_ID, action: WifiStationAction::Activate, }) @@ -183,21 +133,21 @@ impl<'a, AT: AtatClient> Control<'a, AT> { } self.at - .send_edm(ExecWifiStationAction { + .send(ExecWifiStationAction { config_id: CONFIG_ID, action: WifiStationAction::Reset, }) .await?; self.at - .send_edm(SetWifiStationConfig { + .send(SetWifiStationConfig { config_id: CONFIG_ID, config_param: WifiStationConfig::ActiveOnStartup(OnOff::Off), }) .await?; self.at - .send_edm(SetWifiStationConfig { + .send(SetWifiStationConfig { config_id: CONFIG_ID, config_param: WifiStationConfig::SSID( heapless::String::try_from(ssid).map_err(|_| Error::Overflow)?, @@ -206,14 +156,14 @@ impl<'a, AT: AtatClient> Control<'a, AT> { .await?; self.at - .send_edm(SetWifiStationConfig { + .send(SetWifiStationConfig { config_id: CONFIG_ID, config_param: WifiStationConfig::Authentication(Authentication::WpaWpa2Psk), }) .await?; self.at - .send_edm(SetWifiStationConfig { + .send(SetWifiStationConfig { config_id: CONFIG_ID, config_param: WifiStationConfig::WpaPskOrPassphrase( heapless::String::try_from(passphrase).map_err(|_| Error::Overflow)?, @@ -222,7 +172,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { .await?; self.at - .send_edm(ExecWifiStationAction { + .send(ExecWifiStationAction { config_id: CONFIG_ID, action: WifiStationAction::Activate, }) @@ -240,7 +190,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { WifiStatusVal::Disabled => {} WifiStatusVal::Disconnected | WifiStatusVal::Connected => { self.at - .send_edm(ExecWifiStationAction { + .send(ExecWifiStationAction { config_id: CONFIG_ID, action: WifiStationAction::Deactivate, }) @@ -277,7 +227,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { } pub async fn gpio_set(&mut self, id: GPIOId, value: GPIOValue) -> Result<(), Error> { - self.at.send_edm(WriteGPIO { id, value }).await?; + self.at.send(WriteGPIO { id, value }).await?; Ok(()) } @@ -294,7 +244,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { info!("Importing {:?} bytes as {:?}", data.len(), name); self.at - .send_edm(PrepareSecurityDataImport { + .send(PrepareSecurityDataImport { data_type, data_size: data.len(), internal_name: name, @@ -304,7 +254,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { let import_data = self .at - .send_edm(SendSecurityDataImport { + .send(SendSecurityDataImport { data: atat::serde_bytes::Bytes::new(data), }) .await?; diff --git a/src/asynch/internal_stack.rs b/src/asynch/internal_stack.rs new file mode 100644 index 0000000..02cdbac --- /dev/null +++ b/src/asynch/internal_stack.rs @@ -0,0 +1,117 @@ +// pub mod ublox_stack; + +use core::mem::MaybeUninit; + +use atat::{asynch::Client, AtatIngress}; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, mutex::Mutex}; +use embedded_hal::digital::OutputPin; +use embedded_io_async::{Read, Write}; + +use crate::command::custom_digest::EdmDigester; + +pub use super::resources::UbxResources as Resources; + +use super::{control::Control, runner::Runner, state, AtHandle, UbloxUrc}; + +pub struct InternalRunner< + 'a, + R: Read, + W: Write, + RST: OutputPin, + const INGRESS_BUF_SIZE: usize, + const URC_CAPACITY: usize, +> { + pub cellular_runner: Runner<'a, Client<'a, W, INGRESS_BUF_SIZE>, RST, URC_CAPACITY>, + pub ingress: atat::Ingress<'a, EdmDigester, UbloxUrc, INGRESS_BUF_SIZE, URC_CAPACITY, 2>, + pub reader: R, +} + +impl< + 'a, + R: Read, + W: Write, + RST: OutputPin, + const INGRESS_BUF_SIZE: usize, + const URC_CAPACITY: usize, + > InternalRunner<'a, R, W, RST, INGRESS_BUF_SIZE, URC_CAPACITY> +{ + pub async fn run(&mut self) -> ! { + self.cellular_runner.init().await.unwrap(); + + embassy_futures::join::join( + self.ingress.read_from(&mut self.reader), + self.cellular_runner.run(), + ) + .await; + core::unreachable!() + } +} + +pub fn new_internal< + 'a, + R: Read, + W: Write, + RST: OutputPin, + const CMD_BUF_SIZE: usize, + const INGRESS_BUF_SIZE: usize, + const URC_CAPACITY: usize, +>( + reader: R, + writer: W, + resources: &'a mut Resources, + reset: RST, +) -> ( + state::Device<'a, Client<'a, W, INGRESS_BUF_SIZE>, URC_CAPACITY>, + Control<'a, Client<'a, W, INGRESS_BUF_SIZE>>, + InternalRunner<'a, R, W, RST, INGRESS_BUF_SIZE, URC_CAPACITY>, +) { + // safety: this is a self-referential struct, however: + // - it can't move while the `'a` borrow is active. + // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again. + let at_client_uninit: *mut MaybeUninit>> = + (&mut resources.at_client + as *mut MaybeUninit>>) + .cast(); + + unsafe { &mut *at_client_uninit }.write(Mutex::new(Client::new( + writer, + &resources.res_slot, + &mut resources.cmd_buf, + atat::Config::default(), + ))); + + let at_client = unsafe { (&*at_client_uninit).assume_init_ref() }; + + let (ch_runner, net_device) = state::new( + &mut resources.ch, + AtHandle(at_client), + resources.urc_channel.subscribe().unwrap(), + ); + + let control = Control::new(ch_runner.state_runner(), AtHandle(at_client)); + + let runner = Runner::new( + ch_runner, + AtHandle(at_client), + reset, + resources.urc_channel.subscribe().unwrap(), + ); + + // runner.init().await.unwrap(); + // control.init().await.unwrap(); + + let ingress = atat::Ingress::new( + EdmDigester, + &mut resources.ingress_buf, + &resources.res_slot, + &resources.urc_channel, + ); + + let runner = InternalRunner { + cellular_runner: runner, + ingress, + reader, + }; + + (net_device, control, runner) +} diff --git a/src/asynch/mod.rs b/src/asynch/mod.rs index 2e6776b..de8313c 100644 --- a/src/asynch/mod.rs +++ b/src/asynch/mod.rs @@ -1,78 +1,48 @@ pub mod control; +mod resources; pub mod runner; #[cfg(feature = "ublox-sockets")] pub mod ublox_stack; pub(crate) mod state; -use crate::command::edm::{urc::EdmEvent, EdmAtCmdWrapper}; +#[cfg(feature = "internal-network-stack")] +mod internal_stack; +#[cfg(feature = "internal-network-stack")] +pub use internal_stack::{new_internal, InternalRunner, Resources}; + +#[cfg(feature = "ppp")] +mod ppp; +#[cfg(feature = "ppp")] +pub use ppp::{new_ppp, PPPRunner, Resources}; + use atat::asynch::AtatClient; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, mutex::Mutex}; -use embedded_hal::digital::OutputPin; -use runner::Runner; -use state::Device; -use self::control::Control; +#[cfg(feature = "edm")] +pub type UbloxUrc = crate::command::edm::urc::EdmEvent; -// NOTE: Must be pow(2) due to internal usage of `FnvIndexMap` -const MAX_CONNS: usize = 8; +#[cfg(not(feature = "edm"))] +pub type UbloxUrc = crate::command::Urc; pub struct AtHandle<'d, AT: AtatClient>(&'d Mutex); impl<'d, AT: AtatClient> AtHandle<'d, AT> { - async fn send_edm( - &mut self, - cmd: Cmd, - ) -> Result { - self.send(EdmAtCmdWrapper(cmd)).await + #[cfg(feature = "edm")] + async fn send(&mut self, cmd: Cmd) -> Result { + self.send_raw(crate::command::edm::EdmAtCmdWrapper(cmd)) + .await } + #[cfg(not(feature = "edm"))] async fn send(&mut self, cmd: Cmd) -> Result { - self.0.lock().await.send_retry::(&cmd).await + self.send_raw(cmd).await } -} - -pub struct State { - ch: state::State, - at_handle: Mutex, -} -impl State { - pub fn new(at_handle: AT) -> Self { - Self { - ch: state::State::new(), - at_handle: Mutex::new(at_handle), - } + async fn send_raw( + &mut self, + cmd: Cmd, + ) -> Result { + self.0.lock().await.send_retry::(&cmd).await } } - -pub async fn new<'a, AT: AtatClient, RST: OutputPin, const URC_CAPACITY: usize>( - state: &'a mut State, - subscriber: &'a atat::UrcChannel, - reset: RST, -) -> ( - Device<'a, AT, URC_CAPACITY>, - Control<'a, AT>, - Runner<'a, AT, RST, MAX_CONNS, URC_CAPACITY>, -) { - let (ch_runner, net_device) = state::new( - &mut state.ch, - AtHandle(&state.at_handle), - subscriber.subscribe().unwrap(), - ); - let state_ch = ch_runner.state_runner(); - - let mut runner = Runner::new( - ch_runner, - AtHandle(&state.at_handle), - reset, - subscriber.subscribe().unwrap(), - ); - - runner.init().await.unwrap(); - - let mut control = Control::new(state_ch, AtHandle(&state.at_handle)); - control.init().await.unwrap(); - - (net_device, control, runner) -} diff --git a/src/asynch/ppp.rs b/src/asynch/ppp.rs new file mode 100644 index 0000000..dfd3588 --- /dev/null +++ b/src/asynch/ppp.rs @@ -0,0 +1,338 @@ +use core::mem::MaybeUninit; + +use atat::{ + asynch::{AtatClient, Client, SimpleClient}, + AtatIngress, +}; +use embassy_futures::select::Either; +use embassy_net::{ + udp::{PacketMetadata, UdpSocket}, + IpEndpoint, Ipv4Address, +}; +use embassy_sync::{ + blocking_mutex::raw::NoopRawMutex, + mutex::Mutex, + pipe::{Reader, Writer}, +}; +use embassy_time::{Duration, Instant, Timer}; +use embedded_hal::digital::OutputPin; +use embedded_io_async::{BufRead, Read, Write}; + +use crate::command::{ + data_mode::{self, ChangeMode}, + system::{self, SetEcho}, +}; + +use super::{control::Control, resources::UbxResources, runner::Runner, state, AtHandle, UbloxUrc}; + +const PPP_AT_PORT: u16 = 23; +pub const SOCKET_BUF_SIZE: usize = 128; + +pub type Resources< + 'a, + const CMD_BUF_SIZE: usize, + const INGRESS_BUF_SIZE: usize, + const URC_CAPACITY: usize, +> = UbxResources< + Writer<'a, NoopRawMutex, SOCKET_BUF_SIZE>, + CMD_BUF_SIZE, + INGRESS_BUF_SIZE, + URC_CAPACITY, +>; + +pub fn new_ppp< + 'a, + RST: OutputPin, + const CMD_BUF_SIZE: usize, + const INGRESS_BUF_SIZE: usize, + const URC_CAPACITY: usize, +>( + resources: &'a mut Resources<'a, CMD_BUF_SIZE, INGRESS_BUF_SIZE, URC_CAPACITY>, + reset: RST, +) -> ( + embassy_net_ppp::Device<'a>, + Control<'a, Client<'a, Writer<'a, NoopRawMutex, SOCKET_BUF_SIZE>, INGRESS_BUF_SIZE>>, + PPPRunner<'a, RST, INGRESS_BUF_SIZE, URC_CAPACITY>, +) { + let ch_runner = state::new_ppp(&mut resources.ch); + let state_ch = ch_runner.state_runner(); + + let (control_rx_reader, control_rx_writer) = resources.control_rx.split(); + let (control_tx_reader, control_tx_writer) = resources.control_tx.split(); + + // safety: this is a self-referential struct, however: + // - it can't move while the `'a` borrow is active. + // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again. + let at_client_uninit: *mut MaybeUninit< + Mutex< + NoopRawMutex, + Client<'a, Writer<'a, NoopRawMutex, SOCKET_BUF_SIZE>, INGRESS_BUF_SIZE>, + >, + > = (&mut resources.at_client + as *mut MaybeUninit< + Mutex< + NoopRawMutex, + Client<'static, Writer<'a, NoopRawMutex, SOCKET_BUF_SIZE>, INGRESS_BUF_SIZE>, + >, + >) + .cast(); + + unsafe { &mut *at_client_uninit }.write(Mutex::new(Client::new( + control_tx_writer, + &resources.res_slot, + &mut resources.cmd_buf, + atat::Config::default(), + ))); + + let at_client = unsafe { (&*at_client_uninit).assume_init_ref() }; + + let wifi_runner = Runner::new( + ch_runner, + AtHandle(at_client), + reset, + resources.urc_channel.subscribe().unwrap(), + ); + + let ingress = atat::Ingress::new( + atat::AtDigester::::new(), + &mut resources.ingress_buf, + &resources.res_slot, + &resources.urc_channel, + ); + + let control = Control::new(state_ch, AtHandle(at_client)); + + let (net_device, ppp_runner) = embassy_net_ppp::new(&mut resources.ppp_state); + + let runner = PPPRunner { + ppp_runner, + wifi_runner, + ingress, + control_rx_reader, + control_rx_writer, + control_tx_reader, + }; + + (net_device, control, runner) +} + +pub struct PPPRunner<'a, RST: OutputPin, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> { + pub ppp_runner: embassy_net_ppp::Runner<'a>, + pub wifi_runner: Runner< + 'a, + Client<'a, Writer<'a, NoopRawMutex, SOCKET_BUF_SIZE>, INGRESS_BUF_SIZE>, + RST, + URC_CAPACITY, + >, + pub ingress: + atat::Ingress<'a, atat::AtDigester, UbloxUrc, INGRESS_BUF_SIZE, URC_CAPACITY, 2>, + pub control_rx_reader: Reader<'a, NoopRawMutex, SOCKET_BUF_SIZE>, + pub control_rx_writer: Writer<'a, NoopRawMutex, SOCKET_BUF_SIZE>, + pub control_tx_reader: Reader<'a, NoopRawMutex, SOCKET_BUF_SIZE>, +} + +impl<'a, RST: OutputPin, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> + PPPRunner<'a, RST, INGRESS_BUF_SIZE, URC_CAPACITY> +{ + async fn configure(at_client: &mut A) -> Result<(), atat::Error> { + let _ = at_client + .send(&ChangeMode { + mode: data_mode::types::Mode::CommandMode, + }) + .await; + + at_client + .send(&SetEcho { + on: system::types::EchoOn::Off, + }) + .await?; + + // Initialize `ublox` module to desired baudrate + at_client + .send(&system::SetRS232Settings { + baud_rate: system::types::BaudRate::B115200, + flow_control: system::types::FlowControl::On, + data_bits: 8, + stop_bits: system::types::StopBits::One, + parity: system::types::Parity::None, + change_after_confirm: system::types::ChangeAfterConfirm::ChangeAfterOK, + }) + .await?; + + Ok(()) + } + + pub async fn run( + &mut self, + mut iface: RW, + stack: &embassy_net::Stack>, + ) -> ! { + // self.wifi_runner.init().await.unwrap(); + // Timer::after(Duration::from_secs(4)).await; + + loop { + // Reset modem + self.wifi_runner.reset().await; + + Timer::after(Duration::from_secs(1)).await; + + let control_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; 32]; + let mut socket_tx_buf = [0u8; 32]; + let mut socket = UdpSocket::new( + stack, + &mut rx_meta, + &mut socket_rx_buf, + &mut tx_meta, + &mut socket_tx_buf, + ); + + let endpoint = stack.config_v4().unwrap(); + + info!("Socket bound!"); + socket + .bind((endpoint.address.address(), PPP_AT_PORT)) + .unwrap(); + + let mut tx_buf = [0u8; 32]; + let mut rx_buf = [0u8; 32]; + + loop { + match embassy_futures::select::select( + self.control_tx_reader.read(&mut tx_buf), + socket.recv_from(&mut rx_buf), + ) + .await + { + Either::First(n) => { + socket + .send_to( + &tx_buf[..n], + (Ipv4Address::new(172, 30, 0, 251), PPP_AT_PORT), + ) + .await + .unwrap(); + } + Either::Second(Ok((n, _))) => { + self.control_rx_writer + .write_all(&rx_buf[..n]) + .await + .unwrap(); + } + Either::Second(_) => {} + } + } + }; + + let ppp_fut = async { + 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; + } + } + } + last_start = Some(Instant::now()); + + let mut buf = [0u8; 64]; + let mut at_client = SimpleClient::new( + &mut iface, + atat::AtDigester::::new(), + &mut buf, + atat::Config::default(), + ); + + if let Err(e) = Self::configure(&mut at_client).await { + warn!("modem: configure failed {:?}", e); + continue; + } + + Timer::after(Duration::from_secs(2)).await; + + // 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; + } + + 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 config = embassy_net_ppp::Config { + username: b"", + password: b"", + }; + let res = self + .ppp_runner + .run(&mut iface, 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; + + info!("ppp failed"); + } + }; + + let ingress_fut = async { + self.ingress.read_from(&mut self.control_rx_reader).await; + }; + + embassy_futures::select::select4( + ppp_fut, + ingress_fut, + control_fut, + self.wifi_runner.run(), + ) + .await; + } + } +} diff --git a/src/asynch/resources.rs b/src/asynch/resources.rs new file mode 100644 index 0000000..d55fb2f --- /dev/null +++ b/src/asynch/resources.rs @@ -0,0 +1,60 @@ +use core::mem::MaybeUninit; + +use atat::{asynch::Client, ResponseSlot, UrcChannel}; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, mutex::Mutex}; +use embedded_io_async::Write; + +use super::{state, UbloxUrc}; + +pub struct UbxResources< + W: Write, + const CMD_BUF_SIZE: usize, + const INGRESS_BUF_SIZE: usize, + const URC_CAPACITY: usize, +> { + pub(crate) ch: state::State, + + pub(crate) res_slot: ResponseSlot, + pub(crate) urc_channel: UrcChannel, + pub(crate) cmd_buf: [u8; CMD_BUF_SIZE], + pub(crate) ingress_buf: [u8; INGRESS_BUF_SIZE], + + pub(crate) at_client: MaybeUninit>>, + + #[cfg(feature = "ppp")] + pub(crate) ppp_state: embassy_net_ppp::State<2, 2>, + + #[cfg(feature = "ppp")] + pub(crate) control_rx: embassy_sync::pipe::Pipe, + #[cfg(feature = "ppp")] + pub(crate) control_tx: embassy_sync::pipe::Pipe, +} + +impl< + W: Write, + const CMD_BUF_SIZE: usize, + const INGRESS_BUF_SIZE: usize, + const URC_CAPACITY: usize, + > UbxResources +{ + pub fn new() -> Self { + Self { + ch: state::State::new(), + + res_slot: ResponseSlot::new(), + urc_channel: UrcChannel::new(), + cmd_buf: [0; CMD_BUF_SIZE], + ingress_buf: [0; INGRESS_BUF_SIZE], + + at_client: MaybeUninit::uninit(), + + #[cfg(feature = "ppp")] + ppp_state: embassy_net_ppp::State::new(), + + #[cfg(feature = "ppp")] + control_rx: embassy_sync::pipe::Pipe::new(), + #[cfg(feature = "ppp")] + control_tx: embassy_sync::pipe::Pipe::new(), + } + } +} diff --git a/src/asynch/runner.rs b/src/asynch/runner.rs index bab1368..fb0be9d 100644 --- a/src/asynch/runner.rs +++ b/src/asynch/runner.rs @@ -1,9 +1,13 @@ use core::str::FromStr; -use super::state::{self, LinkState}; +use super::{ + state::{self, LinkState}, + UbloxUrc, +}; +#[cfg(feature = "edm")] +use crate::command::edm::SwitchToEdmCommand; use crate::{ command::{ - edm::{urc::EdmEvent, SwitchToEdmCommand}, general::SoftwareVersion, network::{ responses::NetworkStatusResponse, @@ -12,8 +16,8 @@ use crate::{ GetNetworkStatus, }, system::{ - types::{BaudRate, ChangeAfterConfirm, EchoOn, FlowControl, Parity, StopBits}, - RebootDCE, SetEcho, SetRS232Settings, StoreCurrentConfig, + types::{BaudRate, ChangeAfterConfirm, FlowControl, Parity, StopBits}, + RebootDCE, SetRS232Settings, StoreCurrentConfig, }, wifi::{ types::DisconnectReason, @@ -35,19 +39,12 @@ use super::AtHandle; /// Background runner for the Ublox Module. /// /// You must call `.run()` in a background task for the Ublox Module to operate. -pub struct Runner< - 'd, - AT: AtatClient, - RST: OutputPin, - const MAX_CONNS: usize, - const URC_CAPACITY: usize, -> { +pub struct Runner<'d, AT: AtatClient, RST: OutputPin, const URC_CAPACITY: usize> { ch: state::Runner<'d>, at: AtHandle<'d, AT>, reset: RST, wifi_connection: Option, - // connections: FnvIndexMap, - urc_subscription: UrcSubscription<'d, EdmEvent, URC_CAPACITY, 2>, + urc_subscription: UrcSubscription<'d, UbloxUrc, URC_CAPACITY, 2>, } impl< @@ -55,15 +52,14 @@ impl< AT: AtatClient, // AT: AtatClient + atat::UartExt, RST: OutputPin, - const MAX_CONNS: usize, const URC_CAPACITY: usize, - > Runner<'d, AT, RST, MAX_CONNS, URC_CAPACITY> + > Runner<'d, AT, RST, URC_CAPACITY> { pub(crate) fn new( ch: state::Runner<'d>, at: AtHandle<'d, AT>, reset: RST, - urc_subscription: UrcSubscription<'d, EdmEvent, URC_CAPACITY, 2>, + urc_subscription: UrcSubscription<'d, UbloxUrc, URC_CAPACITY, 2>, ) -> Self { Self { ch, @@ -71,7 +67,6 @@ impl< reset, wifi_connection: None, urc_subscription, - // connections: IndexMap::new(), } } @@ -89,10 +84,9 @@ impl< // parameter. Instead, the // parameter must be set to 0 and the serial settings will take effect // when the module is reset. - let baud_rate = BaudRate::B115200; self.at - .send_edm(SetRS232Settings { - baud_rate, + .send(SetRS232Settings { + baud_rate: BaudRate::B115200, flow_control: FlowControl::On, data_bits: 8, stop_bits: StopBits::One, @@ -103,12 +97,12 @@ impl< self.restart(true).await?; - self.at.send_edm(SoftwareVersion).await?; + self.at.send(SoftwareVersion).await?; // Move to control // if let Some(size) = self.config.tls_in_buffer_size { // self.at - // .send_edm(SetPeerConfiguration { + // .send(SetPeerConfiguration { // parameter: PeerConfigParameter::TlsInBuffer(size), // }) // .await?; @@ -116,7 +110,7 @@ impl< // if let Some(size) = self.config.tls_out_buffer_size { // self.at - // .send_edm(SetPeerConfiguration { + // .send(SetPeerConfiguration { // parameter: PeerConfigParameter::TlsOutBuffer(size), // }) // .await?; @@ -128,8 +122,15 @@ impl< async fn wait_startup(&mut self, timeout: Duration) -> Result<(), Error> { let fut = async { loop { - match self.urc_subscription.next_message_pure().await { - EdmEvent::ATEvent(Urc::StartUp) => return, + let event = self.urc_subscription.next_message_pure().await; + + #[cfg(feature = "edm")] + let Some(event) = event.extract_urc() else { + continue; + }; + + match event { + Urc::StartUp => return, _ => {} } } @@ -146,6 +147,7 @@ impl< self.wait_startup(Duration::from_secs(4)).await?; + #[cfg(feature = "edm")] self.enter_edm(Duration::from_secs(4)).await?; Ok(()) @@ -154,19 +156,21 @@ impl< pub async fn restart(&mut self, store: bool) -> Result<(), Error> { warn!("Soft resetting Ublox Short Range"); if store { - self.at.send_edm(StoreCurrentConfig).await?; + self.at.send(StoreCurrentConfig).await?; } - self.at.send_edm(RebootDCE).await?; + self.at.send(RebootDCE).await?; self.wait_startup(Duration::from_secs(10)).await?; info!("Module started again"); + #[cfg(feature = "edm")] self.enter_edm(Duration::from_secs(4)).await?; Ok(()) } + #[cfg(feature = "edm")] pub async fn enter_edm(&mut self, timeout: Duration) -> Result<(), Error> { info!("Entering EDM mode"); @@ -189,7 +193,11 @@ impl< .await .map_err(|_| Error::Timeout)?; - self.at.send_edm(SetEcho { on: EchoOn::On }).await?; + self.at + .send(crate::command::system::SetEcho { + on: crate::command::system::types::EchoOn::Off, + }) + .await?; Ok(()) } @@ -210,20 +218,26 @@ impl< Ok(link_state == LinkState::Up) } - pub async fn run(mut self) -> ! { + pub async fn run(&mut self) -> ! { loop { let wait_link_up = { let event = self.urc_subscription.next_message_pure().await; + + #[cfg(feature = "edm")] + let Some(event) = event.extract_urc() else { + continue; + }; + match event { - EdmEvent::ATEvent(Urc::StartUp) => { + Urc::StartUp => { error!("AT startup event?! Device restarted unintentionally!"); false } - EdmEvent::ATEvent(Urc::WifiLinkConnected(WifiLinkConnected { + Urc::WifiLinkConnected(WifiLinkConnected { connection_id: _, bssid, channel, - })) => { + }) => { if let Some(ref mut con) = self.wifi_connection { con.wifi_state = WiFiState::Connected; con.network.bssid = bssid; @@ -241,10 +255,7 @@ impl< } true } - EdmEvent::ATEvent(Urc::WifiLinkDisconnected(WifiLinkDisconnected { - reason, - .. - })) => { + Urc::WifiLinkDisconnected(WifiLinkDisconnected { reason, .. }) => { if let Some(ref mut con) = self.wifi_connection { match reason { DisconnectReason::NetworkDisabled => { @@ -262,27 +273,23 @@ impl< true } - EdmEvent::ATEvent(Urc::WifiAPUp(_)) => todo!(), - EdmEvent::ATEvent(Urc::WifiAPDown(_)) => todo!(), - EdmEvent::ATEvent(Urc::WifiAPStationConnected(_)) => todo!(), - EdmEvent::ATEvent(Urc::WifiAPStationDisconnected(_)) => todo!(), - EdmEvent::ATEvent(Urc::EthernetLinkUp(_)) => todo!(), - EdmEvent::ATEvent(Urc::EthernetLinkDown(_)) => todo!(), - EdmEvent::ATEvent(Urc::NetworkUp(NetworkUp { interface_id })) => { + Urc::WifiAPUp(_) => todo!(), + Urc::WifiAPDown(_) => todo!(), + Urc::WifiAPStationConnected(_) => todo!(), + Urc::WifiAPStationDisconnected(_) => todo!(), + Urc::EthernetLinkUp(_) => todo!(), + Urc::EthernetLinkDown(_) => todo!(), + Urc::NetworkUp(NetworkUp { interface_id }) => { drop(event); - self.network_status_callback(interface_id).await.unwrap(); + self.network_status_callback(interface_id).await.ok(); true } - EdmEvent::ATEvent(Urc::NetworkDown(NetworkDown { interface_id })) => { + Urc::NetworkDown(NetworkDown { interface_id }) => { drop(event); - self.network_status_callback(interface_id).await.unwrap(); + self.network_status_callback(interface_id).await.ok(); true } - EdmEvent::ATEvent(Urc::NetworkError(_)) => todo!(), - EdmEvent::StartUp => { - error!("EDM startup event?! Device restarted unintentionally!"); - false - } + Urc::NetworkError(_) => todo!(), _ => false, } }; @@ -299,7 +306,7 @@ impl< .. } = self .at - .send_edm(GetNetworkStatus { + .send(GetNetworkStatus { interface_id, status: NetworkStatusParameter::InterfaceType, }) @@ -313,7 +320,7 @@ impl< .. } = self .at - .send_edm(GetNetworkStatus { + .send(GetNetworkStatus { interface_id, status: NetworkStatusParameter::Gateway, }) @@ -333,7 +340,7 @@ impl< .. } = self .at - .send_edm(GetNetworkStatus { + .send(GetNetworkStatus { interface_id, status: NetworkStatusParameter::IPv6LinkLocalAddress, }) diff --git a/src/asynch/state.rs b/src/asynch/state.rs index 7b45214..816e1ac 100644 --- a/src/asynch/state.rs +++ b/src/asynch/state.rs @@ -10,8 +10,6 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_sync::waitqueue::WakerRegistration; -use crate::command::edm::urc::EdmEvent; - /// The link state of a network device. #[derive(PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -22,7 +20,7 @@ pub enum LinkState { Up, } -use super::AtHandle; +use super::{AtHandle, UbloxUrc}; pub struct State { inner: MaybeUninit, @@ -92,7 +90,7 @@ impl<'d> StateRunner<'d> { pub fn new<'d, AT: AtatClient, const URC_CAPACITY: usize>( state: &'d mut State, at: AtHandle<'d, AT>, - urc_subscription: UrcSubscription<'d, EdmEvent, URC_CAPACITY, 2>, + urc_subscription: UrcSubscription<'d, UbloxUrc, URC_CAPACITY, 2>, ) -> (Runner<'d>, Device<'d, AT, URC_CAPACITY>) { // safety: this is a self-referential struct, however: // - it can't move while the `'d` borrow is active. @@ -121,6 +119,25 @@ pub fn new<'d, AT: AtatClient, const URC_CAPACITY: usize>( ) } +pub fn new_ppp<'d>(state: &'d mut State) -> Runner<'d> { + // safety: this is a self-referential struct, however: + // - it can't move while the `'d` borrow is active. + // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again. + let state_uninit: *mut MaybeUninit = + (&mut state.inner as *mut MaybeUninit).cast(); + + let state = unsafe { &mut *state_uninit }.write(StateInner { + shared: Mutex::new(RefCell::new(Shared { + link_state: LinkState::Down, + waker: WakerRegistration::new(), + })), + }); + + Runner { + shared: &state.shared, + } +} + pub struct TestShared<'d> { inner: &'d Mutex>, } @@ -128,7 +145,7 @@ pub struct TestShared<'d> { pub struct Device<'d, AT: AtatClient, const URC_CAPACITY: usize> { pub(crate) shared: TestShared<'d>, pub(crate) at: AtHandle<'d, AT>, - pub(crate) urc_subscription: UrcSubscription<'d, EdmEvent, URC_CAPACITY, 2>, + pub(crate) urc_subscription: UrcSubscription<'d, UbloxUrc, URC_CAPACITY, 2>, } impl<'d> TestShared<'d> { diff --git a/src/blocking/client.rs b/src/blocking/client.rs deleted file mode 100644 index 762d279..0000000 --- a/src/blocking/client.rs +++ /dev/null @@ -1,859 +0,0 @@ -use core::str::FromStr; - -use crate::{ - blocking::timer::Timer, - command::{ - data_mode::{ - types::{IPProtocol, PeerConfigParameter}, - SetPeerConfiguration, - }, - edm::{types::Protocol, urc::EdmEvent, EdmAtCmdWrapper, SwitchToEdmCommand}, - general::{types::FirmwareVersion, SoftwareVersion}, - network::SetNetworkHostName, - ping::types::PingError, - system::{ - types::{BaudRate, ChangeAfterConfirm, FlowControl, Parity, StopBits}, - RebootDCE, SetRS232Settings, StoreCurrentConfig, - }, - wifi::{ - types::{DisconnectReason, WifiConfig}, - SetWifiConfig, - }, - Urc, - }, - config::Config, - error::Error, - wifi::{ - connection::{NetworkState, WiFiState, WifiConnection}, - network::{WifiMode, WifiNetwork}, - supplicant::Supplicant, - SocketMap, - }, -}; -use defmt::{debug, error, trace}; -use embassy_time::Duration; -use embedded_hal::digital::OutputPin; -use embedded_nal::{IpAddr, Ipv4Addr, SocketAddr}; -use ublox_sockets::{ - udp_listener::UdpListener, AnySocket, SocketHandle, SocketSet, SocketType, TcpSocket, TcpState, - UdpSocket, UdpState, -}; - -#[derive(PartialEq, Copy, Clone)] -pub enum SerialMode { - Cmd, - ExtendedData, -} - -#[derive(PartialEq, Copy, Clone)] -pub enum DNSState { - NotResolving, - Resolving, - Resolved(IpAddr), - Error(PingError), -} - -#[derive(PartialEq, Clone, Default)] -pub struct SecurityCredentials { - pub ca_cert_name: Option>, - pub c_cert_name: Option>, // TODO: Make &str with lifetime - pub c_key_name: Option>, -} - -/// Creates new socket numbers -/// Properly not Async safe -pub fn new_socket_num<'a, const N: usize, const L: usize>( - sockets: &'a SocketSet, -) -> Result { - let mut num = 0; - while sockets.socket_type(SocketHandle(num)).is_some() { - num += 1; - if num == u8::MAX { - return Err(()); - } - } - Ok(num) -} - -pub struct UbloxClient -where - C: atat::blocking::AtatClient, - RST: OutputPin, -{ - pub(crate) module_started: bool, - pub(crate) initialized: bool, - serial_mode: SerialMode, - pub(crate) wifi_connection: Option, - pub(crate) wifi_config_active_on_startup: Option, - pub(crate) client: C, - pub(crate) config: Config, - pub(crate) sockets: Option<&'static mut SocketSet>, - pub(crate) dns_state: DNSState, - pub(crate) urc_attempts: u8, - pub(crate) security_credentials: SecurityCredentials, - pub(crate) socket_map: SocketMap, - pub(crate) udp_listener: UdpListener<4, N>, -} - -impl UbloxClient -where - C: atat::blocking::AtatClient, - RST: OutputPin, -{ - pub fn new(client: C, config: Config) -> Self { - UbloxClient { - module_started: false, - initialized: false, - serial_mode: SerialMode::Cmd, - wifi_connection: None, - wifi_config_active_on_startup: None, - client, - config, - sockets: None, - dns_state: DNSState::NotResolving, - urc_attempts: 0, - security_credentials: SecurityCredentials::default(), - socket_map: SocketMap::default(), - udp_listener: UdpListener::new(), - } - } - - pub fn set_socket_storage(&mut self, socket_set: &'static mut SocketSet) { - socket_set.prune(); - self.sockets.replace(socket_set); - } - - pub fn take_socket_storage(&mut self) -> Option<&'static mut SocketSet> { - self.sockets.take() - } - - pub fn has_socket_storage(&self) -> bool { - self.sockets.is_some() - } - - pub fn init(&mut self) -> Result<(), Error> { - // Initilize a new ublox device to a known state (set RS232 settings) - - debug!("Initializing wifi"); - // Hard reset module - self.reset()?; - - // Switch to EDM on Init. If in EDM, fail and check with autosense - // if self.serial_mode != SerialMode::ExtendedData { - // self.retry_send(&SwitchToEdmCommand, 5)?; - // self.serial_mode = SerialMode::ExtendedData; - // } - - while self.serial_mode != SerialMode::ExtendedData { - self.send_internal(&SwitchToEdmCommand, true).ok(); - Timer::after(Duration::from_millis(100)).wait(); - while self.handle_urc()? {} - } - - // TODO: handle EDM settings quirk see EDM datasheet: 2.2.5.1 AT Request Serial settings - self.send_internal( - &EdmAtCmdWrapper(SetRS232Settings { - baud_rate: BaudRate::B115200, - flow_control: FlowControl::On, - data_bits: 8, - stop_bits: StopBits::One, - parity: Parity::None, - change_after_confirm: ChangeAfterConfirm::ChangeAfterOK, - }), - false, - )?; - - if let Some(hostname) = self.config.hostname.clone() { - self.send_internal( - &EdmAtCmdWrapper(SetNetworkHostName { - host_name: hostname.as_str(), - }), - false, - )?; - } - - self.send_internal( - &EdmAtCmdWrapper(SetWifiConfig { - config_param: WifiConfig::RemainOnChannel(0), - }), - false, - )?; - - self.send_internal(&EdmAtCmdWrapper(StoreCurrentConfig), false)?; - - self.software_reset()?; - - while self.serial_mode != SerialMode::ExtendedData { - self.send_internal(&SwitchToEdmCommand, true).ok(); - Timer::after(Duration::from_millis(100)).wait(); - while self.handle_urc()? {} - } - - if self.firmware_version()? < FirmwareVersion::new(8, 0, 0) { - self.config.network_up_bug = true; - } else { - if let Some(size) = self.config.tls_in_buffer_size { - self.send_internal( - &EdmAtCmdWrapper(SetPeerConfiguration { - parameter: PeerConfigParameter::TlsInBuffer(size), - }), - false, - )?; - } - - if let Some(size) = self.config.tls_out_buffer_size { - self.send_internal( - &EdmAtCmdWrapper(SetPeerConfiguration { - parameter: PeerConfigParameter::TlsOutBuffer(size), - }), - false, - )?; - } - } - - self.initialized = true; - self.supplicant::<10>()?.init()?; - - Ok(()) - } - - pub fn firmware_version(&mut self) -> Result { - let response = self.send_internal(&EdmAtCmdWrapper(SoftwareVersion), false)?; - Ok(response.version) - } - - pub fn retry_send(&mut self, cmd: &A, attempts: usize) -> Result - where - A: atat::AtatCmd, - { - for _ in 0..attempts { - match self.send_internal(cmd, true) { - Ok(resp) => { - return Ok(resp); - } - Err(_e) => {} - }; - } - Err(Error::BaudDetection) - } - - pub fn reset(&mut self) -> Result<(), Error> { - self.serial_mode = SerialMode::Cmd; - self.initialized = false; - self.module_started = false; - self.wifi_connection = None; - self.wifi_config_active_on_startup = None; - self.dns_state = DNSState::NotResolving; - self.urc_attempts = 0; - self.security_credentials = SecurityCredentials::default(); - self.socket_map = SocketMap::default(); - self.udp_listener = UdpListener::new(); - - self.clear_buffers()?; - - if let Some(ref mut pin) = self.config.rst_pin { - warn!("Hard resetting Ublox Short Range"); - pin.set_low().ok(); - Timer::after(Duration::from_millis(50)).wait(); - pin.set_high().ok(); - - Timer::with_timeout(Duration::from_secs(4), || { - self.handle_urc().ok(); - if self.module_started { - Some(Ok::<(), ()>(())) - } else { - None - } - }) - .map_err(|_| Error::Timeout)?; - } - Ok(()) - } - - pub fn software_reset(&mut self) -> Result<(), Error> { - self.serial_mode = SerialMode::Cmd; - self.initialized = false; - self.module_started = false; - self.wifi_connection = None; - self.wifi_config_active_on_startup = None; - self.dns_state = DNSState::NotResolving; - self.urc_attempts = 0; - self.security_credentials = SecurityCredentials::default(); - self.socket_map = SocketMap::default(); - self.udp_listener = UdpListener::new(); - - warn!("Soft resetting Ublox Short Range"); - self.send_internal(&EdmAtCmdWrapper(RebootDCE), false)?; - self.clear_buffers()?; - - Timer::with_timeout(Duration::from_secs(4), || { - self.handle_urc().ok(); - if self.module_started { - Some(Ok::<(), ()>(())) - } else { - None - } - }) - .map_err(|_| Error::Timeout)?; - - Ok(()) - } - - pub(crate) fn clear_buffers(&mut self) -> Result<(), Error> { - // self.client.reset(); deprecated - - if let Some(ref mut sockets) = self.sockets.as_deref_mut() { - sockets.prune(); - } - - // Allow ATAT some time to clear the buffers - Timer::after(Duration::from_millis(300)).wait(); - - Ok(()) - } - - pub fn spin(&mut self) -> Result<(), Error> { - if !self.initialized { - return Err(Error::Uninitialized); - } - - while self.handle_urc()? {} - - self.connected_to_network()?; - - // TODO: Is this smart? - // if let Some(ref mut sockets) = self.sockets.as_deref_mut() { - // sockets.recycle(self.timer.now()); - // } - - Ok(()) - } - - pub(crate) fn send_internal( - &mut self, - req: &A, - check_urc: bool, - ) -> Result - where - A: atat::AtatCmd, - { - if check_urc { - if let Err(e) = self.handle_urc() { - error!("Failed handle URC: {:?}", e); - } - } - - self.client.send(req).map_err(|e| { - error!("{:?}: {=[u8]:a}", e, req.as_bytes()); - e.into() - }) - } - - fn handle_urc(&mut self) -> Result { - let mut ran = false; - let socket_set = self.sockets.as_deref_mut(); - let dns_state = &mut self.dns_state; - let socket_map = &mut self.socket_map; - let udp_listener = &mut self.udp_listener; - let wifi_connection = &mut self.wifi_connection; - - let mut a = self.urc_attempts; - let max = self.config.max_urc_attempts; - - self.client.try_read_urc_with::(|edm_urc, _| { - ran = true; - let res = match edm_urc { - EdmEvent::ATEvent(urc) => { - match urc { - Urc::StartUp => { - debug!("[URC] Startup"); - self.module_started = true; - self.initialized = false; - self.serial_mode = SerialMode::Cmd; - true - } - Urc::PeerConnected(event) => { - debug!("[URC] PeerConnected"); - - // TODO: - // - // We should probably move - // `tcp.set_state(TcpState::Connected(endpoint));` - // + `udp.set_state(UdpState::Established);` as - // well as `tcp.update_handle(*socket);` + - // `udp.update_handle(*socket);` here, to make - // sure that part also works without EDM mode - - - if let Some(sockets) = socket_set { - let remote_ip = Ipv4Addr::from_str( - core::str::from_utf8(event.remote_address.as_slice()).unwrap(), - ) - .unwrap(); - - let remote = SocketAddr::new(remote_ip.into(), event.remote_port); - - if let Some(queue) = udp_listener.incoming(event.local_port) { - trace!("[UDP Server] Server socket incomming"); - let mut handled = true; - if sockets.len() >= sockets.capacity() { - // Check if there are any sockets closed by remote, and close it - // if it has exceeded its timeout, in order to recycle it. - // TODO Is this correct? - if !sockets.recycle() { - handled = false; - } - } - let peer_handle = event.handle; - let socket_handle = SocketHandle(new_socket_num(sockets).unwrap()); - let mut new_socket = UdpSocket::new(socket_handle.0); - new_socket.set_state(UdpState::Established); - if new_socket.bind(remote).is_err(){ - error!("[UDP_URC] Binding connecting socket Error"); - handled = false - } - if sockets.add(new_socket).map_err(|_| { - error!("[UDP_URC] Opening socket Error: Socket set full"); - Error::SocketMemory - }).is_err(){ - handled = false; - } - - if socket_map.insert_peer(peer_handle, socket_handle).map_err(|_| { - error!("[UDP_URC] Opening socket Error: Socket Map full"); - Error::SocketMapMemory - }).is_err(){ - handled = false; - } - debug!( - "[URC] Binding remote {=[u8]:a} to UDP server on port: {:?} with handle: {:?}", - event.remote_address.as_slice(), - event.local_port, - socket_handle - ); - if queue.enqueue((socket_handle, remote)).is_err(){ - handled = false - } - handled - } else { - match event.protocol { - IPProtocol::TCP => { - // if let Ok(mut tcp) = - // sockets.get::>(event.handle) - // { - // debug!( - // "Binding remote {=[u8]:a} to TCP socket {:?}", - // event.remote_address.as_slice(), - // event.handle - // ); - // tcp.set_state(TcpState::Connected(remote)); - // return true; - // } - } - IPProtocol::UDP => { - // if let Ok(mut udp) = - // sockets.get::>(event.handle) - // { - // debug!( - // "Binding remote {=[u8]:a} to UDP socket {:?}", - // event.remote_address.as_slice(), - // event.handle - // ); - // udp.bind(remote).unwrap(); - // udp.set_state(UdpState::Established); - // return true; - // } - } - } - true - } - } else { - true - } - } - Urc::PeerDisconnected(msg) => { - debug!("[URC] PeerDisconnected"); - if let Some(sockets) = socket_set { - if let Some(handle) = socket_map.peer_to_socket(&msg.handle) { - match sockets.socket_type(*handle) { - Some(SocketType::Tcp) => { - if let Ok(mut tcp) = - sockets.get::>(*handle) - { - tcp.closed_by_remote(); - } - } - Some(SocketType::Udp) => { - if let Ok(mut udp) = - sockets.get::>(*handle) - { - udp.close(); - } - sockets.remove(*handle).ok(); - } - _ => {} - } - socket_map.remove_peer(&msg.handle).unwrap(); - } - } - true - } - Urc::WifiLinkConnected(msg) => { - debug!("[URC] WifiLinkConnected"); - if let Some(ref mut con) = wifi_connection { - con.wifi_state = WiFiState::Connected; - con.network.bssid = msg.bssid; - con.network.channel = msg.channel; - } else { - debug!("[URC] Active network config discovered"); - wifi_connection.replace( - WifiConnection::new( - WifiNetwork { - bssid: msg.bssid, - op_mode: crate::command::wifi::types::OperationMode::Infrastructure, - ssid: heapless::String::new(), - channel: msg.channel, - rssi: 1, - authentication_suites: 0, - unicast_ciphers: 0, - group_ciphers: 0, - mode: WifiMode::Station, - }, - WiFiState::Connected, - 255, - ).activate() - ); - } - true - } - Urc::WifiLinkDisconnected(msg) => { - debug!("[URC] WifiLinkDisconnected"); - if let Some(con) = wifi_connection { - match msg.reason { - DisconnectReason::NetworkDisabled => { - con.wifi_state = WiFiState::Inactive; - } - DisconnectReason::SecurityProblems => { - error!("Wifi Security Problems"); - } - _ => { - con.wifi_state = WiFiState::NotConnected; - } - } - } - true - } - Urc::WifiAPUp(_) => { - debug!("[URC] WifiAPUp"); - true - } - Urc::WifiAPDown(_) => { - debug!("[URC] WifiAPDown"); - true - } - Urc::WifiAPStationConnected(client) => { - debug!( - "[URC] WifiAPStationConnected {=[u8]:a}", - client.mac_addr.into_inner() - ); - true - } - Urc::WifiAPStationDisconnected(_) => { - debug!("[URC] WifiAPStationDisconnected"); - true - } - Urc::EthernetLinkUp(_) => { - debug!("[URC] EthernetLinkUp"); - true - } - Urc::EthernetLinkDown(_) => { - debug!("[URC] EthernetLinkDown"); - true - } - Urc::NetworkUp(_) => { - debug!("[URC] NetworkUp"); - if let Some(con) = wifi_connection { - if self.config.network_up_bug { - match con.network_state { - NetworkState::Attached => (), - NetworkState::AlmostAttached => { - con.network_state = NetworkState::Attached - } - NetworkState::Unattached => { - con.network_state = NetworkState::AlmostAttached - } - } - } else { - con.network_state = NetworkState::Attached; - } - } - true - } - Urc::NetworkDown(_) => { - debug!("[URC] NetworkDown"); - if let Some(con) = wifi_connection { - con.network_state = NetworkState::Unattached; - } - true - } - Urc::NetworkError(_) => { - debug!("[URC] NetworkError"); - true - } - Urc::PingResponse(resp) => { - debug!("[URC] PingResponse"); - if *dns_state == DNSState::Resolving { - *dns_state = DNSState::Resolved(resp.ip) - } - true - } - Urc::PingErrorResponse(resp) => { - debug!("[URC] PingErrorResponse: {:?}", resp.error); - if *dns_state == DNSState::Resolving { - *dns_state = DNSState::Error(resp.error) - } - true - } - } - } // end match urc - EdmEvent::StartUp => { - debug!("[EDM_URC] STARTUP"); - self.module_started = true; - self.serial_mode = SerialMode::ExtendedData; - true - } - EdmEvent::IPv4ConnectEvent(event) => { - debug!( - "[EDM_URC] IPv4ConnectEvent! Channel_id: {:?}", - event.channel_id - ); - - if let Some(sockets) = socket_set { - let endpoint = SocketAddr::new(event.remote_ip.into(), event.remote_port); - - // This depends upon Connected AT-URC to arrive first. - if let Some(queue) = udp_listener.incoming(event.local_port) { - if let Some((socket_handle, _ )) = queue.into_iter().find(|(_, remote)| remote == &endpoint) { - socket_map.insert_channel(event.channel_id, *socket_handle).is_ok() - } else { - false - } - } else { - sockets - .iter_mut() - .find_map(|(h, s)| { - match event.protocol { - Protocol::TCP => { - let mut tcp = TcpSocket::downcast(s).ok()?; - if tcp.endpoint() == Some(endpoint) { - socket_map.insert_channel(event.channel_id, h).unwrap(); - tcp.set_state(TcpState::Connected(endpoint)); - return Some(true); - } - } - Protocol::UDP => { - let mut udp = UdpSocket::downcast(s).ok()?; - if udp.endpoint() == Some(endpoint) { - socket_map.insert_channel(event.channel_id, h).unwrap(); - udp.set_state(UdpState::Established); - return Some(true); - } - } - _ => {} - } - None - }) - .is_some() - } - } else { - true - } - } - EdmEvent::IPv6ConnectEvent(event) => { - debug!( - "[EDM_URC] IPv6ConnectEvent! Channel_id: {:?}", - event.channel_id - ); - - if let Some(sockets) = socket_set { - let endpoint = SocketAddr::new(event.remote_ip.into(), event.remote_port); - - // This depends upon Connected AT-URC to arrive first. - if let Some(queue) = udp_listener.incoming(event.local_port) { - if let Some((socket_handle, _ )) = queue.into_iter().find(|(_, remote)| remote == &endpoint) { - socket_map.insert_channel(event.channel_id, *socket_handle).is_ok() - } else { - false - } - } else { - sockets - .iter_mut() - .find_map(|(h, s)| { - match event.protocol { - Protocol::TCP => { - let mut tcp = TcpSocket::downcast(s).ok()?; - if tcp.endpoint() == Some(endpoint) { - socket_map.insert_channel(event.channel_id, h).unwrap(); - tcp.set_state(TcpState::Connected(endpoint)); - return Some(true); - } - } - Protocol::UDP => { - let mut udp = UdpSocket::downcast(s).ok()?; - if udp.endpoint() == Some(endpoint) { - socket_map.insert_channel(event.channel_id, h).unwrap(); - udp.set_state(UdpState::Established); - return Some(true); - } - } - _ => {} - } - None - }) - .is_some() - } - } else { - true - } - } - EdmEvent::BluetoothConnectEvent(_) => { - debug!("[EDM_URC] BluetoothConnectEvent"); - true - } - EdmEvent::DisconnectEvent(channel_id) => { - debug!("[EDM_URC] DisconnectEvent! Channel_id: {:?}", channel_id); - socket_map.remove_channel(&channel_id).ok(); - true - } - EdmEvent::DataEvent(event) => { - debug!("[EDM_URC] DataEvent! Channel_id: {:?}", event.channel_id); - if let Some(sockets) = socket_set { - if !event.data.is_empty() { - if let Some(socket_handle) = - socket_map.channel_to_socket(&event.channel_id) - { - match sockets.socket_type(*socket_handle) { - Some(SocketType::Tcp) => { - // Handle tcp socket - let mut tcp = sockets - .get::>(*socket_handle) - .unwrap(); - if tcp.can_recv() { - tcp.rx_enqueue_slice(&event.data); - true - } else { - false - } - } - Some(SocketType::Udp) => { - // Handle udp socket - let mut udp = sockets - .get::>(*socket_handle) - .unwrap(); - - if udp.can_recv() { - udp.rx_enqueue_slice(&event.data); - true - } else { - false - } - } - _ => { - error!("SocketNotFound {:?}", socket_handle); - false - } - } - } else { - false - } - } else { - false - } - } else { - true - } - } - }; // end match edm-urc - if !res { - if a < max { - error!("[EDM_URC] URC handeling failed"); - a += 1; - return false; - } - error!("[EDM_URC] URC thrown away"); - } - a = 0; - true - }); - self.urc_attempts = a; - Ok(ran) - } - - /// Send AT command - /// Automaticaly waraps commands in EDM context - pub fn send_at(&mut self, cmd: A) -> Result - where - A: atat::AtatCmd, - { - if !self.initialized { - self.init()?; - } - match self.serial_mode { - SerialMode::ExtendedData => self.send_internal(&EdmAtCmdWrapper(cmd), true), - SerialMode::Cmd => self.send_internal(&cmd, true), - } - } - - pub fn supplicant(&mut self) -> Result, Error> { - // TODO: better solution - if !self.initialized { - return Err(Error::Uninitialized); - } - - Ok(Supplicant { - client: &mut self.client, - wifi_connection: &mut self.wifi_connection, - active_on_startup: &mut self.wifi_config_active_on_startup, - }) - } - /// Is the module attached to a WiFi and ready to open sockets - pub fn connected_to_network(&self) -> Result<(), Error> { - if let Some(ref con) = self.wifi_connection { - if !self.initialized { - Err(Error::Uninitialized) - } else if !con.is_connected() { - Err(Error::WifiState(con.wifi_state)) - } else if self.sockets.is_none() { - Err(Error::MissingSocketSet) - } else { - Ok(()) - } - } else { - Err(Error::NoWifiSetup) - } - } - - /// Is the module attached to a WiFi - /// - // TODO: handle this case for better stability - // WiFi connection can disconnect momentarily, but if the network state does not change - // the current context is safe. - pub fn attached_to_wifi(&self) -> Result<(), Error> { - if let Some(ref con) = self.wifi_connection { - if !self.initialized { - Err(Error::Uninitialized) - // } else if !(con.network_state == NetworkState::Attached) { - } else if !con.is_connected() { - if con.wifi_state == WiFiState::Connected { - Err(Error::NetworkState(con.network_state)) - } else { - Err(Error::WifiState(con.wifi_state)) - } - } else { - Ok(()) - } - } else { - Err(Error::NoWifiSetup) - } - } -} diff --git a/src/blocking/dns.rs b/src/blocking/dns.rs deleted file mode 100644 index 48b9904..0000000 --- a/src/blocking/dns.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::client::{DNSState, UbloxClient}; -use super::timer; -use embassy_time::Duration; -use embedded_hal::digital::OutputPin; -use embedded_nal::{nb, AddrType, Dns, IpAddr}; -use heapless::String; -use ublox_sockets::Error; - -use crate::{blocking::timer::Timer, command::ping::*}; - -impl Dns for UbloxClient -where - C: atat::blocking::AtatClient, - RST: OutputPin, -{ - type Error = Error; - - fn get_host_by_address(&mut self, _ip_addr: IpAddr) -> nb::Result, Self::Error> { - unimplemented!() - } - - fn get_host_by_name( - &mut self, - hostname: &str, - _addr_type: AddrType, - ) -> nb::Result { - debug!("Lookup hostname: {}", hostname); - self.send_at(Ping { - hostname, - retry_num: 1, - }) - .map_err(|_| nb::Error::Other(Error::Unaddressable))?; - - self.dns_state = DNSState::Resolving; - - match Timer::with_timeout(Duration::from_secs(8), || { - if self.spin().is_err() { - return Some(Err(Error::Illegal)); - } - - match self.dns_state { - DNSState::Resolving => None, - DNSState::Resolved(ip) => Some(Ok(ip)), - _ => Some(Err(Error::Illegal)), - } - }) { - Ok(ip) => Ok(ip), - Err(timer::Error::Timeout) => Err(nb::Error::Other(Error::Timeout)), - Err(timer::Error::Other(e)) => Err(nb::Error::Other(e)), - } - } -} diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs deleted file mode 100644 index 208e4a5..0000000 --- a/src/blocking/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub(crate) mod client; -mod dns; -pub mod timer; -pub mod tls; - -#[cfg(feature = "socket-udp")] -pub mod udp_stack; - -#[cfg(feature = "socket-tcp")] -pub mod tcp_stack; - -pub use client::UbloxClient; diff --git a/src/blocking/tcp_stack.rs b/src/blocking/tcp_stack.rs deleted file mode 100644 index fb72ce3..0000000 --- a/src/blocking/tcp_stack.rs +++ /dev/null @@ -1,227 +0,0 @@ -use super::{client::new_socket_num, UbloxClient}; -use crate::{ - command::data_mode::*, - command::edm::{EdmAtCmdWrapper, EdmDataCommand}, - wifi::peer_builder::PeerUrlBuilder, -}; -use embedded_hal::digital::OutputPin; -/// Handles receiving data from sockets -/// implements TCP and UDP for WiFi client -use embedded_nal::{nb, SocketAddr, TcpClientStack}; - -use ublox_sockets::{Error, SocketHandle, TcpSocket, TcpState}; - -use crate::wifi::EGRESS_CHUNK_SIZE; - -impl TcpClientStack for UbloxClient -where - C: atat::blocking::AtatClient, - RST: OutputPin, -{ - type Error = Error; - - // Only return a SocketHandle to reference into the SocketSet owned by the UbloxClient, - // as the Socket object itself provides no value without accessing it though the client. - type TcpSocket = SocketHandle; - - /// Open a new TCP socket to the given address and port. The socket starts in the unconnected state. - fn socket(&mut self) -> Result { - self.connected_to_network().map_err(|_| Error::Illegal)?; - if let Some(ref mut sockets) = self.sockets { - // Check if there are any unused sockets available - if sockets.len() >= sockets.capacity() { - // Check if there are any sockets closed by remote, and close it - // if it has exceeded its timeout, in order to recycle it. - if sockets.recycle() { - return Err(Error::SocketSetFull); - } - } - - debug!("[TCP] Opening socket"); - - let socket_id = new_socket_num(sockets).unwrap(); - sockets.add(TcpSocket::new(socket_id)).map_err(|e| { - error!("[TCP] Opening socket Error: {:?}", e); - e - }) - } else { - Err(Error::Illegal) - } - } - - /// Connect to the given remote host and port. - fn connect( - &mut self, - socket: &mut Self::TcpSocket, - remote: SocketAddr, - ) -> nb::Result<(), Self::Error> { - if self.sockets.is_none() { - return Err(Error::Illegal.into()); - } - - debug!("[TCP] Connect socket"); - self.connected_to_network().map_err(|_| Error::Illegal)?; - - let url = PeerUrlBuilder::new() - .address(&remote) - .creds(self.security_credentials.clone()) - .tcp() - .map_err(|_| Error::Unaddressable)?; - - // If no socket is found we stop here - let mut tcp = self - .sockets - .as_mut() - .unwrap() - .get::>(*socket) - .map_err(Self::Error::from)?; - - tcp.set_state(TcpState::WaitingForConnect(remote)); - - match self - .send_internal(&EdmAtCmdWrapper(ConnectPeer { url: &url }), false) - .map_err(|_| Error::Unaddressable) - { - Ok(resp) => self - .socket_map - .insert_peer(resp.peer_handle, *socket) - .map_err(|_| Error::InvalidSocket)?, - Err(e) => { - let mut tcp = self - .sockets - .as_mut() - .unwrap() - .get::>(*socket) - .map_err(Self::Error::from)?; - tcp.set_state(TcpState::Created); - return Err(nb::Error::Other(e)); - } - } - - trace!("[TCP] Connecting socket: {:?} to url: {=str}", socket, url); - - // TODO: Timeout? - // TODO: Fix the fact that it doesen't wait for both connect messages - while { - matches!( - self.sockets - .as_mut() - .unwrap() - .get::>(*socket) - .map_err(Self::Error::from)? - .state(), - TcpState::WaitingForConnect(_) - ) - } { - self.spin().map_err(|_| Error::Illegal)?; - } - Ok(()) - } - - /// Check if this socket is still connected - fn is_connected(&mut self, socket: &Self::TcpSocket) -> Result { - self.connected_to_network().map_err(|_| Error::Illegal)?; - if let Some(ref mut sockets) = self.sockets { - let tcp = sockets.get::>(*socket)?; - Ok(tcp.is_connected()) - } else { - Err(Error::Illegal) - } - } - - /// Write to the stream. Returns the number of bytes written is returned - /// (which may be less than `buffer.len()`), or an error. - fn send( - &mut self, - socket: &mut Self::TcpSocket, - buffer: &[u8], - ) -> nb::Result { - self.connected_to_network().map_err(|_| Error::Illegal)?; - if let Some(ref mut sockets) = self.sockets { - let tcp = sockets - .get::>(*socket) - .map_err(|e| nb::Error::Other(e.into()))?; - - if !tcp.is_connected() { - return Err(Error::SocketClosed.into()); - } - - let channel = *self - .socket_map - .socket_to_channel_id(socket) - .ok_or(nb::Error::Other(Error::SocketClosed))?; - - for chunk in buffer.chunks(EGRESS_CHUNK_SIZE) { - self.send_internal( - &EdmDataCommand { - channel, - data: chunk, - }, - true, - ) - .map_err(|_| nb::Error::Other(Error::Unaddressable))?; - } - Ok(buffer.len()) - } else { - Err(Error::Illegal.into()) - } - } - - fn receive( - &mut self, - socket: &mut Self::TcpSocket, - buffer: &mut [u8], - ) -> nb::Result { - // TODO: Handle error states - self.spin().map_err(|_| nb::Error::Other(Error::Illegal))?; - if let Some(ref mut sockets) = self.sockets { - // Enable detecting closed socket from receive function - sockets.recycle(); - - let mut tcp = sockets - .get::>(*socket) - .map_err(Self::Error::from)?; - - Ok(tcp.recv_slice(buffer).map_err(Self::Error::from)?) - } else { - Err(Error::Illegal.into()) - } - } - - /// Close an existing TCP socket. - fn close(&mut self, socket: Self::TcpSocket) -> Result<(), Self::Error> { - if let Some(ref mut sockets) = self.sockets { - debug!("[TCP] Closing socket: {:?}", socket); - // If the socket is not found it is already removed - if let Ok(ref tcp) = sockets.get::>(socket) { - // If socket is not closed that means a connection excists which has to be closed - if !matches!( - tcp.state(), - TcpState::ShutdownForWrite(_) | TcpState::Created - ) { - if let Some(peer_handle) = self.socket_map.socket_to_peer(&tcp.handle()) { - let peer_handle = *peer_handle; - match self.send_at(ClosePeerConnection { peer_handle }) { - Err(crate::error::Error::AT(atat::Error::InvalidResponse)) | Ok(_) => { - () - } - Err(_) => return Err(Error::Unaddressable), - } - } else { - error!( - "Illigal state! Socket connected but not in socket map: {:?}", - tcp.handle() - ); - return Err(Error::Illegal); - } - } else { - // No connection exists the socket should be removed from the set here - sockets.remove(socket)?; - } - } - Ok(()) - } else { - Err(Error::Illegal) - } - } -} diff --git a/src/blocking/timer.rs b/src/blocking/timer.rs deleted file mode 100644 index 7ddae99..0000000 --- a/src/blocking/timer.rs +++ /dev/null @@ -1,42 +0,0 @@ -use embassy_time::{Duration, Instant}; - -pub struct Timer { - expires_at: Instant, -} - -pub enum Error { - Timeout, - Other(E), -} - -impl Timer { - pub fn after(duration: Duration) -> Self { - Self { - expires_at: Instant::now() + duration, - } - } - - pub fn with_timeout(timeout: Duration, mut e: F) -> Result> - where - F: FnMut() -> Option>, - { - let timer = Timer::after(timeout); - - loop { - if let Some(res) = e() { - return res.map_err(Error::Other); - } - if timer.expires_at <= Instant::now() { - return Err(Error::Timeout); - } - } - } - - pub fn wait(self) { - loop { - if self.expires_at <= Instant::now() { - break; - } - } - } -} diff --git a/src/blocking/tls.rs b/src/blocking/tls.rs deleted file mode 100644 index ed5687f..0000000 --- a/src/blocking/tls.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::UbloxClient; -use crate::{ - command::edm::BigEdmAtCmdWrapper, - command::security::{types::*, *}, - error::Error, -}; -use embedded_hal::digital::OutputPin; -use heapless::String; - -pub trait TLS { - fn import_certificate(&mut self, name: &str, certificate: &[u8]) -> Result<(), Error>; - fn import_root_ca(&mut self, name: &str, root_ca: &[u8]) -> Result<(), Error>; - fn import_private_key( - &mut self, - name: &str, - private_key: &[u8], - password: Option<&str>, - ) -> Result<(), Error>; -} - -impl TLS for UbloxClient -where - C: atat::blocking::AtatClient, - RST: OutputPin, -{ - /// Importing credentials enabeles their use for all further TCP connections - fn import_certificate(&mut self, name: &str, certificate: &[u8]) -> Result<(), Error> { - assert!(name.len() < 16); - - self.send_at(PrepareSecurityDataImport { - data_type: SecurityDataType::ClientCertificate, - data_size: certificate.len(), - internal_name: name, - password: None, - })?; - - self.send_internal( - &BigEdmAtCmdWrapper(SendSecurityDataImport { - data: atat::serde_bytes::Bytes::new(certificate), - }), - false, - )?; - - self.security_credentials - .c_cert_name - .replace(String::from(name)); - - Ok(()) - } - - /// Importing credentials enabeles their use for all further TCP connections - fn import_root_ca(&mut self, name: &str, root_ca: &[u8]) -> Result<(), Error> { - assert!(name.len() < 16); - - self.send_at(PrepareSecurityDataImport { - data_type: SecurityDataType::TrustedRootCA, - data_size: root_ca.len(), - internal_name: name, - password: None, - })?; - - self.send_internal( - &BigEdmAtCmdWrapper(SendSecurityDataImport { - data: atat::serde_bytes::Bytes::new(root_ca), - }), - false, - )?; - - self.security_credentials - .ca_cert_name - .replace(String::from(name)); - - Ok(()) - } - - /// Importing credentials enabeles their use for all further TCP connections - fn import_private_key( - &mut self, - name: &str, - private_key: &[u8], - password: Option<&str>, - ) -> Result<(), Error> { - assert!(name.len() < 16); - - self.send_at(PrepareSecurityDataImport { - data_type: SecurityDataType::ClientPrivateKey, - data_size: private_key.len(), - internal_name: name, - password, - })?; - - self.send_internal( - &BigEdmAtCmdWrapper(SendSecurityDataImport { - data: atat::serde_bytes::Bytes::new(private_key), - }), - false, - )?; - - self.security_credentials - .c_key_name - .replace(String::from(name)); - - Ok(()) - } -} diff --git a/src/blocking/udp_stack.rs b/src/blocking/udp_stack.rs deleted file mode 100644 index 795bc93..0000000 --- a/src/blocking/udp_stack.rs +++ /dev/null @@ -1,393 +0,0 @@ -use super::client::new_socket_num; -use super::UbloxClient; -use crate::{ - command::data_mode::*, - command::{ - data_mode::types::{IPVersion, ServerType, UDPBehaviour}, - edm::{EdmAtCmdWrapper, EdmDataCommand}, - }, - wifi::peer_builder::PeerUrlBuilder, -}; -use embedded_hal::digital::OutputPin; -use embedded_nal::{nb, SocketAddr, UdpFullStack}; - -use embedded_nal::UdpClientStack; -use ublox_sockets::{Error, SocketHandle, UdpSocket, UdpState}; - -use crate::wifi::EGRESS_CHUNK_SIZE; - -impl UdpClientStack for UbloxClient -where - C: atat::blocking::AtatClient, - RST: OutputPin, -{ - type Error = Error; - - // Only return a SocketHandle to reference into the SocketSet owned by the UbloxClient, - // as the Socket object itself provides no value without accessing it though the client. - type UdpSocket = SocketHandle; - - fn socket(&mut self) -> Result { - self.connected_to_network().map_err(|_| Error::Illegal)?; - if let Some(ref mut sockets) = self.sockets { - // Check if there are any unused sockets available - if sockets.len() >= sockets.capacity() { - // Check if there are any sockets closed by remote, and close it - // if it has exceeded its timeout, in order to recycle it. - if sockets.recycle() { - return Err(Error::SocketSetFull); - } - } - - let socket_id = new_socket_num(sockets).unwrap(); - debug!("[UDP] Opening socket"); - sockets.add(UdpSocket::new(socket_id)).map_err(|_| { - error!("[UDP] Opening socket Error: Socket set full"); - Error::SocketSetFull - }) - } else { - error!("[UDP] Opening socket Error: Missing socket set"); - Err(Error::Illegal) - } - } - - /// Connect a UDP socket with a peer using a dynamically selected port. - /// Selects a port number automatically and initializes for read/writing. - fn connect( - &mut self, - socket: &mut Self::UdpSocket, - remote: SocketAddr, - ) -> Result<(), Self::Error> { - if self.sockets.is_none() { - error!("[UDP] Connecting socket Error: Missing socket set"); - return Err(Error::Illegal); - } - let url = PeerUrlBuilder::new() - .address(&remote) - .udp() - .map_err(|_| Error::Unaddressable)?; - debug!("[UDP] Connecting Socket: {:?} to URL: {=str}", socket, url); - - self.connected_to_network().map_err(|_| Error::Illegal)?; - - // First look to see if socket is valid - let mut udp = self - .sockets - .as_mut() - .unwrap() - .get::>(*socket)?; - udp.bind(remote)?; - - // Then connect modem - match self - .send_internal(&EdmAtCmdWrapper(ConnectPeer { url: &url }), true) - .map_err(|_| Error::Unaddressable) - { - Ok(resp) => self - .socket_map - .insert_peer(resp.peer_handle.into(), *socket) - .map_err(|_| Error::InvalidSocket)?, - - Err(e) => { - let mut udp = self - .sockets - .as_mut() - .unwrap() - .get::>(*socket)?; - udp.close(); - return Err(e); - } - } - while self - .sockets - .as_mut() - .unwrap() - .get::>(*socket)? - .state() - == UdpState::Closed - { - self.spin().map_err(|_| Error::Illegal)?; - } - Ok(()) - } - - /// Send a datagram to the remote host. - fn send(&mut self, socket: &mut Self::UdpSocket, buffer: &[u8]) -> nb::Result<(), Self::Error> { - self.spin().map_err(|_| Error::Illegal)?; - if let Some(ref mut sockets) = self.sockets { - // No send for server sockets! - if self.udp_listener.is_bound(*socket) { - return Err(nb::Error::Other(Error::Illegal)); - } - - let udp = sockets - .get::>(*socket) - .map_err(Self::Error::from)?; - - if !udp.is_open() { - return Err(Error::SocketClosed.into()); - } - - let channel = *self - .socket_map - .socket_to_channel_id(socket) - .ok_or(nb::Error::Other(Error::SocketClosed))?; - - for chunk in buffer.chunks(EGRESS_CHUNK_SIZE) { - self.send_internal( - &EdmDataCommand { - channel, - data: chunk, - }, - true, - ) - .map_err(|_| nb::Error::Other(Error::Unaddressable))?; - } - Ok(()) - } else { - Err(Error::Illegal.into()) - } - } - - /// Read a datagram the remote host has sent to us. Returns `Ok(n)`, which - /// means a datagram of size `n` has been received and it has been placed - /// in `&buffer[0..n]`, or an error. - fn receive( - &mut self, - socket: &mut Self::UdpSocket, - buffer: &mut [u8], - ) -> nb::Result<(usize, SocketAddr), Self::Error> { - self.spin().ok(); - let udp_listener = &mut self.udp_listener; - // Handle server sockets - if udp_listener.is_bound(*socket) { - // Nothing available, would block - if !udp_listener.available(*socket).unwrap_or(false) { - return Err(nb::Error::WouldBlock); - } - - let (connection_handle, remote) = self - .udp_listener - .peek_remote(*socket) - .map_err(|_| Error::NotBound)?; - - if let Some(ref mut sockets) = self.sockets { - let mut udp = sockets - .get::>(*connection_handle) - .map_err(|_| Self::Error::InvalidSocket)?; - - let bytes = udp.recv_slice(buffer).map_err(Self::Error::from)?; - Ok((bytes, remote.clone())) - } else { - Err(Error::Illegal.into()) - } - - // Handle reciving for udp normal sockets - } else if let Some(ref mut sockets) = self.sockets { - let mut udp = sockets - .get::>(*socket) - .map_err(Self::Error::from)?; - - let bytes = udp.recv_slice(buffer).map_err(Self::Error::from)?; - - let endpoint = udp.endpoint().ok_or(Error::SocketClosed)?; - Ok((bytes, endpoint)) - } else { - Err(Error::Illegal.into()) - } - } - - /// Close an existing UDP socket. - fn close(&mut self, socket: Self::UdpSocket) -> Result<(), Self::Error> { - self.spin().ok(); - // Close server socket - if self.udp_listener.is_bound(socket) { - debug!("[UDP] Closing Server socket: {:?}", socket); - - // ID 2 used by UDP server - self.send_internal( - &EdmAtCmdWrapper(ServerConfiguration { - id: 2, - server_config: ServerType::Disabled, - }), - true, - ) - .map_err(|_| Error::Unaddressable)?; - - // Borrow socket set to close server socket - if let Some(ref mut sockets) = self.sockets { - // If socket in socket set close - if sockets.remove(socket).is_err() { - error!( - "[UDP] Closing server socket error: No socket matching: {:?}", - socket - ); - return Err(Error::InvalidSocket); - } - } else { - return Err(Error::Illegal); - } - - // Close incomming connections - while self.udp_listener.available(socket).unwrap_or(false) { - if let Ok((connection_handle, _)) = self.udp_listener.get_remote(socket) { - debug!( - "[UDP] Closing incomming socket for Server: {:?}", - connection_handle - ); - self.close(connection_handle)?; - } else { - error!("[UDP] Incomming socket for server error - Listener says available, while nothing present"); - } - } - - // Unbind server socket in listener - self.udp_listener.unbind(socket).map_err(|_| { - error!( - "[UDP] Closing socket error: No server socket matching: {:?}", - socket - ); - Error::Illegal - }) - // Handle normal sockets - } else if let Some(ref mut sockets) = self.sockets { - debug!("[UDP] Closing socket: {:?}", socket); - // If no sockets exists, nothing to close. - if let Ok(ref mut udp) = sockets.get::>(socket) { - trace!("[UDP] Closing socket state: {:?}", udp.state()); - match udp.state() { - UdpState::Closed => { - sockets.remove(socket).ok(); - } - UdpState::Established => { - // FIXME:udp.close(); - if let Some(peer_handle) = self.socket_map.socket_to_peer(&udp.handle()) { - let peer_handle = *peer_handle; - self.send_at(ClosePeerConnection { peer_handle }) - .map_err(|_| Error::Unaddressable)?; - } - } - } - } else { - error!( - "[UDP] Closing socket error: No socket matching: {:?}", - socket - ); - return Err(Error::InvalidSocket); - } - Ok(()) - } else { - Err(Error::Illegal) - } - } -} - -/// UDP Full Stack -/// -/// This fullstack is build for request-response type servers due to HW/SW limitations -/// Limitations: -/// - The driver can only send to Socket addresses that have send data first. -/// - The driver can only call send_to once after reciving data once. -/// - The driver has to call send_to after reciving data, to release the socket bound by remote host, -/// even if just sending no bytes. Else these sockets will be held open until closure of server socket. -/// -impl UdpFullStack for UbloxClient -where - C: atat::blocking::AtatClient, - RST: OutputPin, -{ - fn bind(&mut self, socket: &mut Self::UdpSocket, local_port: u16) -> Result<(), Self::Error> { - if self.connected_to_network().is_err() || self.udp_listener.is_port_bound(local_port) { - return Err(Error::Illegal); - } - - debug!( - "[UDP] binding socket: {:?} to port: {:?}", - socket, local_port - ); - - // ID 2 used by UDP server - self.send_internal( - &EdmAtCmdWrapper(ServerConfiguration { - id: 2, - server_config: ServerType::UDP( - local_port, - UDPBehaviour::AutoConnect, - IPVersion::IPv4, - ), - }), - true, - ) - .map_err(|_| Error::Unaddressable)?; - - self.udp_listener - .bind(*socket, local_port) - .map_err(|_| Error::Illegal)?; - - Ok(()) - } - - fn send_to( - &mut self, - socket: &mut Self::UdpSocket, - remote: SocketAddr, - buffer: &[u8], - ) -> nb::Result<(), Self::Error> { - self.spin().map_err(|_| Error::Illegal)?; - // Protect against non server sockets - if !self.udp_listener.is_bound(*socket) { - return Err(Error::Illegal.into()); - } - // Check incomming sockets for the socket address - if let Some(connection_socket) = self.udp_listener.get_outgoing(socket, remote) { - if let Some(ref mut sockets) = self.sockets { - if buffer.len() == 0 { - self.close(connection_socket)?; - return Ok(()); - } - - let udp = sockets - .get::>(connection_socket) - .map_err(Self::Error::from)?; - - if !udp.is_open() { - return Err(Error::SocketClosed.into()); - } - - let channel = *self - .socket_map - .socket_to_channel_id(&connection_socket) - .ok_or(nb::Error::WouldBlock)?; - - for chunk in buffer.chunks(EGRESS_CHUNK_SIZE) { - self.send_internal( - &EdmDataCommand { - channel, - data: chunk, - }, - false, - ) - .map_err(|_| nb::Error::Other(Error::Unaddressable))?; - } - self.close(connection_socket).unwrap(); - Ok(()) - } else { - Err(Error::Illegal.into()) - } - } else { - Err(Error::Illegal.into()) - } - - ////// Do with URC - // Crate a new SocketBuffer allocation for the incoming connection - // let mut tcp = self - // .sockets - // .as_mut() - // .ok_or(Error::Illegal)? - // .get::>(data_socket) - // .map_err(Self::Error::from)?; - - // tcp.update_handle(handle); - // tcp.set_state(TcpState::Connected(remote.clone())); - } -} diff --git a/src/command/data_mode/mod.rs b/src/command/data_mode/mod.rs index fa9da8d..616b03d 100644 --- a/src/command/data_mode/mod.rs +++ b/src/command/data_mode/mod.rs @@ -7,7 +7,6 @@ use atat::atat_derive::AtatCmd; use heapless::String; use responses::*; use types::*; -use ublox_sockets::PeerHandle; use super::NoResponse; @@ -28,6 +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")] #[derive(Clone, AtatCmd)] #[at_cmd("+UDCP", ConnectPeerResponse, timeout_ms = 5000)] pub struct ConnectPeer<'a> { @@ -38,6 +38,7 @@ pub struct ConnectPeer<'a> { /// 5.3 Close peer connection +UDCPC /// /// Closes an existing peer connection. +#[cfg(feature = "ublox-sockets")] #[derive(Clone, AtatCmd)] #[at_cmd("+UDCPC", NoResponse, timeout_ms = 1000)] pub struct ClosePeerConnection { @@ -65,6 +66,7 @@ pub struct SetDefaultRemotePeer<'a> { /// 5.5 Peer list +UDLP /// /// This command reads the connected peers (peer handle). +#[cfg(feature = "ublox-sockets")] #[derive(Clone, AtatCmd)] #[at_cmd("+UDLP?", PeerListResponse, timeout_ms = 1000)] pub struct PeerList; diff --git a/src/command/data_mode/responses.rs b/src/command/data_mode/responses.rs index 0c8832c..03354b0 100644 --- a/src/command/data_mode/responses.rs +++ b/src/command/data_mode/responses.rs @@ -1,26 +1,26 @@ //! Responses for Data Mode use atat::atat_derive::AtatResp; -use heapless::String; -use ublox_sockets::PeerHandle; /// 5.2 Connect peer +UDCP +#[cfg(feature = "ublox-sockets")] #[derive(Clone, AtatResp)] pub struct ConnectPeerResponse { #[at_arg(position = 0)] - pub peer_handle: PeerHandle, + pub peer_handle: ublox_sockets::PeerHandle, } /// 5.5 Peer list +UDLP +#[cfg(feature = "ublox-sockets")] #[derive(Clone, AtatResp)] pub struct PeerListResponse { #[at_arg(position = 0)] - pub peer_handle: PeerHandle, + pub peer_handle: ublox_sockets::PeerHandle, #[at_arg(position = 1)] - pub protocol: String<64>, + pub protocol: heapless::String<64>, #[at_arg(position = 2)] - pub local_address: String<64>, + pub local_address: heapless::String<64>, #[at_arg(position = 3)] - pub remote_address: String<64>, + pub remote_address: heapless::String<64>, } /// 5.12 Bind +UDBIND diff --git a/src/command/data_mode/urc.rs b/src/command/data_mode/urc.rs index 8eb2441..4ec7f75 100644 --- a/src/command/data_mode/urc.rs +++ b/src/command/data_mode/urc.rs @@ -1,15 +1,14 @@ //! Unsolicited responses for Data mode Commands +#[allow(unused_imports)] use super::types::*; -use atat::atat_derive::AtatResp; -use atat::heapless_bytes::Bytes; -use ublox_sockets::PeerHandle; /// 5.10 Peer connected +UUDPC -#[derive(Debug, PartialEq, Clone, AtatResp)] +#[cfg(feature = "ublox-sockets")] +#[derive(Debug, PartialEq, Clone, atat::atat_derive::AtatResp)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PeerConnected { #[at_arg(position = 0)] - pub handle: PeerHandle, + pub handle: ublox_sockets::PeerHandle, #[at_arg(position = 1)] pub connection_type: ConnectionType, #[at_arg(position = 2)] @@ -18,21 +17,22 @@ pub struct PeerConnected { // pub local_address: IpAddr, #[at_arg(position = 3)] #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub local_address: Bytes<40>, + pub local_address: atat::heapless_bytes::Bytes<40>, #[at_arg(position = 4)] pub local_port: u16, // #[at_arg(position = 5)] // pub remote_address: IpAddr, #[at_arg(position = 5)] #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub remote_address: Bytes<40>, + pub remote_address: atat::heapless_bytes::Bytes<40>, #[at_arg(position = 6)] pub remote_port: u16, } /// 5.11 Peer disconnected +UUDPD -#[derive(Debug, PartialEq, Clone, AtatResp)] +#[cfg(feature = "ublox-sockets")] +#[derive(Debug, PartialEq, Clone, atat::atat_derive::AtatResp)] pub struct PeerDisconnected { #[at_arg(position = 0)] - pub handle: PeerHandle, + pub handle: ublox_sockets::PeerHandle, } diff --git a/src/command/edm/urc.rs b/src/command/edm/urc.rs index 7d382d8..4b78503 100644 --- a/src/command/edm/urc.rs +++ b/src/command/edm/urc.rs @@ -20,6 +20,15 @@ pub enum EdmEvent { StartUp, } +impl EdmEvent { + pub fn extract_urc(self) -> Option { + match self { + EdmEvent::ATEvent(urc) => Some(urc), + _ => None, + } + } +} + impl AtatUrc for EdmEvent { /// The type of the response. Usually the enum this trait is implemented on. type Response = Self; diff --git a/src/command/mod.rs b/src/command/mod.rs index 083b282..6b2b680 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -1,8 +1,10 @@ //! AT Commands for U-Blox short range module family\ //! Following the [u-connect ATCommands Manual](https://www.u-blox.com/sites/default/files/u-connect-ATCommands-Manual_(UBX-14044127).pdf) +#[cfg(feature = "edm")] pub mod custom_digest; pub mod data_mode; +#[cfg(feature = "edm")] pub mod edm; pub mod ethernet; pub mod general; @@ -28,9 +30,11 @@ pub enum Urc { #[at_urc("+STARTUP")] StartUp, /// 5.10 Peer connected +UUDPC + #[cfg(feature = "ublox-sockets")] #[at_urc("+UUDPC")] PeerConnected(data_mode::urc::PeerConnected), /// 5.11 Peer disconnected +UUDPD + #[cfg(feature = "ublox-sockets")] #[at_urc("+UUDPD")] PeerDisconnected(data_mode::urc::PeerDisconnected), /// 7.15 Wi-Fi Link connected +UUWLE diff --git a/src/command/network/mod.rs b/src/command/network/mod.rs index c80b06e..c8e401b 100644 --- a/src/command/network/mod.rs +++ b/src/command/network/mod.rs @@ -23,7 +23,7 @@ pub struct SetNetworkHostName<'a> { /// /// Shows current status of the network interface id. #[derive(Clone, AtatCmd)] -#[at_cmd("+UNSTAT", NetworkStatusResponse, timeout_ms = 1000)] +#[at_cmd("+UNSTAT", NetworkStatusResponse, timeout_ms = 3000)] pub struct GetNetworkStatus { #[at_arg(position = 0)] pub interface_id: u8, diff --git a/src/error.rs b/src/error.rs index 5c4d426..c770c9b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "ublox-sockets")] pub use ublox_sockets::Error as SocketError; #[derive(Debug)] @@ -17,6 +18,7 @@ pub enum Error { // NetworkState(crate::wifi::connection::NetworkState), NoWifiSetup, // WifiState(crate::wifi::connection::WiFiState), + #[cfg(feature = "ublox-sockets")] Socket(ublox_sockets::Error), AT(atat::Error), Busy, @@ -40,6 +42,7 @@ impl From for Error { } } +#[cfg(feature = "ublox-sockets")] impl From for Error { fn from(e: ublox_sockets::Error) -> Self { Error::Socket(e) diff --git a/src/lib.rs b/src/lib.rs index c1adaa5..180e4fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,17 @@ -#![cfg_attr(all(not(test), not(feature = "std")), no_std)] +#![cfg_attr(not(test), no_std)] #![allow(async_fn_in_trait)] +#[cfg(all(feature = "ppp", feature = "internal-network-stack"))] +compile_error!("You may not enable both `ppp` and `internal-network-stack` features."); + mod fmt; pub mod asynch; -pub use embedded_nal_async; - -pub use ublox_sockets; - mod connection; mod network; mod peer_builder; -// mod blocking; mod hex; pub use atat; @@ -29,7 +27,6 @@ pub use peer_builder::SecurityCredentials; // - Network scan // - AP Mode (control) // - TCP listener stack -// - (Blocking client?) // - // // FIXME: