Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrent commissioning, WIP Thread support #7

Merged
merged 13 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading