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

In place init #3

Merged
merged 12 commits into from
Sep 23, 2024
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ readme = "README.md"
rust-version = "1.77"

[patch.crates-io]
rs-matter = { git = "https://github.com/ivmarkov/rs-matter", branch = "wifi" }
rs-matter = { git = "https://github.com/ivmarkov/rs-matter", branch = "tip" }
#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 @@ -25,7 +25,7 @@ rs-matter = { git = "https://github.com/ivmarkov/rs-matter", branch = "wifi" }
[features]
default = []
zeroconf = ["os", "rs-matter/zeroconf"]
os = ["backtrace", "rs-matter/os", "rs-matter/mbedtls", "embassy-time/std", "embassy-time/generic-queue"]
os = ["backtrace", "rs-matter/os", "rs-matter/rustcrypto", "embassy-time/std", "embassy-time/generic-queue"]
backtrace = ["std", "rs-matter/backtrace"]
std = ["alloc", "rs-matter/std", "edge-nal-std"]
alloc = ["embedded-svc/alloc"]
Expand All @@ -45,6 +45,7 @@ edge-nal-std = { version = "0.3", optional = true }
edge-mdns = { version = "0.3", optional = true }

[target.'cfg(all(unix, not(target_os = "espidf")))'.dependencies]
bitflags = "2"
nix = { version = "0.27", features = ["net"], optional = true }

[dev-dependencies]
Expand Down
44 changes: 23 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ Instantiate it and then call `MatterStack::<...>::run(...)`.

**Flexibility**.

Using `MatterStack<...>` hard-codes the following:
* _One large future_: The Matter stack is assembled as one large future which is not `Send`. Using an executor to poll that future together with others is still possible, but the executor should be a local one (i.e. Tokio's `LocalSet`, `async_executor::LocalExecutor` and so on).
* _Allocation strategy_: a number of large-ish buffers are const-allocated inside the `MatterStack` struct. This allows the whole stack to be statically-allocated with `ConstStaticCell` - yet - that would eat up 20 to 60K of your flash size, depending on the selected max number of subscriptions, exchange buffers and so on. A different allocation strategy might be provided in future.
The Matter stack is assembled as one large future which is not `Send`. Using an executor to poll that future together with others is still possible, but the executor should be a local one (i.e. Tokio's `LocalSet`, `async_executor::LocalExecutor` and so on).

## The examples are STD-only?

Expand Down Expand Up @@ -69,14 +67,15 @@ use rs_matter::data_model::objects::{Dataver, Endpoint, HandlerCompat, Node};
use rs_matter::data_model::system_model::descriptor;
use rs_matter::error::Error;
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_stack::netif::UnixNetif;
use rs_matter_stack::persist::{DirKvBlobStore, KvBlobBuf, KvPersist};
use rs_matter_stack::EthMatterStack;

use static_cell::ConstStaticCell;
use static_cell::StaticCell;

#[path = "dev_att/dev_att.rs"]
mod dev_att;
Expand All @@ -88,9 +87,24 @@ fn main() -> Result<(), Error> {

info!("Starting...");

// 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(EthMatterStack::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",
},
&DEV_ATT,
));

// Our "light" on-off cluster.
// Can be anything implementing `rs_matter::data_model::AsyncHandler`
Expand Down Expand Up @@ -162,21 +176,9 @@ fn main() -> Result<(), Error> {

/// The Matter stack is allocated statically to avoid
/// program stack blowups.
static MATTER_STACK: ConstStaticCell<EthMatterStack<KvBlobBuf<()>>> =
ConstStaticCell::new(EthMatterStack::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<EthMatterStack<KvBlobBuf<()>>> = StaticCell::new();

const 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 Down
54 changes: 28 additions & 26 deletions examples/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ 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::error::Error;
use rs_matter::secure_channel::spake2p::VerifierData;
use rs_matter::utils::init::InitMaybeUninit;
use rs_matter::utils::select::Coalesce;
use rs_matter::utils::std_mutex::StdRawMutex;
use rs_matter::CommissioningData;
use rs_matter::utils::sync::blocking::raw::StdRawMutex;
use rs_matter::BasicCommData;

use rs_matter_stack::modem::DummyLinuxModem;
use rs_matter_stack::persist::{DirKvBlobStore, KvBlobBuf, KvPersist};
use rs_matter_stack::WifiBleMatterStack;

use static_cell::ConstStaticCell;
use static_cell::StaticCell;

#[path = "dev_att/dev_att.rs"]
mod dev_att;
Expand All @@ -39,9 +39,28 @@ fn main() -> Result<(), Error> {

info!("Starting...");

// 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(WifiBleMatterStack::init_default(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8001,
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,
));

// Our "light" on-off cluster.
// Can be anything implementing `rs_matter::data_model::AsyncHandler`
Expand Down Expand Up @@ -75,11 +94,6 @@ fn main() -> Result<(), Error> {
KvPersist::new_wifi_ble(DirKvBlobStore::new_default(), stack),
// A Linux-specific modem using BlueZ
DummyLinuxModem::default(),
// 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 @@ -116,21 +130,9 @@ fn main() -> Result<(), 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<WifiBleMatterStack<StdRawMutex, KvBlobBuf<()>>> =
ConstStaticCell::new(WifiBleMatterStack::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<WifiBleMatterStack<StdRawMutex, KvBlobBuf<()>>> = StaticCell::new();

const 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 Down
59 changes: 32 additions & 27 deletions examples/light_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use core::pin::pin;
use embassy_futures::select::select;
use embassy_time::{Duration, Timer};

use env_logger::Target;
use log::info;

use rs_matter::data_model::cluster_basic_information::BasicInfoConfig;
Expand All @@ -22,29 +23,50 @@ 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::error::Error;
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::netif::UnixNetif;
use rs_matter_stack::persist::{DirKvBlobStore, KvBlobBuf, KvPersist};
use rs_matter_stack::EthMatterStack;

use static_cell::ConstStaticCell;
use static_cell::StaticCell;

#[path = "dev_att/dev_att.rs"]
mod dev_att;

fn main() -> Result<(), Error> {
env_logger::init_from_env(
env_logger::Builder::from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
);
)
.target(Target::Stdout)
.init();

info!("Starting...");

// 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(EthMatterStack::init_default(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8001,
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,
));

// Our "light" on-off cluster.
// Can be anything implementing `rs_matter::data_model::AsyncHandler`
Expand Down Expand Up @@ -78,11 +100,6 @@ fn main() -> Result<(), Error> {
KvPersist::new_eth(DirKvBlobStore::new_default(), stack),
// Will try to find a default network interface
UnixNetif::default(),
// 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 @@ -116,21 +133,9 @@ fn main() -> Result<(), Error> {

/// The Matter stack is allocated statically to avoid
/// program stack blowups.
static MATTER_STACK: ConstStaticCell<EthMatterStack<KvBlobBuf<()>>> =
ConstStaticCell::new(EthMatterStack::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<EthMatterStack<KvBlobBuf<()>>> = StaticCell::new();

const 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 Down
43 changes: 30 additions & 13 deletions src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rs_matter::data_model::sdm::nw_commissioning::EthNwCommCluster;
use rs_matter::data_model::sdm::{ethernet_nw_diagnostics, nw_commissioning};
use rs_matter::error::Error;
use rs_matter::pairing::DiscoveryCapabilities;
use rs_matter::CommissioningData;
use rs_matter::utils::init::{init, Init};

use crate::netif::Netif;
use crate::network::{Embedding, Network};
Expand All @@ -32,18 +32,26 @@ use crate::MatterStack;
///
/// The expectation is nevertheless that for production use-cases
/// the `Eth` network would really only be used for Ethernet.
pub struct Eth<E = ()>(E);
pub struct Eth<E = ()> {
embedding: E,
}

impl<E> Network for Eth<E>
where
E: Embedding + 'static,
{
const INIT: Self = Self(E::INIT);
const INIT: Self = Self { embedding: E::INIT };

type Embedding = E;

fn embedding(&self) -> &Self::Embedding {
&self.0
&self.embedding
}

fn init() -> impl Init<Self> {
init!(Self {
embedding <- E::init(),
})
}
}

Expand Down Expand Up @@ -85,6 +93,16 @@ where
Ok(())
}

/// Enable basic commissioning over IP (mDNS) by setting up a PASE session and printing the pairing code and QR code.
///
/// The method will return an error if there is not enough space in the buffer to print the pairing code and QR code
/// or if the PASE session could not be set up (due to another PASE session already being active, for example).
pub async fn enable_basic_commissioning(&self) -> Result<(), Error> {
self.matter()
.enable_basic_commissioning(DiscoveryCapabilities::IP, 0)
.await // TODO
}

/// Run the Matter stack for Ethernet network.
///
/// Parameters:
Expand All @@ -97,7 +115,6 @@ where
&self,
persist: P,
netif: I,
dev_comm: CommissioningData,
handler: H,
user: U,
) -> Result<(), Error>
Expand All @@ -111,14 +128,14 @@ where

let mut user = pin!(user);

self.run_with_netif(
persist,
netif,
Some((dev_comm, DiscoveryCapabilities::new(true, false, false))),
handler,
&mut user,
)
.await
// TODO persist.load().await?;

if !self.is_commissioned().await? {
self.enable_basic_commissioning().await?;
}

self.run_with_netif(persist, netif, handler, &mut user)
.await
}
}

Expand Down
Loading
Loading