Skip to content

Commit

Permalink
Concurrent commissioning
Browse files Browse the repository at this point in the history
  • Loading branch information
ivmarkov committed Oct 5, 2024
1 parent 3cdbb14 commit dc427bd
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 35 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down Expand Up @@ -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 }

Expand Down
27 changes: 16 additions & 11 deletions examples/light.rs
Original file line number Diff line number Diff line change
@@ -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).
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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],
},
],
Expand Down
18 changes: 8 additions & 10 deletions examples/light_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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],
},
],
Expand Down
4 changes: 2 additions & 2 deletions src/ble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/netif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
netif: T,
sysloop: EspSystemEventLoop,
Expand All @@ -33,6 +34,7 @@ impl<T> EspMatterNetif<T>
where
T: Borrow<EspNetif>,
{
/// Create a new `EspMatterNetif` instance
pub const fn new(netif: T, sysloop: EspSystemEventLoop) -> Self {
Self { netif, sysloop }
}
Expand All @@ -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<NetifConf, EspError> {
let ip_info = netif.get_ip_info()?;

Expand Down Expand Up @@ -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::<EspRawMutex>::new());

Expand Down
46 changes: 37 additions & 9 deletions src/wireless.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -31,26 +31,53 @@ 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};

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<NC>, 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<NC>, 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<T, E>>;

/// 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<T, E> = WirelessBle<EspRawMutex, T, KvBlobBuf<EspGatt<E>>>;

/// 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<WifiBle<EspGatt<E>>>::new();
/// MatterStack<WirelessBle<EspRawMutex, Wifi, KvBlobBuf<EspGatt<E>>>>::new(...);
/// ```
///
/// ... where `E` can be a next-level, user-supplied embedding or just `()` if the user does not need to embed anything.
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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<EnumSet<Capability>, Self::Error> {
Expand Down Expand Up @@ -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,
Expand All @@ -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<P = T> + 'd,
sysloop: EspSystemEventLoop,
Expand Down

0 comments on commit dc427bd

Please sign in to comment.