From dc427bdb5ca108b8447f46e9646cbab93d5a4964 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 5 Oct 2024 15:07:39 +0000 Subject: [PATCH] Concurrent commissioning --- Cargo.toml | 6 +++--- examples/light.rs | 27 ++++++++++++++----------- examples/light_eth.rs | 18 ++++++++--------- src/ble.rs | 4 ++-- src/netif.rs | 4 ++++ src/wireless.rs | 46 ++++++++++++++++++++++++++++++++++--------- 6 files changed, 70 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d5f1985..a80bbc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ rust-version = "1.78" #[patch.'https://github.com/ivmarkov/async-io-mini'] #async-io-mini = { path = "../async-io-mini" } -[patch.'https://github.com/ivmarkov/rs-matter-stack'] -rs-matter-stack = { path = "../rs-matter-stack" } +#[patch.'https://github.com/ivmarkov/rs-matter-stack'] +#rs-matter-stack = { path = "../rs-matter-stack" } [patch.crates-io] rs-matter = { git = "https://github.com/ivmarkov/rs-matter" } @@ -55,7 +55,7 @@ esp-idf-svc = { version = "0.49.1", default-features = false, features = ["alloc embedded-svc = { version = "0.28", default-features = false } rs-matter = { version = "0.1", default-features = false, features = ["rustcrypto"] } async-io = { version = "=2.0.0", default-features = false } # Workaround for https://github.com/smol-rs/async-lock/issues/84 -rs-matter-stack = { git = "https://github.com/ivmarkov/rs-matter-stack", branch = "in-place-init", default-features = false, features = ["std"], optional = true } +rs-matter-stack = { git = "https://github.com/ivmarkov/rs-matter-stack", branch = "thread", default-features = false, features = ["std"], optional = true } edge-nal = "0.3" edge-nal-std = { version = "0.3", default-features = false, optional = true } diff --git a/examples/light.rs b/examples/light.rs index 6b7e911..e707e42 100644 --- a/examples/light.rs +++ b/examples/light.rs @@ -1,7 +1,12 @@ -//! An example utilizing the `EspWifiBleMatterStack` struct. +//! An example utilizing the `EspNCWifiMatterStack` struct. +//! //! As the name suggests, this Matter stack assembly uses Wifi as the main transport, -//! and BLE for commissioning. +//! (and thus BLE for commissioning), where `NC` stands for non-concurrent commisisoning +//! (i.e., the stack will not run the BLE and Wifi radio simultaneously, which saves memory). +//! //! If you want to use Ethernet, utilize `EspEthMatterStack` instead. +//! If you want to use concurrent commissioning, utilize `EspWifiMatterStack` instead +//! (Alexa does not work (yet) with non-concurrent commissioning). //! //! The example implements a fictitious Light device (an On-Off Matter cluster). @@ -10,7 +15,7 @@ use core::pin::pin; use embassy_futures::select::select; use embassy_time::{Duration, Timer}; -use esp_idf_matter::{init_async_io, EspKvBlobStore, EspMatterBle, EspPersist, EspWifiMatterStack}; +use esp_idf_matter::{init_async_io, EspMatterBle, EspMatterWifi, EspWifiMatterStack}; use esp_idf_svc::eventloop::EspSystemEventLoop; use esp_idf_svc::hal::peripherals::Peripherals; @@ -26,11 +31,10 @@ use rs_matter::data_model::cluster_on_off; use rs_matter::data_model::device_types::DEV_TYPE_ON_OFF_LIGHT; use rs_matter::data_model::objects::{Dataver, Endpoint, HandlerCompat, Node}; use rs_matter::data_model::system_model::descriptor; -use rs_matter::secure_channel::spake2p::VerifierData; use rs_matter::utils::init::InitMaybeUninit; use rs_matter::utils::select::Coalesce; -use rs_matter::CommissioningData; +use rs_matter::BasicCommData; use rs_matter_stack::persist::DummyPersist; use static_cell::StaticCell; @@ -128,20 +132,21 @@ async fn matter() -> Result<(), anyhow::Error> { ))), ); + let (mut wifi_modem, mut bt_modem) = peripherals.modem.split(); + // Run the Matter stack with our handler // Using `pin!` is completely optional, but saves some memory due to `rustc` // not being very intelligent w.r.t. stack usage in async functions let mut matter = pin!(stack.run( + // The Matter stack needs the Wifi modem peripheral + EspMatterWifi::new(&mut wifi_modem, sysloop, timers, nvs.clone()), + // The Matter stack needs the BT modem peripheral + EspMatterBle::new(&mut bt_modem, nvs, stack), // The Matter stack needs a persister to store its state // `EspPersist`+`EspKvBlobStore` saves to a user-supplied NVS partition // under namespace `esp-idf-matter` DummyPersist, //EspPersist::new_wifi_ble(EspKvBlobStore::new_default(nvs.clone())?, stack), - // The Matter stack needs the BT/Wifi modem peripheral - and in general - - // the Bluetooth / Wifi connections will be managed by the Matter stack itself - // For finer-grained control, call `MatterStack::is_commissioned`, - // `MatterStack::commission` and `MatterStack::operate` - EspMatterBle::new(peripherals.modem, sysloop, timers, nvs, stack), // Our `AsyncHandler` + `AsyncMetadata` impl (NODE, handler), // No user future to run @@ -193,7 +198,7 @@ const NODE: Node = Node { EspWifiMatterStack::<()>::root_metadata(), Endpoint { id: LIGHT_ENDPOINT_ID, - device_type: DEV_TYPE_ON_OFF_LIGHT, + device_types: &[DEV_TYPE_ON_OFF_LIGHT], clusters: &[descriptor::CLUSTER, cluster_on_off::CLUSTER], }, ], diff --git a/examples/light_eth.rs b/examples/light_eth.rs index b6d00ce..b347f79 100644 --- a/examples/light_eth.rs +++ b/examples/light_eth.rs @@ -12,9 +12,8 @@ use core::pin::pin; use embassy_futures::select::select; use embassy_time::{Duration, Timer}; -use esp_idf_matter::{ - init_async_io, EspEthMatterStack, EspKvBlobStore, EspMatterNetif, EspPersist, -}; +use esp_idf_matter::netif::EspMatterNetif; +use esp_idf_matter::{init_async_io, EspEthMatterStack}; use esp_idf_svc::eventloop::EspSystemEventLoop; use esp_idf_svc::hal::peripherals::Peripherals; @@ -33,11 +32,10 @@ use rs_matter::data_model::cluster_on_off; use rs_matter::data_model::device_types::DEV_TYPE_ON_OFF_LIGHT; use rs_matter::data_model::objects::{Dataver, Endpoint, HandlerCompat, Node}; use rs_matter::data_model::system_model::descriptor; -use rs_matter::secure_channel::spake2p::VerifierData; use rs_matter::utils::init::InitMaybeUninit; use rs_matter::utils::select::Coalesce; -use rs_matter::CommissioningData; +use rs_matter::BasicCommData; use rs_matter_stack::persist::DummyPersist; use static_cell::StaticCell; @@ -158,15 +156,15 @@ async fn matter() -> Result<(), anyhow::Error> { // Using `pin!` is completely optional, but saves some memory due to `rustc` // not being very intelligent w.r.t. stack usage in async functions let mut matter = pin!(stack.run( + // The Matter stack need access to the netif on which we'll operate + // Since we are pretending to use a wired Ethernet connection - yet - + // we are using a Wifi STA, provide the Wifi netif here + EspMatterNetif::new(wifi.wifi().sta_netif(), sysloop), // The Matter stack needs a persister to store its state // `EspPersist`+`EspKvBlobStore` saves to a user-supplied NVS partition // under namespace `esp-idf-matter` DummyPersist, //EspPersist::new_eth(EspKvBlobStore::new_default(nvs.clone())?, stack), - // The Matter stack need access to the netif on which we'll operate - // Since we are pretending to use a wired Ethernet connection - yet - - // we are using a Wifi STA, provide the Wifi netif here - EspMatterNetif::new(wifi.wifi().sta_netif(), sysloop), // Our `AsyncHandler` + `AsyncMetadata` impl (NODE, handler), // No user future to run @@ -217,7 +215,7 @@ const NODE: Node = Node { EspEthMatterStack::<()>::root_metadata(), Endpoint { id: LIGHT_ENDPOINT_ID, - device_type: DEV_TYPE_ON_OFF_LIGHT, + device_types: &[DEV_TYPE_ON_OFF_LIGHT], clusters: &[descriptor::CLUSTER, cluster_on_off::CLUSTER], }, ], diff --git a/src/ble.rs b/src/ble.rs index 05099c3..bfd6a44 100644 --- a/src/ble.rs +++ b/src/ble.rs @@ -98,7 +98,7 @@ impl IndBuffer { } } -/// The `'static` state of the `BtpGattPeripheral` struct. +/// The `'static` state of the `EspBtpGattPeripheral` struct. /// Isolated as a separate struct to allow for `const fn` construction /// and static allocation. pub struct EspBtpGattContext { @@ -165,7 +165,7 @@ impl Default for EspBtpGattContext { } } -/// A GATT peripheral implementation for the BTP protocol in `rs-matter`. +/// A GATT peripheral implementation for the BTP protocol in `rs-matter` via ESP-IDF. /// Implements the `GattPeripheral` trait. pub struct EspBtpGattPeripheral<'a, 'd, M> where diff --git a/src/netif.rs b/src/netif.rs index 113fca8..92aeb87 100644 --- a/src/netif.rs +++ b/src/netif.rs @@ -24,6 +24,7 @@ use rs_matter_stack::netif::{Netif, NetifConf}; const TIMEOUT_PERIOD_SECS: u8 = 5; +/// A `Netif` and `UdpBind` traits implementation via ESP-IDF pub struct EspMatterNetif { netif: T, sysloop: EspSystemEventLoop, @@ -33,6 +34,7 @@ impl EspMatterNetif where T: Borrow, { + /// Create a new `EspMatterNetif` instance pub const fn new(netif: T, sysloop: EspSystemEventLoop) -> Self { Self { netif, sysloop } } @@ -45,6 +47,7 @@ where Self::wait_any_conf_change(&self.sysloop).await } + /// Get the network interface configuration pub fn get_netif_conf(netif: &EspNetif) -> Result { let ip_info = netif.get_ip_info()?; @@ -89,6 +92,7 @@ where }) } + /// Wait for any IP configuration change pub async fn wait_any_conf_change(sysloop: &EspSystemEventLoop) -> Result<(), EspError> { let notification = Arc::new(Notification::::new()); diff --git a/src/wireless.rs b/src/wireless.rs index 02f9b82..0ee2f56 100644 --- a/src/wireless.rs +++ b/src/wireless.rs @@ -7,7 +7,7 @@ use edge_nal_std::{Stack, UdpSocket}; use embassy_sync::mutex::Mutex; -use embedded_svc::wifi::asynch::Wifi; +use embedded_svc::wifi::asynch::Wifi as WifiSvc; use enumset::EnumSet; @@ -31,7 +31,7 @@ use rs_matter_stack::network::{Embedding, Network}; use rs_matter_stack::persist::KvBlobBuf; use rs_matter_stack::wireless::svc::SvcWifiController; use rs_matter_stack::wireless::traits::{ - Ble, ThreadCredentials, WifiCredentials, WifiData, Wireless, WirelessConfig, WirelessData, + Ble, Thread, Wifi, WifiData, Wireless, WirelessConfig, WirelessData, NC, }; use rs_matter_stack::{MatterStack, WirelessBle}; @@ -39,18 +39,45 @@ use crate::ble::{EspBtpGattContext, EspBtpGattPeripheral}; use super::netif::EspMatterNetif; -pub type EspWifiMatterStack<'a, E> = EspWirelessMatterStack<'a, WifiCredentials, E>; -pub type EspThreadMatterStack<'a, E> = EspWirelessMatterStack<'a, ThreadCredentials, E>; +/// A type alias for an ESP-IDF Matter stack running over Wifi (and BLE, during commissioning). +pub type EspWifiMatterStack<'a, E> = EspWirelessMatterStack<'a, Wifi, E>; +/// A type alias for an ESP-IDF Matter stack running over Thread (and BLE, during commissioning). +pub type EspThreadMatterStack<'a, E> = EspWirelessMatterStack<'a, Thread, E>; + +/// A type alias for an ESP-IDF Matter stack running over Wifi (and BLE, during commissioning). +/// +/// Unlike `EspWifiMatterStack`, this type alias runs the commissioning in a non-concurrent mode, +/// where the device runs either BLE or Wifi, but not both at the same time. +/// +/// This is useful to save memory by only having one of the stacks active at any point in time. +/// +/// Note that Alexa does not (yet) work with non-concurrent commissioning. +pub type EspWifiNCMatterStack<'a, E> = EspWirelessMatterStack<'a, Wifi, E>; + +/// A type alias for an ESP-IDF Matter stack running over Thread (and BLE, during commissioning). +/// +/// Unlike `EspThreadMatterStack`, this type alias runs the commissioning in a non-concurrent mode, +/// where the device runs either BLE or Thread, but not both at the same time. +/// +/// This is useful to save memory by only having one of the stacks active at any point in time. +/// +/// Note that Alexa does not (yet) work with non-concurrent commissioning. +pub type EspThreadNCMatterStack<'a, E> = EspWirelessMatterStack<'a, Thread, E>; + +/// A type alias for an ESP-IDF Matter stack running over a wireless network (Wifi or Thread) and BLE. pub type EspWirelessMatterStack<'a, T, E> = MatterStack<'a, EspWirelessBle>; + +/// A type alias for an ESP-IDF implementation of the `Network` trait for a Matter stack running over +/// BLE during commissioning, and then over either WiFi or Thread when operating. pub type EspWirelessBle = WirelessBle>>; -/// An embedding of the ESP IDF Bluedroid Gatt peripheral context for the `WifiBle` network type from `rs-matter-stack`. +/// An embedding of the ESP IDF Bluedroid Gatt peripheral context for the `WirelessBle` network type from `rs-matter-stack`. /// Allows the memory of this context to be statically allocated and cost-initialized. /// /// Usage: /// ```no_run -/// MatterStack>>::new(); +/// MatterStack>>>::new(...); /// ``` /// /// ... where `E` can be a next-level, user-supplied embedding or just `()` if the user does not need to embed anything. @@ -105,7 +132,7 @@ where const GATTS_APP_ID: u16 = 0; -/// A `Ble` trait implementation +/// A `Ble` trait implementation via ESP-IDF pub struct EspMatterBle<'a, 'd, T> { context: &'a EspBtpGattContext, modem: PeripheralRef<'d, T>, @@ -172,7 +199,7 @@ pub struct EspWifiSplit<'a>( EspSystemEventLoop, ); -impl<'a> Wifi for EspWifiSplit<'a> { +impl<'a> WifiSvc for EspWifiSplit<'a> { type Error = EspError; async fn get_capabilities(&self) -> Result, Self::Error> { @@ -277,6 +304,7 @@ impl<'a> UdpBind for EspWifiSplit<'a> { } } +/// A `Wireless` trait implementation via ESP-IDF's Wifi modem pub struct EspMatterWifi<'d, T> { modem: PeripheralRef<'d, T>, sysloop: EspSystemEventLoop, @@ -288,7 +316,7 @@ impl<'d, T> EspMatterWifi<'d, T> where T: WifiModemPeripheral, { - /// Create a new instance of the `EspBle` type. + /// Create a new instance of the `EspMatterWifi` type. pub fn new( modem: impl Peripheral

+ 'd, sysloop: EspSystemEventLoop,