diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml
deleted file mode 100644
index 4974b63..0000000
--- a/.github/workflows/audit.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: Security audit
-on:
- push:
- paths:
- - '**/Cargo.toml'
- - '**/Cargo.lock'
-jobs:
- security_audit:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v1
- - uses: actions-rs/audit-check@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..8797b4d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,56 @@
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+
+name: CI
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ name: Build & Test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout source code
+ uses: actions/checkout@v3
+ - uses: dsherret/rust-toolchain-file@v1
+ - name: Build
+ uses: actions-rs/cargo@v1
+ with:
+ command: build
+ args: --all --features "defmt-impl,lara-r6" --target thumbv7em-none-eabihf
+
+ - name: Test
+ uses: actions-rs/cargo@v1
+ with:
+ command: test
+ args: --lib --features "log,lara-r6"
+ env:
+ DEFMT_LOG: off
+
+ rustfmt:
+ name: rustfmt
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout source code
+ uses: actions/checkout@v3
+ - uses: dsherret/rust-toolchain-file@v1
+ - name: Rustfmt
+ run: cargo fmt -- --check
+
+ clippy:
+ name: clippy
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout source code
+ uses: actions/checkout@v3
+ - uses: dsherret/rust-toolchain-file@v1
+ - name: Run clippy
+ uses: actions-rs/clippy-check@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ args: --features "lara-r6" -- ${{ env.CLIPPY_PARAMS }}
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
deleted file mode 100644
index 68df0b5..0000000
--- a/.github/workflows/docs.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: Documentation
-
-on:
- push:
- branches:
- - master
-
-jobs:
- docs:
- name: Documentation
- runs-on: ubuntu-latest
- steps:
- - name: Checkout source code
- uses: actions/checkout@v2
- with:
- persist-credentials: false
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
-
- - name: Build documentation
- uses: actions-rs/cargo@v1
- with:
- command: doc
- args: --verbose --no-deps --features "log,lara-r6"
-
- # - name: Finalize documentation
- # run: |
- # CRATE_NAME=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]' | cut -f2 -d"/")
- # echo "" > target/doc/index.html
- # touch target/doc/.nojekyll
- # - name: Upload as artifact
- # uses: actions/upload-artifact@v2
- # with:
- # name: Documentation
- # path: target/doc
-
- # - name: Deploy
- # uses: JamesIves/github-pages-deploy-action@releases/v3
- # with:
- # ACCESS_TOKEN: ${{ secrets.GH_PAT }}
- # BRANCH: gh-pages
- # FOLDER: target/doc
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
deleted file mode 100644
index 4f385f6..0000000
--- a/.github/workflows/lint.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-name: Lint
-
-on:
- push:
- branches:
- - master
- pull_request:
-
-defaults:
- run:
- shell: bash
-
-env:
- # CLIPPY_PARAMS: -W clippy::all -W clippy::pedantic -W clippy::nursery -W clippy::cargo
- CLIPPY_PARAMS:
-
-jobs:
- rustfmt:
- name: rustfmt
- runs-on: ubuntu-latest
- steps:
- - name: Checkout source code
- uses: actions/checkout@v2
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - name: Run rustfmt
- uses: actions-rs/cargo@v1
- with:
- command: fmt
- args: --all -- --check --verbose
-
- # tomlfmt:
- # name: tomlfmt
- # runs-on: ubuntu-latest
- # steps:
- # - name: Checkout source code
- # uses: actions/checkout@v2
-
- # - name: Install Rust
- # uses: actions-rs/toolchain@v1
- # with:
- # profile: minimal
- # toolchain: nightly
- # override: true
-
- # - name: Install tomlfmt
- # uses: actions-rs/install@v0.1
- # with:
- # crate: cargo-tomlfmt
- # version: latest
- # use-tool-cache: true
-
- # - name: Run Tomlfmt
- # uses: actions-rs/cargo@v1
- # with:
- # command: tomlfmt
- # args: --dryrun
-
- clippy:
- name: clippy
- runs-on: ubuntu-latest
- steps:
- - name: Checkout source code
- uses: actions/checkout@v2
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- override: true
- components: clippy
-
- - name: Run clippy
- uses: actions-rs/clippy-check@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- args: --features "lara-r6" -- ${{ env.CLIPPY_PARAMS }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 85aa2dd..0000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: Test
-
-on:
- push:
- branches:
- - master
- pull_request:
-
-jobs:
- test:
- name: Test
- runs-on: ubuntu-latest
- steps:
- - name: Checkout source code
- uses: actions/checkout@v2
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- target: thumbv7m-none-eabi
- override: true
-
- - name: Build
- uses: actions-rs/cargo@v1
- with:
- command: build
- args: --all --features "defmt-impl,lara-r6" --target thumbv7m-none-eabi
-
- - name: Test
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --lib --features "log,lara-r6"
diff --git a/Cargo.toml b/Cargo.toml
index fc2bad9..2420618 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,5 +9,5 @@ members = [
]
[patch.crates-io]
-atat = { git = "https://github.com/BlackbirdHQ/atat", rev = "70283be" }
+atat = { git = "https://github.com/BlackbirdHQ/atat", rev = "c5caaf7" }
# ublox-sockets = { path = "../ublox-sockets" }
\ No newline at end of file
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..3cd5460
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,7 @@
+[toolchain]
+channel = "nightly-2023-06-28"
+components = [ "rust-src", "rustfmt", "llvm-tools-preview", "clippy" ]
+targets = [
+ "x86_64-unknown-linux-gnu",
+ "thumbv7em-none-eabihf"
+]
diff --git a/ublox-cellular/Cargo.toml b/ublox-cellular/Cargo.toml
index d202223..26704ae 100644
--- a/ublox-cellular/Cargo.toml
+++ b/ublox-cellular/Cargo.toml
@@ -16,17 +16,18 @@ doctest = false
[dependencies]
# atat = { version = "0.18", features = ["derive", "bytes"] }
-atat = { git = "https://github.com/BlackbirdHQ/atat", rev = "70283be", features = ["derive", "defmt", "bytes"] }
+atat = { git = "https://github.com/BlackbirdHQ/atat", rev = "c5caaf7", features = ["derive", "defmt", "bytes"] }
embedded-hal = "=1.0.0-rc.1"
embedded-nal = "0.6"
-fugit = { version = "0.3" }
-fugit-timer = { version = "0.1.3" }
hash32 = "^0.2.1"
hash32-derive = "^0.1.0"
heapless = { version = "^0.7", features = ["serde"] }
nb = "^1"
serde = { version = "^1", default-features = false, features = ["derive"] }
-ublox-sockets = "0.5.0"
+# ublox-sockets = "0.5.0"
+ublox-sockets = { git = "https://github.com/BlackbirdHQ/ublox-sockets", rev = "b1ff942" }
+embassy-time = "0.1"
+embedded-io = "0.5"
# Enable `serde` feature of `no-std-net`
no-std-net = { version = "^0.5", features = ["serde"] }
@@ -40,7 +41,7 @@ default = ["socket-udp", "socket-tcp"]
async = ["atat/async"]
# Use `defmt-impl to enable defmt based logging
-defmt-impl = ["defmt", "ublox-sockets/defmt", "fugit/defmt", "atat/defmt", "heapless/defmt-impl"]
+defmt-impl = ["defmt", "ublox-sockets/defmt", "atat/defmt", "heapless/defmt-impl"]
# Use `log-impl` to enable log based logging
log-impl = ["log", "ublox-sockets/log", "atat/log"]
diff --git a/ublox-cellular/src/blocking_timer.rs b/ublox-cellular/src/blocking_timer.rs
new file mode 100644
index 0000000..15be27b
--- /dev/null
+++ b/ublox-cellular/src/blocking_timer.rs
@@ -0,0 +1,21 @@
+use embassy_time::{Duration, Instant};
+
+pub struct BlockingTimer {
+ expires_at: Instant,
+}
+
+impl BlockingTimer {
+ pub fn after(duration: Duration) -> Self {
+ Self {
+ expires_at: Instant::now() + duration,
+ }
+ }
+
+ pub fn wait(self) {
+ loop {
+ if self.expires_at <= Instant::now() {
+ break;
+ }
+ }
+ }
+}
diff --git a/ublox-cellular/src/client.rs b/ublox-cellular/src/client.rs
index d8c1177..b5c4e44 100644
--- a/ublox-cellular/src/client.rs
+++ b/ublox-cellular/src/client.rs
@@ -1,9 +1,9 @@
-use atat::blocking::AtatClient;
-use embedded_hal::digital::{InputPin, OutputPin};
-use fugit::ExtU32;
+use atat::{blocking::AtatClient, AtatUrcChannel};
+use embassy_time::Duration;
use ublox_sockets::SocketSet;
use crate::{
+ blocking_timer::BlockingTimer,
command::device_lock::{responses::PinStatus, types::PinStatusCode, GetPinStatus},
command::{
control::{
@@ -31,12 +31,13 @@ use crate::{
},
psn::{types::PSEventReportingMode, SetPacketSwitchedEventReporting},
},
- config::Config,
- error::{from_clock, Error, GenericError},
+ config::CellularConfig,
+ error::{Error, GenericError},
network::{AtTx, Network},
power::PowerState,
registration::ConnectionState,
services::data::ContextState,
+ UbloxCellularBuffers, UbloxCellularIngress, UbloxCellularUrcChannel,
};
use ip_transport_layer::{types::HexMode, SetHexMode};
use network_service::{types::NetworkRegistrationUrcConfig, SetNetworkRegistrationStatus};
@@ -45,6 +46,9 @@ use psn::{
SetEPSNetworkRegistrationStatus, SetGPRSNetworkRegistrationStatus,
};
+pub(crate) const URC_CAPACITY: usize = 3;
+pub(crate) const URC_SUBSCRIBERS: usize = 1;
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum State {
@@ -56,117 +60,79 @@ pub enum State {
FullyInitialized,
}
-pub struct Device
-where
- C: AtatClient,
- CLK: 'static + fugit_timer::Timer,
- RST: OutputPin,
- PWR: OutputPin,
- DTR: OutputPin,
- VINT: InputPin,
-{
- pub(crate) config: Config,
- pub(crate) network: Network,
+pub struct Device<'buf, 'sub, AtCl, AtUrcCh, Config, const N: usize, const L: usize> {
+ pub(crate) config: Config,
+ pub(crate) network: Network<'sub, AtCl>,
+ urc_channel: &'buf AtUrcCh,
pub(crate) state: State,
pub(crate) power_state: PowerState,
// Ublox devices can hold a maximum of 6 active sockets
- pub(crate) sockets: Option<&'static mut SocketSet>,
+ pub(crate) sockets: Option<&'static mut SocketSet>,
}
-impl Drop
- for Device
+impl<'buf, 'sub, W, Config, const INGRESS_BUF_SIZE: usize, const N: usize, const L: usize>
+ Device<
+ 'buf,
+ 'sub,
+ atat::blocking::Client<'buf, W, INGRESS_BUF_SIZE>,
+ UbloxCellularUrcChannel,
+ Config,
+ N,
+ L,
+ >
where
- C: AtatClient,
- CLK: fugit_timer::Timer,
- RST: OutputPin,
- PWR: OutputPin,
- DTR: OutputPin,
- VINT: InputPin,
+ 'buf: 'sub,
+ W: embedded_io::Write,
+ Config: CellularConfig,
{
- fn drop(&mut self) {
- if self.state != State::Off {
- self.state = State::Off;
- self.hard_power_off().ok();
- }
+ /// Create new u-blox device
+ ///
+ /// Look for [`data_service`](Device::data_service) how to handle data connection automatically.
+ ///
+ pub fn from_buffers(
+ buffers: &'buf UbloxCellularBuffers,
+ tx: W,
+ config: Config,
+ ) -> (UbloxCellularIngress, Self) {
+ let (ingress, client) = buffers.split_blocking(
+ tx,
+ atat::DefaultDigester::::default(),
+ atat::Config::default(),
+ );
+
+ (ingress, Device::new(client, &buffers.urc_channel, config))
}
}
-impl
- Device
+impl<'buf, 'sub, AtCl, AtUrcCh, Config, const N: usize, const L: usize>
+ Device<'buf, 'sub, AtCl, AtUrcCh, Config, N, L>
where
- C: AtatClient,
- CLK: fugit_timer::Timer,
- RST: OutputPin,
- PWR: OutputPin,
- DTR: OutputPin,
- VINT: InputPin,
+ 'buf: 'sub,
+ AtCl: AtatClient,
+ AtUrcCh: AtatUrcChannel,
+ Config: CellularConfig,
{
- /// Create new u-blox device
- ///
- /// Look for [`data_service`](Device::data_service) how to handle data connection automatically.
- ///
- /// # Examples
- ///
- /// ```ignore
- /// use bbqueue::BBBuffer;
- /// use ublox_cellular::prelude::*;
- /// use ublox_cellular::atat;
- /// use ublox_cellular::{Config, GsmClient};
- ///
- /// const RX_BUF_LEN: usize = 256;
- /// const RES_CAPACITY: usize = 256;
- /// const URC_CAPACITY: usize = 256;
- /// const MAX_SOCKET_COUNT: usize = 1;
- /// static mut RES_QUEUE: BBBuffer = BBBuffer::new();
- /// static mut URC_QUEUE: BBBuffer = BBBuffer::new();
- ///
- /// type UbloxResetPin = gpio::Gpio26;
- ///
- /// let queues = atat::Queues {
- /// res_queue: unsafe { RES_QUEUE.try_split_framed().unwrap() },
- /// urc_queue: unsafe { URC_QUEUE.try_split_framed().unwrap() },
- /// };
- ///
- /// let (atat_client, mut ingress) =
- /// atat::ClientBuilder::<_, _, _, TIMER_HZ, RX_BUF_LEN, RES_CAPACITY, URC_CAPACITY>::new(
- /// tx,
- /// timer::SysTimer::new(),
- /// atat::AtDigester::::new(),
- /// atat::Config::new(atat::Mode::Timeout),
- /// )
- /// .build(queues);
- ///
- ///
- /// let mut modem = GsmClient::<
- /// _,
- /// _,
- /// UbloxResetPin,
- /// gpio::Gpio0,
- /// gpio::Gpio0,
- /// gpio::Gpio0,
- /// TIMER_HZ,
- /// MAX_SOCKET_COUNT,
- /// SOCKET_RING_BUFFER_LEN,
- /// >::new(
- /// atat_client,
- /// timer::SysTimer::new(),
- /// Config::new("").with_flow_control().with_rst(reset),
- /// );
- /// ```
- pub fn new(client: C, timer: CLK, config: Config) -> Self {
- let mut device = Self {
+ pub fn new(client: AtCl, urc_channel: &'buf AtUrcCh, config: Config) -> Self {
+ let urc_subscription = urc_channel.subscribe().unwrap();
+ Self {
config,
+ network: Network::new(AtTx::new(client, urc_subscription)),
state: State::Off,
power_state: PowerState::Off,
- network: Network::new(AtTx::new(client, 10), timer),
sockets: None,
- };
-
- device.power_state = device.power_state().unwrap_or(PowerState::Off);
- device
+ urc_channel,
+ }
}
+}
+impl<'buf, 'sub, AtCl, AtUrcCh, Config, const N: usize, const L: usize>
+ Device<'buf, 'sub, AtCl, AtUrcCh, Config, N, L>
+where
+ 'buf: 'sub,
+ AtCl: AtatClient,
+ Config: CellularConfig,
+{
/// Set storage for TCP/UDP sockets
///
/// # Examples
@@ -174,11 +140,10 @@ where
/// ```ignore
/// use ublox_cellular::sockets::SocketSet;
///
- /// const TIMER_HZ: u32 = 1000;
/// const MAX_SOCKET_COUNT: usize = 1;
/// const SOCKET_RING_BUFFER_LEN: usize = 1024;
///
- /// static mut SOCKET_SET: Option> = None;
+ /// static mut SOCKET_SET: Option> = None;
///
/// unsafe {
/// SOCKET_SET = Some(SocketSet::new());
@@ -186,12 +151,12 @@ where
///
/// modem.set_socket_storage(unsafe { SOCKET_SET.as_mut().unwrap() });
/// ```
- pub fn set_socket_storage(&mut self, socket_set: &'static mut SocketSet) {
+ 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> {
+ pub fn take_socket_storage(&mut self) -> Option<&'static mut SocketSet> {
self.sockets.take()
}
@@ -233,35 +198,13 @@ where
self.clear_buffers()?;
- if self.config.baud_rate > 230_400_u32 {
- // Needs a way to reconfigure uart baud rate temporarily
- // Relevant issue: https://github.com/rust-embedded/embedded-hal/issues/79
- return Err(Error::_Unknown);
-
- // self.network.send_internal(
- // &SetDataRate {
- // rate: BaudRate::B115200,
- // },
- // true,
- // )?;
-
- // NOTE: On the UART AT interface, after the reception of the "OK" result code for the +IPR command, the DTE
- // shall wait for at least 100 ms before issuing a new AT command; this is to guarantee a proper baud rate
- // reconfiguration.
-
- // UART end
- // delay(100);
- // UART begin(self.config.baud_rate)
-
- // self.is_alive()?;
- } else {
- // Make sure AT commands parser is in clean state.
- // self.network.at_tx.reset()?;
- self.power_on()?;
- }
+ self.power_on()?;
// At this point, if is_alive fails, the configured Baud rate is probably wrong
- self.is_alive(10).map_err(|_| Error::BaudDetection)?;
+ if let Err(e) = self.is_alive(5).map_err(|_| Error::BaudDetection) {
+ self.hard_reset()?;
+ return Err(e);
+ }
// Extended errors on
self.network.send_internal(
@@ -329,7 +272,7 @@ where
false,
)?;
- if self.config.hex_mode {
+ if Config::HEX_MODE {
self.network.send_internal(
&SetHexMode {
hex_mode_disable: HexMode::Enabled,
@@ -347,7 +290,7 @@ where
// Tell module whether we support flow control
// FIXME: Use AT+IFC=2,2 instead of AT&K here
- if self.config.flow_control {
+ if Config::FLOW_CONTROL {
self.network.send_internal(
&SetFlowControl {
value: FlowControl::RtsCts,
@@ -408,12 +351,7 @@ where
_ => {}
}
- self.network
- .status
- .timer
- .start(1.secs())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
+ BlockingTimer::after(Duration::from_secs(1)).wait();
}
// There was an error initializing the SIM
@@ -531,14 +469,6 @@ where
sockets.prune();
}
- // Allow ATAT some time to clear the buffers
- self.network
- .status
- .timer
- .start(300.millis())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
-
Ok(())
}
@@ -560,6 +490,9 @@ where
warn!("Packet domain event reporting set failed");
}
+ // FIXME: Currently `atat` is unable to distinguish `xREG` family of
+ // commands from URC's
+
// CREG URC
self.network.send_internal(
&SetNetworkRegistrationStatus {
@@ -589,7 +522,6 @@ where
fn handle_urc_internal(&mut self) -> Result<(), Error> {
if let Some(ref mut sockets) = self.sockets.as_deref_mut() {
- let ts = self.network.status.timer.now();
self.network
.at_tx
.handle_urc(|urc| {
@@ -599,7 +531,7 @@ where
if let Some((_, mut sock)) =
sockets.iter_mut().find(|(handle, _)| *handle == socket)
{
- sock.closed_by_remote(ts);
+ sock.closed_by_remote();
}
}
Urc::SocketDataAvailable(
@@ -647,95 +579,3 @@ where
self.network.at_tx.handle_urc(f).map_err(Error::Network)
}
}
-
-#[cfg(test)]
-mod tests {
- use ublox_sockets::{SocketHandle, TcpSocket, UdpSocket};
-
- use super::*;
- use crate::test_helpers::{MockAtClient, MockTimer};
- use crate::{config::Config, services::data::ContextState, APNInfo};
-
- const SOCKET_SIZE: usize = 128;
- const SOCKET_SET_LEN: usize = 2;
- const TIMER_HZ: u32 = 1000;
-
- static mut SOCKET_SET: Option> = None;
-
- #[test]
- #[ignore]
- fn prune_on_initialize() {
- let client = MockAtClient::new(0);
- let timer = MockTimer::new(None);
- let config = Config::default();
-
- let socket_set: &'static mut _ = unsafe {
- SOCKET_SET = Some(SocketSet::new());
- SOCKET_SET.as_mut().unwrap_or_else(|| {
- panic!("Failed to get the static com_queue");
- })
- };
-
- let mut device = Device::<_, _, _, _, _, _, TIMER_HZ, SOCKET_SET_LEN, SOCKET_SIZE>::new(
- client, timer, config,
- );
- device.set_socket_storage(socket_set);
-
- // device.fsm.set_state(State::Connected);
- // assert_eq!(device.fsm.get_state(), State::Connected);
- device.state = State::FullyInitialized;
- device.power_state = PowerState::On;
- // assert_eq!(device.spin(), Ok(()));
-
- device.network.context_state = ContextState::Active;
-
- let mut data_service = device.data_service(&APNInfo::default()).unwrap();
-
- if let Some(ref mut sockets) = data_service.sockets {
- sockets
- .add(TcpSocket::new(0))
- .expect("Failed to add new tcp socket!");
- assert_eq!(sockets.len(), 1);
-
- let mut tcp = sockets
- .get::>(SocketHandle(0))
- .expect("Failed to get socket");
-
- assert_eq!(tcp.rx_window(), SOCKET_SIZE);
- let socket_data = b"This is socket data!!";
- tcp.rx_enqueue_slice(socket_data);
- assert_eq!(tcp.recv_queue(), socket_data.len());
- assert_eq!(tcp.rx_window(), SOCKET_SIZE - socket_data.len());
-
- sockets
- .add(UdpSocket::new(1))
- .expect("Failed to add new udp socket!");
- assert_eq!(sockets.len(), 2);
-
- assert!(sockets.add(UdpSocket::new(0)).is_err());
- } else {
- panic!()
- }
-
- drop(data_service);
-
- device.clear_buffers().expect("Failed to clear buffers");
-
- let mut data_service = device.data_service(&APNInfo::default()).unwrap();
- if let Some(ref mut sockets) = data_service.sockets {
- assert_eq!(sockets.len(), 0);
-
- sockets
- .add(TcpSocket::new(0))
- .expect("Failed to add new tcp socket!");
- assert_eq!(sockets.len(), 1);
-
- let tcp = sockets
- .get::>(SocketHandle(0))
- .expect("Failed to get socket");
- assert_eq!(tcp.recv_queue(), 0);
- } else {
- panic!()
- }
- }
-}
diff --git a/ublox-cellular/src/config.rs b/ublox-cellular/src/config.rs
index 7fb984e..727500b 100644
--- a/ublox-cellular/src/config.rs
+++ b/ublox-cellular/src/config.rs
@@ -1,5 +1,4 @@
use embedded_hal::digital::{ErrorType, InputPin, OutputPin};
-use heapless::String;
pub struct NoPin;
@@ -27,99 +26,15 @@ impl OutputPin for NoPin {
}
}
-#[derive(Debug)]
-pub struct Config {
- pub(crate) rst_pin: Option,
- pub(crate) dtr_pin: Option,
- pub(crate) pwr_pin: Option,
- pub(crate) vint_pin: Option,
- pub(crate) baud_rate: u32,
- pub(crate) hex_mode: bool,
- pub(crate) flow_control: bool,
- pub(crate) pin: String<4>,
-}
-
-impl Default for Config {
- fn default() -> Self {
- Self {
- rst_pin: None,
- dtr_pin: None,
- pwr_pin: None,
- vint_pin: None,
- baud_rate: 115_200_u32,
- hex_mode: true,
- flow_control: false,
- pin: String::new(),
- }
- }
-}
-
-impl Config
-where
- RST: OutputPin,
- PWR: OutputPin,
- DTR: OutputPin,
- VINT: InputPin,
-{
- #[must_use]
- pub fn new(pin: &str) -> Self {
- Self {
- rst_pin: None,
- dtr_pin: None,
- pwr_pin: None,
- vint_pin: None,
- baud_rate: 115_200_u32,
- hex_mode: true,
- flow_control: false,
- pin: String::from(pin),
- }
- }
+pub trait CellularConfig {
+ type ResetPin: OutputPin;
+ type PowerPin: OutputPin;
+ type VintPin: InputPin;
- pub fn with_rst(self, rst_pin: RST) -> Self {
- Self {
- rst_pin: Some(rst_pin),
- ..self
- }
- }
+ const FLOW_CONTROL: bool = false;
+ const HEX_MODE: bool = true;
- pub fn with_pwr(self, pwr_pin: PWR) -> Self {
- Self {
- pwr_pin: Some(pwr_pin),
- ..self
- }
- }
-
- pub fn with_dtr(self, dtr_pin: DTR) -> Self {
- Self {
- dtr_pin: Some(dtr_pin),
- ..self
- }
- }
-
- pub fn with_vint(self, vint_pin: VINT) -> Self {
- Self {
- vint_pin: Some(vint_pin),
- ..self
- }
- }
-
- pub fn baud_rate>(self, baud_rate: B) -> Self {
- // FIXME: Validate baudrates
-
- Self {
- baud_rate: baud_rate.into(),
- ..self
- }
- }
-
- pub fn with_flow_control(self) -> Self {
- Self {
- flow_control: true,
- ..self
- }
- }
-
- pub fn pin(&self) -> &str {
- &self.pin
- }
+ fn reset_pin(&mut self) -> Option<&mut Self::ResetPin>;
+ fn power_pin(&mut self) -> Option<&mut Self::PowerPin>;
+ fn vint_pin(&mut self) -> Option<&mut Self::VintPin>;
}
diff --git a/ublox-cellular/src/error.rs b/ublox-cellular/src/error.rs
index c4cb3ec..57e6999 100644
--- a/ublox-cellular/src/error.rs
+++ b/ublox-cellular/src/error.rs
@@ -50,10 +50,3 @@ impl From for Error {
}
}
}
-
-// `Clock` trait has associated `Error` type.
-// Therefore we cannot use `From` for error converion.
-// This is helper that can be used as `.map_err(from_clock)`
-pub fn from_clock(_error: E) -> Error {
- Error::Generic(GenericError::Clock)
-}
diff --git a/ublox-cellular/src/lib.rs b/ublox-cellular/src/lib.rs
index f36e370..bb2deda 100644
--- a/ublox-cellular/src/lib.rs
+++ b/ublox-cellular/src/lib.rs
@@ -78,6 +78,7 @@
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
+mod blocking_timer;
mod client;
pub mod command;
mod config;
@@ -89,21 +90,35 @@ mod registration;
mod services;
pub use atat::serde_bytes;
+use client::{URC_CAPACITY, URC_SUBSCRIBERS};
+use command::Urc;
pub use ublox_sockets as sockets;
-#[cfg(test)]
-mod test_helpers;
-
pub use client::Device as GsmClient;
-pub use config::{Config, NoPin};
+pub use config::NoPin;
pub use network::{ContextId, ProfileId};
pub use services::data::apn::{APNInfo, Apn};
pub use services::data::ssl::SecurityProfileId;
pub use services::data::DataService;
-// Re-export atat and fugit
+// Re-export atat
pub use atat;
-pub use fugit;
+
+pub type UbloxCellularBuffers =
+ atat::Buffers;
+
+pub type UbloxCellularIngress<'a, const INGRESS_BUF_SIZE: usize> = atat::Ingress<
+ 'a,
+ atat::DefaultDigester,
+ Urc,
+ INGRESS_BUF_SIZE,
+ URC_CAPACITY,
+ URC_SUBSCRIBERS,
+>;
+
+pub type UbloxCellularUrcChannel = atat::UrcChannel;
+
+pub use config::CellularConfig;
/// Prelude - Include traits
pub mod prelude {
diff --git a/ublox-cellular/src/module_timing.rs b/ublox-cellular/src/module_timing.rs
index 5f2f7dc..18d1fc7 100644
--- a/ublox-cellular/src/module_timing.rs
+++ b/ublox-cellular/src/module_timing.rs
@@ -1,47 +1,46 @@
#![allow(clippy::if_same_then_else)]
-use fugit::ExtU32;
-use fugit::TimerDurationU32;
+use embassy_time::Duration;
/// Low time of `PWR_ON` pin to trigger module switch on from power off mode
-pub fn pwr_on_time() -> TimerDurationU32 {
+pub fn pwr_on_time() -> Duration {
if cfg!(feature = "lara-r6") {
- 150.millis()
+ Duration::from_millis(150)
} else if cfg!(feature = "toby-r2") {
- 50.micros()
+ Duration::from_micros(50)
} else {
- 50.micros()
+ Duration::from_micros(50)
}
}
/// Low time of `PWR_ON` pin to trigger module graceful switch off
-pub fn pwr_off_time() -> TimerDurationU32 {
+pub fn pwr_off_time() -> Duration {
if cfg!(feature = "lara-r6") {
- 1500.millis()
+ Duration::from_millis(1500)
} else if cfg!(feature = "toby-r2") {
- 1.secs()
+ Duration::from_secs(1)
} else {
- 1.secs()
+ Duration::from_secs(1)
}
}
/// Low time of `RESET_N` pin to trigger module reset (reboot)
-pub fn reset_time() -> TimerDurationU32 {
+pub fn reset_time() -> Duration {
if cfg!(feature = "lara-r6") {
- 10.millis()
+ Duration::from_millis(10)
} else if cfg!(feature = "toby-r2") {
- 50.millis()
+ Duration::from_millis(50)
} else {
- 50.millis()
+ Duration::from_millis(50)
}
}
/// Low time of `RESET_N` pin to trigger module abrupt emergency switch off
///
/// NOTE: Not all modules support this operation from `RESET_N`
-pub fn kill_time() -> Option> {
+pub fn kill_time() -> Option {
if cfg!(feature = "lara-r6") {
- Some(10.secs())
+ Some(Duration::from_secs(10))
} else {
None
}
diff --git a/ublox-cellular/src/network.rs b/ublox-cellular/src/network.rs
index 28131fe..044dd02 100644
--- a/ublox-cellular/src/network.rs
+++ b/ublox-cellular/src/network.rs
@@ -1,4 +1,5 @@
use crate::{
+ client::{URC_CAPACITY, URC_SUBSCRIBERS},
command::{
general::GetCIMI,
mobile_control::{
@@ -15,17 +16,17 @@ use crate::{
Urc, AT,
},
error::GenericError,
- registration::{self, ConnectionState, RegistrationParams, RegistrationState},
+ registration::{self, ConnectionState, RegistrationState},
services::data::{ContextState, PROFILE_ID},
};
-use atat::{atat_derive::AtatLen, blocking::AtatClient};
-use fugit::{ExtU32, MinutesDurationU32, SecsDurationU32};
+use atat::{atat_derive::AtatLen, blocking::AtatClient, UrcSubscription};
+use embassy_time::{Duration, Instant};
use hash32_derive::Hash32;
use serde::{Deserialize, Serialize};
-const REGISTRATION_CHECK_INTERVAL: SecsDurationU32 = SecsDurationU32::secs(15);
-const REGISTRATION_TIMEOUT: MinutesDurationU32 = MinutesDurationU32::minutes(3);
-const CHECK_IMSI_TIMEOUT: MinutesDurationU32 = MinutesDurationU32::minutes(1);
+const REGISTRATION_CHECK_INTERVAL: Duration = Duration::from_secs(15);
+const REGISTRATION_TIMEOUT: Duration = Duration::from_secs(3 * 60);
+const CHECK_IMSI_TIMEOUT: Duration = Duration::from_secs(60);
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -46,19 +47,20 @@ pub struct ProfileId(pub u8);
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ContextId(pub u8);
-pub struct AtTx {
- urc_attempts: u8,
- max_urc_attempts: u8,
+pub struct AtTx<'sub, AtCl> {
consecutive_timeouts: u8,
- client: C,
+ urc_subscription: UrcSubscription<'sub, Urc, URC_CAPACITY, URC_SUBSCRIBERS>,
+ client: AtCl,
}
-impl AtTx {
- pub fn new(client: C, max_urc_attempts: u8) -> Self {
+impl<'sub, AtCl: AtatClient> AtTx<'sub, AtCl> {
+ pub fn new(
+ client: AtCl,
+ urc_subscription: UrcSubscription<'sub, Urc, URC_CAPACITY, URC_SUBSCRIBERS>,
+ ) -> Self {
Self {
- urc_attempts: 0,
consecutive_timeouts: 0,
- max_urc_attempts,
+ urc_subscription,
client,
}
}
@@ -74,7 +76,8 @@ impl AtTx {
.send_retry(req)
.map_err(|e| match e {
atat::Error::Timeout => {
- self.consecutive_timeouts += A::ATTEMPTS;
+ self.consecutive_timeouts =
+ self.consecutive_timeouts.saturating_add(A::ATTEMPTS);
Error::AT(atat::Error::Timeout)
}
atat::Error::Read => Error::AT(atat::Error::Read),
@@ -98,7 +101,8 @@ impl AtTx {
.send_retry(req)
.map_err(|e| match e {
atat::Error::Timeout => {
- self.consecutive_timeouts += A::ATTEMPTS;
+ self.consecutive_timeouts =
+ self.consecutive_timeouts.saturating_add(A::ATTEMPTS);
Error::AT(atat::Error::Timeout)
}
atat::Error::Read => Error::AT(atat::Error::Read),
@@ -115,41 +119,26 @@ impl AtTx {
}
pub fn handle_urc bool>(&mut self, f: F) -> Result<(), Error> {
- let mut a = self.urc_attempts;
- let max = self.max_urc_attempts;
-
- self.client.try_read_urc_with::(|urc, _| {
- if !f(urc) && a < max {
- a += 1;
- return false;
- // } else {
- // warn!("Dropping stale URC! {}", Debug2Format(&urc));
- }
- a = 0;
- true
- });
- self.urc_attempts = a;
+ if let Some(urc) = self.urc_subscription.try_next_message_pure() {
+ f(urc);
+ }
Ok(())
}
}
-pub struct Network
-where
- CLK: fugit_timer::Timer,
-{
- pub(crate) status: RegistrationState,
+pub struct Network<'sub, AtCl> {
+ pub(crate) status: RegistrationState,
pub(crate) context_state: ContextState,
- pub(crate) at_tx: AtTx,
+ pub(crate) at_tx: AtTx<'sub, AtCl>,
}
-impl Network
+impl<'sub, AtCl> Network<'sub, AtCl>
where
- C: AtatClient,
- CLK: fugit_timer::Timer,
+ AtCl: AtatClient,
{
- pub(crate) fn new(at_tx: AtTx, timer: CLK) -> Self {
+ pub(crate) fn new(at_tx: AtTx<'sub, AtCl>) -> Self {
Self {
- status: RegistrationState::new(timer),
+ status: RegistrationState::new(),
context_state: ContextState::Setup,
at_tx,
}
@@ -160,9 +149,7 @@ where
}
pub fn reset_reg_time(&mut self) -> Result<(), Error> {
- let now = self.status.timer.now();
-
- self.status.reg_start_time.replace(now);
+ self.status.reg_start_time.replace(Instant::now());
self.status.reg_check_time = self.status.reg_start_time;
Ok(())
}
@@ -179,7 +166,7 @@ where
self.intervene_registration()?;
self.check_running_imsi().ok(); // Ignore errors
- let now = self.status.timer.now();
+ let now = Instant::now();
let should_check = self
.status
.reg_check_time
@@ -197,7 +184,7 @@ where
self.update_registration()?;
- let now = self.status.timer.now();
+ let now = Instant::now();
let is_timeout = self
.status
.reg_start_time
@@ -219,7 +206,7 @@ where
// Check current IMSI if registered successfully in which case
// imsi_check_time will be `None`, else if not registered, check after
// CHECK_IMSI_TIMEOUT is expired
- let now = self.status.timer.now();
+ let now = Instant::now();
let check_imsi = self
.status
.imsi_check_time
@@ -275,10 +262,10 @@ where
return Ok(());
}
- let now = self.status.timer.now();
+ let now = Instant::now();
// If EPS has been sticky for longer than `timeout`
- let timeout: SecsDurationU32 = (self.status.registration_interventions * 15).secs();
+ let timeout = Duration::from_secs(self.status.registration_interventions as u64 * 15);
if self.status.eps.sticky() && self.status.eps.duration(now) >= timeout {
// If (EPS + CSD) is not attempting registration
if self.status.eps.get_status() == registration::Status::NotRegistering
@@ -292,7 +279,8 @@ where
self.status.csd.reset();
self.status.psd.reset();
self.status.eps.reset();
- self.status.registration_interventions += 1;
+ self.status.registration_interventions =
+ self.status.registration_interventions.saturating_add(1);
self.send_internal(
&SetOperatorSelection {
@@ -315,7 +303,8 @@ where
self.status.csd.reset();
self.status.psd.reset();
self.status.eps.reset();
- self.status.registration_interventions += 1;
+ self.status.registration_interventions =
+ self.status.registration_interventions.saturating_add(1);
self.send_internal(
&SetModuleFunctionality {
fun: Functionality::Minimum,
@@ -355,7 +344,8 @@ where
self.status.csd.reset();
self.status.psd.reset();
self.status.eps.reset();
- self.status.registration_interventions += 1;
+ self.status.registration_interventions =
+ self.status.registration_interventions.saturating_add(1);
self.send_internal(
&SetModuleFunctionality {
fun: Functionality::Minimum,
@@ -390,7 +380,8 @@ where
self.status.psd.duration(now)
);
self.status.psd.reset();
- self.status.registration_interventions += 1;
+ self.status.registration_interventions =
+ self.status.registration_interventions.saturating_add(1);
self.send_internal(&GetPDPContextState, true)?;
if self
@@ -421,20 +412,18 @@ where
}
pub fn update_registration(&mut self) -> Result<(), Error> {
- let ts = self.status.timer.now();
-
self.send_internal(&GetExtendedErrorReport, false).ok();
if let Ok(reg) = self.send_internal(&GetNetworkRegistrationStatus, false) {
- self.status.compare_and_set(reg.into(), ts);
+ self.status.compare_and_set(reg.into());
}
if let Ok(reg) = self.send_internal(&GetGPRSNetworkRegistrationStatus, false) {
- self.status.compare_and_set(reg.into(), ts);
+ self.status.compare_and_set(reg.into());
}
if let Ok(reg) = self.send_internal(&GetEPSNetworkRegistrationStatus, false) {
- self.status.compare_and_set(reg.into(), ts);
+ self.status.compare_and_set(reg.into());
}
Ok(())
@@ -443,7 +432,7 @@ where
pub(crate) fn handle_urc(&mut self) -> Result<(), Error> {
// TODO: How to do this cleaner?
let mut ctx_state = self.context_state;
- let mut new_reg_params: Option = None;
+ // let mut new_reg_params: Option = None;
self.at_tx.handle_urc(|urc| {
match urc {
@@ -470,6 +459,9 @@ where
}) => {
info!("[URC] ExtendedPSNetworkRegistration {:?}", state);
}
+ // FIXME: Currently `atat` is unable to distinguish `xREG` family of
+ // commands from URC's
+
// Urc::GPRSNetworkRegistration(reg_params) => {
// new_reg_params.replace(reg_params.into());
// }
@@ -506,10 +498,9 @@ where
true
})?;
- if let Some(reg_params) = new_reg_params {
- let ts = self.status.timer.now();
- self.status.compare_and_set(reg_params, ts)
- }
+ // if let Some(reg_params) = new_reg_params {
+ // self.status.compare_and_set(reg_params)
+ // }
self.context_state = ctx_state;
Ok(())
@@ -532,169 +523,3 @@ where
self.at_tx.send(req)
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::{
- registration::Status,
- test_helpers::{MockAtClient, MockTimer},
- };
- use fugit::{MillisDurationU32, TimerInstantU32};
- use fugit_timer::Timer;
-
- const TIMER_HZ: u32 = 1000;
-
- #[test]
- #[ignore]
- fn intervene_registration() {
- // Setup
- let tx = AtTx::new(MockAtClient::new(0), 5);
- let timer: MockTimer = MockTimer::new(Some(TimerInstantU32::from_ticks(25_234)));
- let mut network = Network::new(tx, timer);
- network.status.conn_state = ConnectionState::Connecting;
- // Update both started & updated
- network
- .status
- .eps
- .set_status(Status::NotRegistering, TimerInstantU32::from_ticks(1234));
- // Update only updated
- network
- .status
- .eps
- .set_status(Status::NotRegistering, TimerInstantU32::from_ticks(1534));
- network
- .status
- .csd
- .set_status(Status::NotRegistering, TimerInstantU32::from_ticks(1534));
-
- assert_eq!(
- network.status.eps.updated(),
- Some(TimerInstantU32::from_ticks(1534))
- );
- assert_eq!(
- network.status.eps.started(),
- Some(TimerInstantU32::from_ticks(1234))
- );
- assert!(network.status.eps.sticky());
-
- let ts = network.status.timer.now();
- assert_eq!(
- network.status.eps.duration(ts),
- MillisDurationU32::millis(24_000)
- );
-
- assert!(network.intervene_registration().is_ok());
-
- assert_eq!(network.status.registration_interventions, 2);
- }
-
- #[test]
- fn reset_reg_time() {
- let tx = AtTx::new(MockAtClient::new(0), 5);
- let timer: MockTimer = MockTimer::new(Some(TimerInstantU32::from_ticks(1234)));
- let mut network = Network::new(tx, timer);
-
- assert!(network.reset_reg_time().is_ok());
-
- assert_eq!(
- network.status.reg_start_time,
- Some(TimerInstantU32::from_ticks(1234))
- );
- assert_eq!(
- network.status.reg_check_time,
- Some(TimerInstantU32::from_ticks(1234))
- );
- }
-
- #[test]
- fn check_registration_state() {
- let tx = AtTx::new(MockAtClient::new(0), 5);
- let timer: MockTimer = MockTimer::new(Some(TimerInstantU32::from_ticks(1234)));
- let mut network = Network::new(tx, timer);
-
- // Check that `ConnectionState` will change from `Connected` to `Connecting`
- // with a state reset, if neither (csd + psd) || eps is actually registered
- network.status.conn_state = ConnectionState::Connected;
- network.status.registration_interventions = 3;
- network
- .status
- .csd
- .set_status(Status::Denied, TimerInstantU32::from_ticks(1));
- network
- .status
- .eps
- .set_status(Status::NotRegistering, TimerInstantU32::from_ticks(5));
-
- network.check_registration_state();
-
- assert_eq!(network.status.conn_state, ConnectionState::Connecting);
- assert_eq!(
- network.status.reg_start_time,
- Some(TimerInstantU32::from_ticks(1234))
- );
- assert_eq!(
- network.status.reg_check_time,
- Some(TimerInstantU32::from_ticks(1234))
- );
- assert_eq!(network.status.csd.get_status(), Status::None);
- assert_eq!(network.status.csd.updated(), None);
- assert_eq!(network.status.csd.started(), None);
- assert_eq!(network.status.psd.get_status(), Status::None);
- assert_eq!(network.status.psd.updated(), None);
- assert_eq!(network.status.psd.started(), None);
- assert_eq!(network.status.eps.get_status(), Status::None);
- assert_eq!(network.status.eps.updated(), None);
- assert_eq!(network.status.eps.started(), None);
-
- // Check that `ConnectionState` will change from `Connecting` to `Connected`
- // if eps is actually registered
- network
- .status
- .eps
- .set_status(Status::Roaming, TimerInstantU32::from_ticks(5));
-
- network.check_registration_state();
-
- assert_eq!(network.status.conn_state, ConnectionState::Connected);
-
- // Check that `ConnectionState` will change from `Connecting` to `Connected`
- // if (csd + psd) is actually registered
- network.status.conn_state = ConnectionState::Connecting;
- network.status.reset();
- network
- .status
- .eps
- .set_status(Status::Denied, TimerInstantU32::from_ticks(5));
- network
- .status
- .csd
- .set_status(Status::Roaming, TimerInstantU32::from_ticks(5));
- network
- .status
- .psd
- .set_status(Status::Home, TimerInstantU32::from_ticks(5));
-
- network.check_registration_state();
-
- assert_eq!(network.status.conn_state, ConnectionState::Connected);
- }
-
- #[test]
- fn unhandled_urcs() {
- let mut tx = AtTx::new(MockAtClient::new(0), 5);
-
- tx.handle_urc(|_| false).unwrap();
- assert_eq!(tx.client.n_urcs_dequeued, 0);
- tx.handle_urc(|_| false).unwrap();
- tx.handle_urc(|_| false).unwrap();
- tx.handle_urc(|_| false).unwrap();
- tx.handle_urc(|_| false).unwrap();
- tx.handle_urc(|_| false).unwrap();
- assert_eq!(tx.client.n_urcs_dequeued, 1);
- tx.handle_urc(|_| false).unwrap();
- tx.handle_urc(|_| true).unwrap();
- tx.handle_urc(|_| false).unwrap();
- assert_eq!(tx.client.n_urcs_dequeued, 2);
- }
-}
diff --git a/ublox-cellular/src/power.rs b/ublox-cellular/src/power.rs
index a308772..aa9355d 100644
--- a/ublox-cellular/src/power.rs
+++ b/ublox-cellular/src/power.rs
@@ -1,8 +1,9 @@
use atat::blocking::AtatClient;
+use embassy_time::{Duration, Instant};
use embedded_hal::digital::{InputPin, OutputPin};
-use fugit::{ExtU32, MillisDurationU32};
use crate::{
+ blocking_timer::BlockingTimer,
client::Device,
command::{
mobile_control::{
@@ -15,7 +16,8 @@ use crate::{
},
AT,
},
- error::{from_clock, Error, GenericError},
+ config::CellularConfig,
+ error::{Error, GenericError},
module_timing::{pwr_off_time, pwr_on_time, reset_time},
};
@@ -26,15 +28,11 @@ pub enum PowerState {
On,
}
-impl
- Device
+impl<'buf, 'sub, AtCl, AtUrcCh, Config, const N: usize, const L: usize>
+ Device<'buf, 'sub, AtCl, AtUrcCh, Config, N, L>
where
- C: AtatClient,
- CLK: fugit_timer::Timer,
- RST: OutputPin,
- PWR: OutputPin,
- DTR: OutputPin,
- VINT: InputPin,
+ AtCl: AtatClient,
+ Config: CellularConfig,
{
/// Check that the cellular module is alive.
///
@@ -98,8 +96,8 @@ where
false,
)?;
- self.wait_power_state(PowerState::On, 30_000.millis())
- .map_err(from_clock)?;
+ self.wait_power_state(PowerState::On, Duration::from_secs(30))
+ .map_err(|_| Error::Generic(GenericError::Timeout))?;
Ok(())
}
@@ -109,23 +107,14 @@ where
/// **NOTE** This function will reset NVM settings!
pub fn hard_reset(&mut self) -> Result<(), Error> {
trace!("Attempting to hard reset of the modem.");
- if let Some(ref mut rst) = self.config.rst_pin {
+ if let Some(rst) = self.config.reset_pin() {
rst.set_low().ok();
- self.network
- .status
- .timer
- .start(reset_time::())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
+ BlockingTimer::after(reset_time()).wait();
rst.set_high().ok();
- self.network
- .status
- .timer
- .start(5.secs())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
+
+ BlockingTimer::after(Duration::from_secs(5)).wait();
}
self.power_state = PowerState::Off;
@@ -138,32 +127,23 @@ where
pub fn power_on(&mut self) -> Result<(), Error> {
info!(
"Attempting to power on the modem with PWR_ON pin: {} and VInt pin: {}.",
- self.config.pwr_pin.is_some(),
- self.config.vint_pin.is_some(),
+ self.config.power_pin().is_some(),
+ self.config.vint_pin().is_some(),
);
if self.power_state()? != PowerState::On {
trace!("Powering modem on.");
- match self.config.pwr_pin {
+ match self.config.power_pin() {
// Apply Low pulse on PWR_ON for 50 microseconds to power on
- Some(ref mut pwr) => {
+ Some(pwr) => {
pwr.set_low().ok();
- self.network
- .status
- .timer
- .start(pwr_on_time::())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
+ BlockingTimer::after(pwr_on_time()).wait();
pwr.set_high().ok();
- self.network
- .status
- .timer
- .start(1.secs())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
-
- if let Err(e) = self.wait_power_state(PowerState::On, 10.secs()) {
+
+ BlockingTimer::after(Duration::from_secs(1)).wait();
+
+ if let Err(e) = self.wait_power_state(PowerState::On, Duration::from_secs(10)) {
error!("Failed to power on modem");
return Err(e);
} else {
@@ -191,12 +171,7 @@ where
self.power_state = PowerState::Off;
trace!("Modem powered off");
- self.network
- .status
- .timer
- .start(10.secs())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
+ BlockingTimer::after(Duration::from_secs(10)).wait();
Ok(())
}
@@ -205,16 +180,11 @@ where
trace!("Attempting to hard power off the modem.");
if self.power_state()? == PowerState::On {
- match self.config.pwr_pin {
- Some(ref mut pwr) => {
+ match self.config.power_pin() {
+ Some(pwr) => {
// Apply Low pulse on PWR_ON >= 1 second to power off
pwr.set_low().ok();
- self.network
- .status
- .timer
- .start(pwr_off_time::())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
+ BlockingTimer::after(pwr_off_time()).wait();
pwr.set_high().ok();
self.power_state = PowerState::Off;
@@ -232,8 +202,8 @@ where
/// Check the power state of the module, by probing `Vint` pin if available,
/// fallbacking to checking for AT responses through `is_alive`
pub fn power_state(&mut self) -> Result {
- match self.config.vint_pin {
- Some(ref mut vint) => {
+ match self.config.vint_pin() {
+ Some(vint) => {
if vint
.is_high()
.map_err(|_| Error::Generic(GenericError::Unsupported))?
@@ -248,21 +218,14 @@ where
}
/// Wait for the power state to change into `expected`, with a timeout
- fn wait_power_state(
- &mut self,
- expected: PowerState,
- timeout: MillisDurationU32,
- ) -> Result<(), Error> {
- let start = self.network.status.timer.now();
+ fn wait_power_state(&mut self, expected: PowerState, timeout: Duration) -> Result<(), Error> {
+ let start = Instant::now();
let mut res = false;
trace!("Waiting for the modem to reach {:?}.", expected);
- while self
- .network
- .status
- .timer
- .now()
+
+ while Instant::now()
.checked_duration_since(start)
.map_or(false, |dur| dur < timeout)
{
@@ -271,12 +234,7 @@ where
break;
}
- self.network
- .status
- .timer
- .start(5.millis())
- .map_err(from_clock)?;
- nb::block!(self.network.status.timer.wait()).map_err(from_clock)?;
+ BlockingTimer::after(Duration::from_millis(5)).wait();
}
if res {
diff --git a/ublox-cellular/src/registration.rs b/ublox-cellular/src/registration.rs
index 2a548aa..253c112 100644
--- a/ublox-cellular/src/registration.rs
+++ b/ublox-cellular/src/registration.rs
@@ -10,17 +10,17 @@ use crate::command::{
// urc::{EPSNetworkRegistration, GPRSNetworkRegistration},
},
};
-use fugit::{ExtU32, TimerInstantU32};
+use embassy_time::{Duration, Instant};
use heapless::String;
#[derive(Debug, Clone, Default)]
-pub struct CellularRegistrationStatus {
+pub struct CellularRegistrationStatus {
status: Status,
- updated: Option>,
- started: Option>,
+ updated: Option,
+ started: Option,
}
-impl CellularRegistrationStatus {
+impl CellularRegistrationStatus {
pub fn new() -> Self {
Self {
status: Status::default(),
@@ -29,17 +29,17 @@ impl CellularRegistrationStatus {
}
}
- pub fn duration(&self, ts: TimerInstantU32) -> fugit::TimerDurationU32 {
+ pub fn duration(&self, ts: Instant) -> Duration {
self.started
.and_then(|started| ts.checked_duration_since(started))
- .unwrap_or_else(|| 0.millis())
+ .unwrap_or_else(|| Duration::from_millis(0))
}
- pub fn started(&self) -> Option> {
+ pub fn started(&self) -> Option {
self.started
}
- pub fn updated(&self) -> Option> {
+ pub fn updated(&self) -> Option {
self.updated
}
@@ -53,7 +53,8 @@ impl CellularRegistrationStatus {
self.status
}
- pub fn set_status(&mut self, stat: Status, ts: TimerInstantU32) {
+ pub fn set_status(&mut self, stat: Status) {
+ let ts = Instant::now();
if self.status != stat {
self.status = stat;
self.started = Some(ts);
@@ -185,25 +186,20 @@ pub struct CellularGlobalIdentity {
}
#[derive(Debug, Clone)]
-pub struct RegistrationState
-where
- CLK: fugit_timer::Timer,
-{
- pub(crate) timer: CLK,
-
- pub(crate) reg_check_time: Option>,
- pub(crate) reg_start_time: Option>,
- pub(crate) imsi_check_time: Option>,
+pub struct RegistrationState {
+ pub(crate) reg_check_time: Option,
+ pub(crate) reg_start_time: Option,
+ pub(crate) imsi_check_time: Option,
pub(crate) conn_state: ConnectionState,
/// CSD (Circuit Switched Data) registration status (registered/searching/roaming etc.).
- pub(crate) csd: CellularRegistrationStatus,
+ pub(crate) csd: CellularRegistrationStatus,
/// PSD (Packet Switched Data) registration status (registered/searching/roaming etc.).
- pub(crate) psd: CellularRegistrationStatus,
+ pub(crate) psd: CellularRegistrationStatus,
/// EPS (Evolved Packet Switched) registration status (registered/searching/roaming etc.).
- pub(crate) eps: CellularRegistrationStatus,
+ pub(crate) eps: CellularRegistrationStatus,
- pub(crate) registration_interventions: u32,
+ pub(crate) registration_interventions: u8,
check_imsi: bool,
pub(crate) cgi: CellularGlobalIdentity,
@@ -225,13 +221,9 @@ impl Default for ConnectionState {
}
}
-impl RegistrationState
-where
- CLK: fugit_timer::Timer,
-{
- pub fn new(timer: CLK) -> Self {
+impl RegistrationState {
+ pub fn new() -> Self {
Self {
- timer,
reg_check_time: None,
reg_start_time: None,
imsi_check_time: None,
@@ -252,8 +244,8 @@ where
self.csd.reset();
self.psd.reset();
self.eps.reset();
- self.reg_start_time = Some(self.timer.now());
- self.reg_check_time = self.reg_start_time;
+ self.reg_start_time = Some(Instant::now());
+ self.reg_check_time = Some(Instant::now());
self.imsi_check_time = None;
self.registration_interventions = 1;
}
@@ -267,29 +259,25 @@ where
self.conn_state = state;
}
- pub fn compare_and_set(
- &mut self,
- new_params: RegistrationParams,
- ts: TimerInstantU32,
- ) {
+ pub fn compare_and_set(&mut self, new_params: RegistrationParams) {
match new_params.reg_type {
RegType::Creg => {
let prev_reg_status = self.csd.registered();
- self.csd.set_status(new_params.status, ts);
+ self.csd.set_status(new_params.status);
if !prev_reg_status && self.csd.registered() {
self.check_imsi = true
}
}
RegType::Cgreg => {
let prev_reg_status = self.psd.registered();
- self.psd.set_status(new_params.status, ts);
+ self.psd.set_status(new_params.status);
if !prev_reg_status && self.psd.registered() {
self.check_imsi = true
}
}
RegType::Cereg => {
let prev_reg_status = self.eps.registered();
- self.eps.set_status(new_params.status, ts);
+ self.eps.set_status(new_params.status);
if !prev_reg_status && self.eps.registered() {
self.check_imsi = true
}
diff --git a/ublox-cellular/src/services/data/apn.rs b/ublox-cellular/src/services/data/apn.rs
index 03478ff..9729778 100644
--- a/ublox-cellular/src/services/data/apn.rs
+++ b/ublox-cellular/src/services/data/apn.rs
@@ -1,29 +1,27 @@
-use heapless::String;
-
#[derive(Debug, Clone)]
-pub enum Apn {
- Given(String<99>),
+pub enum Apn<'a> {
+ Given(&'a str),
Automatic,
}
-impl Default for Apn {
+impl<'a> Default for Apn<'a> {
fn default() -> Self {
Self::Automatic
}
}
#[derive(Debug, Clone, Default)]
-pub struct APNInfo {
- pub apn: Apn,
- pub user_name: Option>,
- pub password: Option>,
+pub struct APNInfo<'a> {
+ pub apn: Apn<'a>,
+ pub user_name: Option<&'a str>,
+ pub password: Option<&'a str>,
}
-impl APNInfo {
+impl<'a> APNInfo<'a> {
#[must_use]
- pub fn new(apn: &str) -> Self {
+ pub fn new(apn: &'a str) -> Self {
Self {
- apn: Apn::Given(String::from(apn)),
+ apn: Apn::Given(apn),
user_name: None,
password: None,
}
diff --git a/ublox-cellular/src/services/data/dns.rs b/ublox-cellular/src/services/data/dns.rs
index 7395c28..57bcc28 100644
--- a/ublox-cellular/src/services/data/dns.rs
+++ b/ublox-cellular/src/services/data/dns.rs
@@ -8,11 +8,9 @@ use super::DataService;
use crate::command::dns::{self, types::ResolutionType};
use ublox_sockets::Error;
-impl<'a, C, CLK, const TIMER_HZ: u32, const N: usize, const L: usize> Dns
- for DataService<'a, C, CLK, TIMER_HZ, N, L>
+impl<'a, 'sub, AtCl, const N: usize, const L: usize> Dns for DataService<'a, 'sub, AtCl, N, L>
where
- C: AtatClient,
- CLK: fugit_timer::Timer,
+ AtCl: AtatClient,
{
type Error = Error;
diff --git a/ublox-cellular/src/services/data/mod.rs b/ublox-cellular/src/services/data/mod.rs
index 64986f9..efc1c47 100644
--- a/ublox-cellular/src/services/data/mod.rs
+++ b/ublox-cellular/src/services/data/mod.rs
@@ -12,6 +12,7 @@ mod udp_stack;
mod hex;
use crate::{
+ blocking_timer::BlockingTimer,
client::Device,
command::mobile_control::types::{Functionality, ResetMode},
command::mobile_control::SetModuleFunctionality,
@@ -26,15 +27,14 @@ use crate::{
},
psn::{self, responses::GPRSAttached, GetPDPContextState},
},
+ config::CellularConfig,
error::Error as DeviceError,
- error::GenericError,
network::{ContextId, Network},
ProfileId,
};
use apn::{APNInfo, Apn};
use atat::blocking::AtatClient;
-use embedded_hal::digital::{InputPin, OutputPin};
-use fugit::ExtU32;
+use embassy_time::Duration;
pub use error::Error;
use psn::{types::GPRSAttachedState, GetGPRSAttached};
@@ -53,15 +53,12 @@ pub const PROFILE_ID: ProfileId = ProfileId(1);
#[cfg(not(feature = "upsd-context-activation"))]
const CONTEXT_ID: ContextId = ContextId(1);
-impl
- Device
+impl<'buf, 'sub, AtCl, AtUrcCh, Config, const N: usize, const L: usize>
+ Device<'buf, 'sub, AtCl, AtUrcCh, Config, N, L>
where
- C: AtatClient,
- CLK: fugit_timer::Timer,
- RST: OutputPin,
- PWR: OutputPin,
- DTR: OutputPin,
- VINT: InputPin,
+ 'buf: 'sub,
+ AtCl: AtatClient,
+ Config: CellularConfig,
{
/// Define a PDP context
#[cfg(not(feature = "upsd-context-activation"))]
@@ -82,12 +79,12 @@ where
true,
)?;
- if let Apn::Given(apn) = apn_info.clone().apn {
+ if let Apn::Given(apn) = apn_info.apn {
self.network.send_internal(
&SetPDPContextDefinition {
cid,
pdp_type: "IP",
- apn: apn.as_str(),
+ apn,
},
true,
)?;
@@ -136,7 +133,7 @@ where
pub fn data_service<'a>(
&'a mut self,
apn_info: &APNInfo,
- ) -> nb::Result, DeviceError> {
+ ) -> nb::Result, DeviceError> {
// Spin [`Device`], handling [`Network`] related URC changes and
// propagting the FSM
match self.spin() {
@@ -173,25 +170,22 @@ pub enum ContextState {
Active,
}
-pub struct DataService<'a, C, CLK, const TIMER_HZ: u32, const N: usize, const L: usize>
+pub struct DataService<'a, 'sub, AtCl, const N: usize, const L: usize>
where
- C: atat::blocking::AtatClient,
- CLK: 'static + fugit_timer::Timer,
+ AtCl: AtatClient,
{
- network: &'a mut Network,
- pub(crate) sockets: Option<&'a mut SocketSet>,
+ network: &'a mut Network<'sub, AtCl>,
+ pub(crate) sockets: Option<&'a mut SocketSet>,
}
-impl<'a, C, CLK, const TIMER_HZ: u32, const N: usize, const L: usize>
- DataService<'a, C, CLK, TIMER_HZ, N, L>
+impl<'a, 'sub, AtCl, const N: usize, const L: usize> DataService<'a, 'sub, AtCl, N, L>
where
- C: atat::blocking::AtatClient,
- CLK: 'static + fugit_timer::Timer,
+ AtCl: AtatClient,
{
pub fn try_new(
apn_info: &APNInfo,
- network: &'a mut Network,
- sockets: Option<&'a mut SocketSet>,
+ network: &'a mut Network<'sub, AtCl>,
+ sockets: Option<&'a mut SocketSet>,
) -> nb::Result {
let mut data_service = Self { network, sockets };
@@ -248,14 +242,7 @@ where
return Ok(());
}
- self.network
- .status
- .timer
- .start(1.secs())
- .map_err(|_e| Error::Generic(GenericError::Clock))?;
-
- nb::block!(self.network.status.timer.wait())
- .map_err(|_e| Error::Generic(GenericError::Clock))?;
+ BlockingTimer::after(Duration::from_secs(1)).wait();
}
// self.network .send_internal( &SetGPRSAttached { state:
@@ -532,7 +519,7 @@ where
if available_data == 0 {
// Check for new socket data available at regular
// intervals, just in case a URC is missed
- if socket.should_update_available_data(network.status.timer.now()) {
+ if socket.should_update_available_data() {
match network.send_internal(
&ReadSocketData {
socket: handle,
@@ -541,7 +528,7 @@ where
false,
) {
Ok(SocketData { length, .. }) => socket.set_available_data(length),
- Err(_) => socket.closed_by_remote(network.status.timer.now()),
+ Err(_) => socket.closed_by_remote(),
}
}
diff --git a/ublox-cellular/src/services/data/ssl.rs b/ublox-cellular/src/services/data/ssl.rs
index 383f9ef..7ac13e8 100644
--- a/ublox-cellular/src/services/data/ssl.rs
+++ b/ublox-cellular/src/services/data/ssl.rs
@@ -3,7 +3,7 @@ use crate::command::device_data_security::{
types::{CertificateValidationLevel, SecurityDataType, SecurityProfileOperation},
PrepareSecurityDataImport, SecurityProfileManager, SendSecurityDataImport,
};
-use atat::atat_derive::AtatLen;
+use atat::{atat_derive::AtatLen, blocking::AtatClient};
use heapless::String;
use serde::{Deserialize, Serialize};
@@ -38,11 +38,9 @@ pub trait SSL {
) -> Result<(), Error>;
}
-impl<'a, C, CLK, const TIMER_HZ: u32, const N: usize, const L: usize> SSL
- for DataService<'a, C, CLK, TIMER_HZ, N, L>
+impl<'a, 'sub, AtCl, const N: usize, const L: usize> SSL for DataService<'a, 'sub, AtCl, N, L>
where
- C: atat::blocking::AtatClient,
- CLK: fugit_timer::Timer,
+ AtCl: AtatClient,
{
fn import_certificate(
&mut self,
diff --git a/ublox-cellular/src/services/data/tcp_stack.rs b/ublox-cellular/src/services/data/tcp_stack.rs
index 2b69325..476c8b8 100644
--- a/ublox-cellular/src/services/data/tcp_stack.rs
+++ b/ublox-cellular/src/services/data/tcp_stack.rs
@@ -6,14 +6,14 @@ use crate::command::ip_transport_layer::{
CloseSocket, ConnectSocket, CreateSocket, PrepareWriteSocketDataBinary, SetSocketSslState,
WriteSocketDataBinary,
};
+use atat::blocking::AtatClient;
use embedded_nal::{SocketAddr, TcpClientStack};
use ublox_sockets::{Error, SocketHandle, TcpSocket, TcpState};
-impl<'a, C, CLK, const TIMER_HZ: u32, const N: usize, const L: usize> TcpClientStack
- for DataService<'a, C, CLK, TIMER_HZ, N, L>
+impl<'a, 'sub, AtCl, const N: usize, const L: usize> TcpClientStack
+ for DataService<'a, 'sub, AtCl, N, L>
where
- C: atat::blocking::AtatClient,
- CLK: fugit_timer::Timer,
+ AtCl: AtatClient,
{
type Error = Error;
@@ -26,10 +26,9 @@ where
if let Some(ref mut sockets) = self.sockets {
// Check if there are any unused sockets available
if sockets.len() >= sockets.capacity() {
- let ts = self.network.status.timer.now();
// 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(ts) {
+ if !sockets.recycle() {
return Err(Error::SocketSetFull);
}
}
@@ -59,7 +58,7 @@ where
) -> nb::Result<(), Self::Error> {
if let Some(ref mut sockets) = self.sockets {
let mut tcp = sockets
- .get::>(*socket)
+ .get::>(*socket)
.map_err(Self::Error::from)?;
if matches!(tcp.state(), TcpState::Created) {
@@ -102,9 +101,7 @@ where
/// Check if this socket is still connected
fn is_connected(&mut self, socket: &Self::TcpSocket) -> Result {
if let Some(ref mut sockets) = self.sockets {
- Ok(sockets
- .get::>(*socket)?
- .is_connected())
+ Ok(sockets.get::>(*socket)?.is_connected())
} else {
Err(Error::Illegal)
}
@@ -164,7 +161,7 @@ where
) -> nb::Result {
if let Some(ref mut sockets) = self.sockets {
let mut tcp = sockets
- .get::>(*socket)
+ .get::>(*socket)
.map_err(Self::Error::from)?;
Ok(tcp.recv_slice(buffer).map_err(Self::Error::from)?)
diff --git a/ublox-cellular/src/services/data/udp_stack.rs b/ublox-cellular/src/services/data/udp_stack.rs
index c4a0324..82f0b46 100644
--- a/ublox-cellular/src/services/data/udp_stack.rs
+++ b/ublox-cellular/src/services/data/udp_stack.rs
@@ -4,14 +4,14 @@ use crate::command::ip_transport_layer::{
types::SocketProtocol, CloseSocket, CreateSocket, PrepareUDPSendToDataBinary,
UDPSendToDataBinary,
};
+use atat::blocking::AtatClient;
use embedded_nal::{SocketAddr, UdpClientStack};
use ublox_sockets::{Error, SocketHandle, UdpSocket};
-impl<'a, C, CLK, const TIMER_HZ: u32, const N: usize, const L: usize> UdpClientStack
- for DataService<'a, C, CLK, TIMER_HZ, N, L>
+impl<'a, 'sub, AtCl, const N: usize, const L: usize> UdpClientStack
+ for DataService<'a, 'sub, AtCl, N, L>
where
- C: atat::blocking::AtatClient,
- CLK: fugit_timer::Timer,
+ AtCl: AtatClient,
{
type Error = Error;
@@ -24,10 +24,9 @@ where
fn socket(&mut self) -> Result {
if let Some(ref mut sockets) = self.sockets {
if sockets.len() >= sockets.capacity() {
- let ts = self.network.status.timer.now();
// 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(ts) {
+ if !sockets.recycle() {
return Err(Error::SocketSetFull);
}
}
@@ -56,7 +55,7 @@ where
) -> Result<(), Self::Error> {
if let Some(ref mut sockets) = self.sockets {
let mut udp = sockets
- .get::>(*socket)
+ .get::>(*socket)
.map_err(Self::Error::from)?;
udp.bind(remote).map_err(Self::Error::from)?;
Ok(())
@@ -69,7 +68,7 @@ where
fn send(&mut self, socket: &mut Self::UdpSocket, buffer: &[u8]) -> nb::Result<(), Self::Error> {
if let Some(ref mut sockets) = self.sockets {
let udp = sockets
- .get::>(*socket)
+ .get::>(*socket)
.map_err(Self::Error::from)?;
if !udp.is_open() {
@@ -125,7 +124,7 @@ where
) -> nb::Result<(usize, SocketAddr), Self::Error> {
if let Some(ref mut sockets) = self.sockets {
let mut udp = sockets
- .get::>(*socket)
+ .get::>(*socket)
.map_err(Self::Error::from)?;
let bytes = udp.recv_slice(buffer).map_err(Self::Error::from)?;
diff --git a/ublox-cellular/src/services/location/error.rs b/ublox-cellular/src/services/location/error.rs
deleted file mode 100644
index e69de29..0000000
diff --git a/ublox-cellular/src/services/location/mod.rs b/ublox-cellular/src/services/location/mod.rs
deleted file mode 100644
index 28368b8..0000000
--- a/ublox-cellular/src/services/location/mod.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use core::convert::TryInto;
-
-use crate::services::data::socket::Socket;
-use crate::{client::Device, error::Error as DeviceError};
-use atat::blocking::AtatClient;
-use embedded_hal::digital::{InputPin, OutputPin};
-use embedded_time::{Clock, duration::{Generic, Milliseconds}};
-use heapless::{ArrayLength, Bucket, Pos};
-
-impl Device
-where
- C: AtatClient,
- CLK: fugit_timer::Timer,
- RST: OutputPin,
- PWR: OutputPin,
- DTR: OutputPin,
- VINT: InputPin,
- N: ArrayLength