Skip to content

Commit

Permalink
Concurrent commissioning, WIP Thread support (#7)
Browse files Browse the repository at this point in the history
* In-place init

* Build with Rust stable

* Build with Rust stable

* Update to the tip of my fork

* Update to latest edge-net; proper init for GattResponse

* Concurrent commissioning

* Concurrent commissioning

* fmt

* Update example

* fmt

* Fix CI

* Bugfixing

* Switch to the main branch
  • Loading branch information
ivmarkov authored Oct 5, 2024
1 parent d7c1d47 commit 5533b18
Show file tree
Hide file tree
Showing 14 changed files with 799 additions and 524 deletions.
17 changes: 9 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ readme = "README.md"
build = "build.rs"
rust-version = "1.78"

#[lib]
#harness = false

#[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", default-features = false, features = ["std"], optional = true }
#rs-matter-stack = { path = "../rs-matter-stack" }

[patch.crates-io]
rs-matter = { git = "https://github.com/ivmarkov/rs-matter", branch = "wifi" }
rs-matter = { git = "https://github.com/ivmarkov/rs-matter" }
#rs-matter = { path = "../rs-matter/rs-matter" }
#edge-nal = { git = "https://github.com/ivmarkov/edge-net" }
#edge-nal = { path = "../edge-net/edge-nal" }
Expand All @@ -34,26 +37,24 @@ debug = true
opt-level = "z"

[features]
#default = ["rs-matter-stack"]
default = ["rs-matter-stack", "async-io-mini"]
rs-matter-stack = ["dep:rs-matter-stack", "std"]
#default = ["std", "rs-matter-stack"]
default = ["std", "rs-matter-stack", "async-io-mini"]
async-io-mini = ["std", "edge-nal-std/async-io-mini"]
std = ["esp-idf-svc/std", "edge-nal-std"]
std = ["esp-idf-svc/std", "edge-nal-std", "rs-matter-stack?/std"]
examples = ["default", "esp-idf-svc/binstart", "esp-idf-svc/critical-section"] # Enable only when building the examples

[dependencies]
log = { version = "0.4", default-features = false }
heapless = "0.8"
enumset = { version = "1", default-features = false }
scopeguard = { version = "1", default-features = false }
embassy-futures = "0.1"
embassy-sync = "0.6"
embassy-time = { version = "0.3", features = ["generic-queue"] }
esp-idf-svc = { version = "0.49.1", default-features = false, features = ["alloc", "embassy-sync", "embassy-time-driver", "experimental"] }
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", default-features = false, features = ["std"], optional = true }
rs-matter-stack = { git = "https://github.com/ivmarkov/rs-matter-stack", default-features = false, optional = true }
edge-nal = "0.3"
edge-nal-std = { version = "0.3", default-features = false, optional = true }

Expand Down
84 changes: 47 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ Since ESP-IDF does support the Rust Standard Library, UDP networking just works.
(See also [All examples](#all-examples))

```rust
//! 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 @@ -31,7 +36,7 @@ use core::pin::pin;
use embassy_futures::select::select;
use embassy_time::{Duration, Timer};

use esp_idf_matter::{init_async_io, EspKvBlobStore, EspModem, EspPersist, EspWifiBleMatterStack};
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 @@ -47,13 +52,13 @@ 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::ConstStaticCell;
use static_cell::StaticCell;

#[path = "dev_att/dev_att.rs"]
mod dev_att;
Expand All @@ -67,7 +72,7 @@ fn main() -> Result<(), anyhow::Error> {
// confused by the low priority of the ESP IDF main task
// Also allocate a very large stack (for now) as `rs-matter` futures do occupy quite some space
let thread = std::thread::Builder::new()
.stack_size(70 * 1024)
.stack_size(55 * 1024)
.spawn(|| {
// Eagerly initialize `async-io` to minimize the risk of stack blowups later on
init_async_io()?;
Expand Down Expand Up @@ -95,9 +100,28 @@ fn run() -> Result<(), anyhow::Error> {
}

async fn matter() -> Result<(), anyhow::Error> {
// Take the Matter stack (can be done only once),
// Initialize the Matter stack (can be done only once),
// as we'll run it in this thread
let stack = MATTER_STACK.take();
let stack = MATTER_STACK
.uninit()
.init_with(EspWifiMatterStack::init_default(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8000,
hw_ver: 2,
sw_ver: 1,
sw_ver_str: "1",
serial_no: "aabbccdd",
device_name: "MyLight",
product_name: "ACME Light",
vendor_name: "ACME",
},
BasicCommData {
password: 20202021,
discriminator: 3840,
},
&DEV_ATT,
));

// Take some generic ESP-IDF stuff we'll need later
let sysloop = EspSystemEventLoop::take()?;
Expand All @@ -124,28 +148,26 @@ async fn matter() -> Result<(), anyhow::Error> {
.chain(
LIGHT_ENDPOINT_ID,
descriptor::ID,
HandlerCompat(descriptor::DescriptorCluster::new(Dataver::new_rand(stack.matter().rand()))),
HandlerCompat(descriptor::DescriptorCluster::new(Dataver::new_rand(
stack.matter().rand(),
))),
);

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`
EspModem::new(peripherals.modem, sysloop, timers, nvs, stack),
// Hard-coded for demo purposes
CommissioningData {
verifier: VerifierData::new_with_pw(123456, stack.matter().rand()),
discriminator: 250,
},
// Our `AsyncHandler` + `AsyncMetadata` impl
(NODE, handler),
// No user future to run
Expand Down Expand Up @@ -182,21 +204,9 @@ async fn matter() -> Result<(), anyhow::Error> {
/// The Matter stack is allocated statically to avoid
/// program stack blowups.
/// It is also a mandatory requirement when the `WifiBle` stack variation is used.
static MATTER_STACK: ConstStaticCell<EspWifiBleMatterStack<()>> =
ConstStaticCell::new(EspWifiBleMatterStack::new_default(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8000,
hw_ver: 2,
sw_ver: 1,
sw_ver_str: "1",
serial_no: "aabbccdd",
device_name: "MyLight",
product_name: "ACME Light",
vendor_name: "ACME",
},
&dev_att::HardCodedDevAtt::new(),
));
static MATTER_STACK: StaticCell<EspWifiMatterStack<()>> = StaticCell::new();

static DEV_ATT: dev_att::HardCodedDevAtt = dev_att::HardCodedDevAtt::new();

/// Endpoint 0 (the root endpoint) always runs
/// the hidden Matter system clusters, so we pick ID=1
Expand All @@ -206,10 +216,10 @@ const LIGHT_ENDPOINT_ID: u16 = 1;
const NODE: Node = Node {
id: 0,
endpoints: &[
EspWifiBleMatterStack::<()>::root_metadata(),
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
80 changes: 44 additions & 36 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 `EspWifiNCMatterStack` 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, EspModem, EspPersist, EspWifiBleMatterStack};
use esp_idf_matter::{init_async_io, EspMatterBle, EspMatterWifi, EspWifiNCMatterStack};

use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripherals::Peripherals;
Expand All @@ -26,13 +31,13 @@ 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::ConstStaticCell;
use static_cell::StaticCell;

#[path = "dev_att/dev_att.rs"]
mod dev_att;
Expand All @@ -46,7 +51,7 @@ fn main() -> Result<(), anyhow::Error> {
// confused by the low priority of the ESP IDF main task
// Also allocate a very large stack (for now) as `rs-matter` futures do occupy quite some space
let thread = std::thread::Builder::new()
.stack_size(70 * 1024)
.stack_size(75 * 1024)
.spawn(|| {
// Eagerly initialize `async-io` to minimize the risk of stack blowups later on
init_async_io()?;
Expand Down Expand Up @@ -74,9 +79,28 @@ fn run() -> Result<(), anyhow::Error> {
}

async fn matter() -> Result<(), anyhow::Error> {
// Take the Matter stack (can be done only once),
// Initialize the Matter stack (can be done only once),
// as we'll run it in this thread
let stack = MATTER_STACK.take();
let stack = MATTER_STACK
.uninit()
.init_with(EspWifiNCMatterStack::init_default(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8000,
hw_ver: 2,
sw_ver: 1,
sw_ver_str: "1",
serial_no: "aabbccdd",
device_name: "MyLight",
product_name: "ACME Light",
vendor_name: "ACME",
},
BasicCommData {
password: 20202021,
discriminator: 3840,
},
&DEV_ATT,
));

// Take some generic ESP-IDF stuff we'll need later
let sysloop = EspSystemEventLoop::take()?;
Expand Down Expand Up @@ -108,25 +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`
EspModem::new(peripherals.modem, sysloop, timers, nvs, stack),
// Hard-coded for demo purposes
CommissioningData {
verifier: VerifierData::new_with_pw(123456, stack.matter().rand()),
discriminator: 250,
},
// Our `AsyncHandler` + `AsyncMetadata` impl
(NODE, handler),
// No user future to run
Expand Down Expand Up @@ -163,21 +183,9 @@ async fn matter() -> Result<(), anyhow::Error> {
/// The Matter stack is allocated statically to avoid
/// program stack blowups.
/// It is also a mandatory requirement when the `WifiBle` stack variation is used.
static MATTER_STACK: ConstStaticCell<EspWifiBleMatterStack<()>> =
ConstStaticCell::new(EspWifiBleMatterStack::new_default(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8000,
hw_ver: 2,
sw_ver: 1,
sw_ver_str: "1",
serial_no: "aabbccdd",
device_name: "MyLight",
product_name: "ACME Light",
vendor_name: "ACME",
},
&dev_att::HardCodedDevAtt::new(),
));
static MATTER_STACK: StaticCell<EspWifiNCMatterStack<()>> = StaticCell::new();

static DEV_ATT: dev_att::HardCodedDevAtt = dev_att::HardCodedDevAtt::new();

/// Endpoint 0 (the root endpoint) always runs
/// the hidden Matter system clusters, so we pick ID=1
Expand All @@ -187,10 +195,10 @@ const LIGHT_ENDPOINT_ID: u16 = 1;
const NODE: Node = Node {
id: 0,
endpoints: &[
EspWifiBleMatterStack::<()>::root_metadata(),
EspWifiNCMatterStack::<()>::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
Loading

0 comments on commit 5533b18

Please sign in to comment.