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/lint.yml b/.github/workflows/ci.yml
similarity index 67%
rename from .github/workflows/lint.yml
rename to .github/workflows/ci.yml
index 338dbda..ad619a5 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/ci.yml
@@ -1,4 +1,4 @@
-name: Lint
+name: CI
on:
push:
@@ -10,8 +10,8 @@ defaults:
run:
shell: bash
-env:
- CLIPPY_PARAMS: -W clippy::all -W clippy::pedantic -W clippy::nursery -W clippy::cargo
+# env:
+# CLIPPY_PARAMS: -W clippy::all -W clippy::pedantic -W clippy::nursery -W clippy::cargo
jobs:
rustfmt:
@@ -81,4 +81,31 @@ jobs:
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- args: -- ${{ env.CLIPPY_PARAMS }}
+ args: --features odin-w2xx,ppp -- ${{ env.CLIPPY_PARAMS }}
+
+ 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 --target thumbv7m-none-eabi --features odin-w2xx,ppp
+
+ - name: Test
+ uses: actions-rs/cargo@v1
+ with:
+ command: test
+ args: --lib --features odin-w2xx,ppp
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
deleted file mode 100644
index c9b0dba..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
-
- # - 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/grcov.yml b/.github/workflows/grcov.yml
deleted file mode 100644
index af13453..0000000
--- a/.github/workflows/grcov.yml
+++ /dev/null
@@ -1,78 +0,0 @@
-# name: Coverage
-
-# on:
-# push:
-# branches:
-# - master
-# pull_request:
-
-# jobs:
-# grcov:
-# name: Coverage
-# 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
-# target: thumbv7m-none-eabi
-# override: true
-
-# - name: Install grcov
-# uses: actions-rs/cargo@v1
-# # uses: actions-rs/install@v0.1
-# with:
-# # crate: grcov
-# # version: latest
-# # use-tool-cache: true
-# command: install
-# args: grcov --git https://github.com/mozilla/grcov
-
-# - name: Test
-# uses: actions-rs/cargo@v1
-# with:
-# command: test
-# args: --lib --no-fail-fast
-# env:
-# CARGO_INCREMENTAL: "0"
-# RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=unwind -Zpanic_abort_tests"
-# RUSTDOCFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=unwind -Zpanic_abort_tests"
-
-# - name: Generate coverage data
-# id: grcov
-# # uses: actions-rs/grcov@v0.1
-# run: |
-# grcov target/debug/ \
-# --branch \
-# --llvm \
-# --source-dir . \
-# --output-file lcov.info \
-# --ignore='/**' \
-# --ignore='C:/**' \
-# --ignore='../**' \
-# --ignore-not-existing \
-# --excl-line "#\\[derive\\(" \
-# --excl-br-line "(#\\[derive\\()|(debug_assert)" \
-# --excl-start "#\\[cfg\\(test\\)\\]" \
-# --excl-br-start "#\\[cfg\\(test\\)\\]" \
-# --commit-sha ${{ github.sha }} \
-# --service-job-id ${{ github.job }} \
-# --service-name "GitHub Actions" \
-# --service-number ${{ github.run_id }}
-# - name: Upload coverage as artifact
-# uses: actions/upload-artifact@v2
-# with:
-# name: lcov.info
-# # path: ${{ steps.grcov.outputs.report }}
-# path: lcov.info
-
-# - name: Upload coverage to codecov.io
-# uses: codecov/codecov-action@v1
-# with:
-# # file: ${{ steps.grcov.outputs.report }}
-# file: lcov.info
-# fail_ci_if_error: true
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 9884357..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 --target thumbv7m-none-eabi
-
- - name: Test
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --lib
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 1b2bf72..8fc7548 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -7,7 +7,7 @@
"rust-analyzer.check.allTargets": false,
"rust-analyzer.linkedProjects": [],
"rust-analyzer.cargo.features": [
- "odin_w2xx",
+ "odin-w2xx",
// "internal-network-stack"
"ppp"
],
diff --git a/Cargo.toml b/Cargo.toml
index 5d31af1..650b27a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,7 @@ atat = { version = "0.23", features = ["derive", "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 = { version = "0.5", optional = true }
ublox-sockets = { git = "https://github.com/BlackbirdHQ/ublox-sockets", rev = "9f7fe54", optional = true }
portable-atomic = "1.6"
@@ -50,6 +50,8 @@ default = ["socket-tcp", "socket-udp"]
internal-network-stack = ["dep:ublox-sockets", "edm"]
edm = ["ublox-sockets?/edm"]
+ipv6 = ["embassy-net?/proto-ipv6"]
+
# 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"]
@@ -66,12 +68,13 @@ defmt = [
]
log = ["dep:log", "ublox-sockets?/log", "atat/log"]
-odin_w2xx = []
-nina_w1xx = []
-nina_b1xx = []
-anna_b1xx = []
-nina_b2xx = []
-nina_b3xx = []
+# Supported Ublox modules
+odin-w2xx = []
+nina-w1xx = []
+nina-b1xx = []
+anna-b1xx = []
+nina-b2xx = []
+nina-b3xx = []
[workspace]
members = []
@@ -81,4 +84,5 @@ 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 = { git = "https://github.com/BlackbirdHQ/atat", rev = "a466836" }
+# atat = { path = "../atat/atat" }
\ No newline at end of file
diff --git a/README.md b/README.md
index 4010a77..402312c 100644
--- a/README.md
+++ b/README.md
@@ -13,12 +13,12 @@
A driver crate for AT-command based serial ublox short range modules, built on top of [atat].
The driver aims to be compatible with the ublox short range modules:
-- odin_w2xx
-- nina_w1xx
-- nina_b1xx
-- anna_b1xx
-- nina_b2xx
-- nina_b3xx
+- odin-w2xx
+- nina-w1xx
+- nina-b1xx
+- anna-b1xx
+- nina-b2xx
+- nina-b3xx
[atat]: https://crates.io/crates/atat
@@ -48,12 +48,12 @@ The samples can be built using `cargo build -p linux_example --target x86_64-unk
## Features
- device selection (must select one, and only one!):
- - `odin_w2xx`
- - `nina_w1xx`
- - `nina_b1xx`
- - `anna_b1xx`
- - `nina_b2xx`
- - `nina_b3xx`
+ - `odin-w2xx`
+ - `nina-w1xx`
+ - `nina-b1xx`
+ - `anna-b1xx`
+ - `nina-b2xx`
+ - `nina-b3xx`
- `socket-tcp`: Enabled by default. Adds TCP socket capabilities, and implements [`TcpStack`] trait.
- `socket-udp`: Enabled by default. Adds UDP socket capabilities, and implements [`UdpStack`] trait.
- `defmt-default`: Disabled by default. Add log statements on trace (dev) or info (release) log levels to aid debugging.
diff --git a/examples/rpi-pico/Cargo.toml b/examples/rpi-pico/Cargo.toml
index 4a9d2a1..3a26943 100644
--- a/examples/rpi-pico/Cargo.toml
+++ b/examples/rpi-pico/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
-ublox-short-range-rs = { path = "../../", features = ["odin_w2xx", "defmt"] }
+ublox-short-range-rs = { path = "../../", features = ["odin-w2xx", "defmt"] }
embassy-executor = { version = "0.5", features = [
"defmt",
"integrated-timers",
diff --git a/src/asynch/at_udp_socket.rs b/src/asynch/at_udp_socket.rs
index 5b3f87f..4428181 100644
--- a/src/asynch/at_udp_socket.rs
+++ b/src/asynch/at_udp_socket.rs
@@ -1,6 +1,8 @@
use embassy_net::{udp::UdpSocket, Ipv4Address};
use embedded_io_async::{Read, Write};
+use crate::config::Transport;
+
pub struct AtUdpSocket<'a>(pub(crate) UdpSocket<'a>);
impl<'a> AtUdpSocket<'a> {
@@ -32,6 +34,16 @@ impl<'a> Write for &AtUdpSocket<'a> {
}
}
+impl<'a> Transport for AtUdpSocket<'a> {
+ fn set_baudrate(&mut self, _baudrate: u32) {
+ // Nothing to do here
+ }
+
+ fn split_ref(&mut self) -> (impl Write, impl Read) {
+ (&*self, &*self)
+ }
+}
+
impl<'a> embedded_io_async::ErrorType for AtUdpSocket<'a> {
type Error = core::convert::Infallible;
}
diff --git a/src/asynch/control.rs b/src/asynch/control.rs
index ed4206d..8f03d79 100644
--- a/src/asynch/control.rs
+++ b/src/asynch/control.rs
@@ -1,13 +1,26 @@
use core::cell::Cell;
+use core::str::FromStr as _;
+use atat::AtatCmd;
use atat::{asynch::AtatClient, response_slot::ResponseSlotGuard, UrcChannel};
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Sender};
use embassy_time::{with_timeout, Duration, Timer};
use heapless::Vec;
+use no_std_net::Ipv4Addr;
+use crate::command::general::responses::SoftwareVersionResponse;
+use crate::command::general::types::FirmwareVersion;
+use crate::command::general::SoftwareVersion;
use crate::command::gpio::responses::ReadGPIOResponse;
use crate::command::gpio::types::GPIOMode;
use crate::command::gpio::ConfigureGPIO;
+use crate::command::network::responses::NetworkStatusResponse;
+use crate::command::network::types::{NetworkStatus, NetworkStatusParameter};
+use crate::command::network::GetNetworkStatus;
+use crate::command::ping::Ping;
+use crate::command::system::responses::LocalAddressResponse;
+use crate::command::system::types::InterfaceID;
+use crate::command::system::GetLocalAddress;
use crate::command::wifi::{ExecWifiStationAction, GetWifiStatus, SetWifiStationConfig};
use crate::command::OnOff;
use crate::command::{
@@ -32,13 +45,19 @@ use crate::command::{
system::{RebootDCE, ResetToFactoryDefaults},
wifi::types::AccessPointId,
};
-use crate::connection::WiFiState;
+use crate::connection::{DnsServers, StaticConfigV4, WiFiState};
use crate::error::Error;
use super::runner::{MAX_CMD_LEN, URC_SUBSCRIBERS};
use super::state::LinkState;
use super::{state, UbloxUrc};
+enum WifiAuthentication<'a> {
+ None,
+ Wpa2Passphrase(&'a str),
+ Wpa2Psk(&'a [u8; 32]),
+}
+
const CONFIG_ID: u8 = 0;
pub(crate) struct ProxyClient<'a, const INGRESS_BUF_SIZE: usize> {
@@ -90,9 +109,12 @@ impl<'a, const INGRESS_BUF_SIZE: usize> atat::asynch::AtatClient
}
// TODO: Guard against race condition!
- self.req_sender
- .send(Vec::try_from(&buf[..len]).unwrap())
- .await;
+ with_timeout(
+ Duration::from_secs(1),
+ self.req_sender.send(Vec::try_from(&buf[..len]).unwrap()),
+ )
+ .await
+ .map_err(|_| atat::Error::Timeout)?;
self.cooldown_timer.set(Some(Timer::after_millis(20)));
@@ -111,7 +133,7 @@ impl<'a, const INGRESS_BUF_SIZE: usize> atat::asynch::AtatClient
pub struct Control<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize> {
state_ch: state::Runner<'a>,
at_client: ProxyClient<'a, INGRESS_BUF_SIZE>,
- _urc_channel: &'a UrcChannel,
+ urc_channel: &'a UrcChannel,
}
impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>
@@ -119,21 +141,22 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>
{
pub(crate) fn new(
state_ch: state::Runner<'a>,
- urc_channel: &'a UrcChannel,
+ urc_channel: &'a UrcChannel,
req_sender: Sender<'a, NoopRawMutex, Vec, 1>,
res_slot: &'a atat::ResponseSlot,
) -> Self {
Self {
state_ch,
at_client: ProxyClient::new(req_sender, res_slot),
- _urc_channel: urc_channel,
+ urc_channel: urc_channel,
}
}
+ /// Set the hostname of the device
pub async fn set_hostname(&self, hostname: &str) -> Result<(), Error> {
self.state_ch.wait_for_initialized().await;
- (&(&self).at_client)
+ (&self.at_client)
.send_retry(&SetNetworkHostName {
host_name: hostname,
})
@@ -141,6 +164,28 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>
Ok(())
}
+ /// Gets the firmware version of the device
+ pub async fn get_version(&self) -> Result {
+ self.state_ch.wait_for_initialized().await;
+
+ let SoftwareVersionResponse { version } =
+ (&self.at_client).send_retry(&SoftwareVersion).await?;
+ Ok(version)
+ }
+
+ /// Gets the MAC address of the device
+ pub async fn hardware_address(&mut self) -> Result<[u8; 6], Error> {
+ self.state_ch.wait_for_initialized().await;
+
+ let LocalAddressResponse { mac } = (&self.at_client)
+ .send_retry(&GetLocalAddress {
+ interface_id: InterfaceID::WiFi,
+ })
+ .await?;
+
+ Ok(mac.to_be_bytes()[2..].try_into().unwrap())
+ }
+
async fn get_wifi_status(&self) -> Result {
match (&self.at_client)
.send_retry(&GetWifiStatus {
@@ -154,7 +199,91 @@ impl<'a, const INGRESS_BUF_SIZE: usize, const URC_CAPACITY: usize>
}
}
- async fn get_connected_ssid(&self) -> Result, Error> {
+ pub async fn wait_for_link_state(&self, link_state: LinkState) {
+ self.state_ch.wait_for_link_state(link_state).await
+ }
+
+ pub async fn config_v4(&self) -> Result