Skip to content

Commit

Permalink
Fix example, clippy, use GIT based deps patching
Browse files Browse the repository at this point in the history
  • Loading branch information
ivmarkov committed Apr 30, 2024
1 parent 76038ca commit 8ba5a79
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 89 deletions.
7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ rust-version = "1.77"

[patch.crates-io]
embedded-svc = { git = "https://github.com/esp-rs/embedded-svc" }
esp-idf-svc = { path = "../esp-idf-svc" }
esp-idf-sys = { path = "../esp-idf-sys" }
rs-matter = { path = "../rs-matter/rs-matter" }
rs-matter-macros = { path = "../rs-matter/rs-matter-macros" }
esp-idf-svc = { git = "https://github.com/esp-rs/esp-idf-svc", branch = "gatt" }
rs-matter = { git = "https://github.com/ivmarkov/rs-matter", branch = "wifi" }
rs-matter-macros = { git = "https://github.com/ivmarkov/rs-matter", branch = "wifi" }

[features]
default = ["std"]
Expand Down
54 changes: 36 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@

## Overview

Configuring and running the [`rs-matter`]() stack is not trivial.
Configuring and running the [`rs-matter`](https://github.com/project-chip/rs-matter) stack is not trivial.

Users are expected to provide implementations for various `rs-matter` abstractions, like a UDP stack, BLE stack, randomizer, epoch time, responder and so on and so forth.

Furthermore, _operating_ the assembled Matter stack is also challenging, as various features might need to be switched on or off depending on whether Matter is running in commissioning or operating mode, and also depending on the current network connectivity (as in e.g. Wifi signal lost).

This crate provides an all-in-one `MatterStack` assembly.
This crate provides an all-in-one [`MatterStack`]() assembly that configures `rs-matter` for operating on top of the ESP IDF SDK.

Instantiate it and then call `MatterStack::run(...)`.

```rust
//! An example utilizing the `MatterStack<WifiBle>` struct.
//! As the name suggests, this Matter stack assembly uses Wifi as the main transport, and BLE for commissioning.
//!
//! If use want to use Ethernet, utilize `MatterStack<Eth>` instead.
//!
//! The example implements a fictitious Light device (an on-off cluster).

use core::borrow::Borrow;
Expand All @@ -35,6 +36,7 @@ use esp_idf_matter::{error::Error, MatterStack, WifiBle};
use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripherals::Peripherals;
use esp_idf_svc::nvs::EspDefaultNvsPartition;
use esp_idf_svc::timer::EspTaskTimerService;

use log::info;

Expand All @@ -53,13 +55,16 @@ use static_cell::ConstStaticCell;
mod dev_att;

fn main() -> Result<(), Error> {
// Take the Matter stack (can be done only once), as we'll run it in this thread
// Take the Matter stack (can be done only once),
// as we'll run it in this thread
let stack = MATTER_STACK.take();

// Our "light" on-off cluster. Can be anything implementing `rs_matter::data_model::AsyncHandler`
// Our "light" on-off cluster.
// Can be anything implementing `rs_matter::data_model::AsyncHandler`
let on_off = cluster_on_off::OnOffCluster::new(*stack.matter().borrow());

// Chain our endpoint clusters with the (root) Endpoint 0 system clusters in the final handler
// Chain our endpoint clusters with the
// (root) Endpoint 0 system clusters in the final handler
let handler = HandlerCompat(
stack
.root_handler()
Expand All @@ -75,25 +80,34 @@ fn main() -> Result<(), Error> {
);

// 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
// 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 matter = pin!(stack.run(
EspSystemEventLoop::take()?, // The Matter stack needs (a clone of) the system event loop
EspDefaultNvsPartition::take()?, // The Matter stack needs (a clone of) the default ESP IDF NVS partition too
Peripherals::take()?.modem, // The Matter stack needs the BT/Wifi modem peripheral
// The Matter stack needs (a clone of) the system event loop
EspSystemEventLoop::take()?,
// The Matter stack needs (a clone of) the timer service
EspTaskTimerService::new()?,
// The Matter stack needs (a clone of) the default ESP IDF NVS partition
EspDefaultNvsPartition::take()?,
// 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`
Peripherals::take()?.modem,
// Hard-coded for demo purposes
CommissioningData {
// Hard-coded for demo purposes
verifier: VerifierData::new_with_pw(123456, *stack.matter().borrow()),
discriminator: 250,
},
// Our `AsyncHandler` + `AsyncMetadata` impl
(NODE, handler),
));

// Just for demoing purposes:
//
// Run a sample loop that simulates state changes triggered by the HAL
// Changes will be properly communicated to the Matter controllers (i.e. Google Home, Alexa)
// and other Matter devices thanks to subscriptions
// Changes will be properly communicated to the Matter controllers
// (i.e. Google Home, Alexa) and other Matter devices thanks to subscriptions
let device = pin!(async {
loop {
// Simulate user toggling the light with a physical switch every 5 seconds
Expand All @@ -102,20 +116,23 @@ fn main() -> Result<(), Error> {
// Toggle
on_off.set(!on_off.get());

// Let the Matter stack know that we have changed the state of our Lamp device
// Let the Matter stack know that we have changed
// the state of our Lamp device
stack.notify_changed();

info!("Lamp toggled");
}
});

// Schedule both the Matter loop & the device loop together
// Schedule the Matter run & the device loop together
esp_idf_svc::hal::task::block_on(select(matter, device).coalesce())?;

Ok(())
}

/// The Matter stack is allocated statically to avoid program stack blowups
/// 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<MatterStack<WifiBle>> =
ConstStaticCell::new(MatterStack::new(
&BasicInfoConfig {
Expand All @@ -132,7 +149,8 @@ static MATTER_STACK: ConstStaticCell<MatterStack<WifiBle>> =
&dev_att::HardCodedDevAtt::new(),
));

/// Endpoint 0 (the root endpoint) always runs the hidden Matter system clusters, so we pick ID=1
/// Endpoint 0 (the root endpoint) always runs
/// the hidden Matter system clusters, so we pick ID=1
const LIGHT_ENDPOINT_ID: u16 = 1;

/// The Matter Light device Node
Expand Down
2 changes: 1 addition & 1 deletion clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
future-size-threshold = 250
future-size-threshold = 2048
48 changes: 32 additions & 16 deletions examples/light.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! An example utilizing the `MatterStack<WifiBle>` struct.
//! As the name suggests, this Matter stack assembly uses Wifi as the main transport, and BLE for commissioning.
//! If use want to use Ethernet, utilize `MatterStack<Eth>` instead.
//!
//! The example implements a fictitious Light device (an on-off cluster).
Expand All @@ -14,6 +15,7 @@ use esp_idf_matter::{error::Error, MatterStack, WifiBle};
use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripherals::Peripherals;
use esp_idf_svc::nvs::EspDefaultNvsPartition;
use esp_idf_svc::timer::EspTaskTimerService;

use log::info;

Expand All @@ -32,13 +34,16 @@ use static_cell::ConstStaticCell;
mod dev_att;

fn main() -> Result<(), Error> {
// Take the Matter stack (can be done only once), as we'll run it in this thread
// Take the Matter stack (can be done only once),
// as we'll run it in this thread
let stack = MATTER_STACK.take();

// Our "light" on-off cluster. Can be anything implementing `rs_matter::data_model::AsyncHandler`
// Our "light" on-off cluster.
// Can be anything implementing `rs_matter::data_model::AsyncHandler`
let on_off = cluster_on_off::OnOffCluster::new(*stack.matter().borrow());

// Chain our endpoint clusters with the (root) Endpoint 0 system clusters in the final handler
// Chain our endpoint clusters with the
// (root) Endpoint 0 system clusters in the final handler
let handler = HandlerCompat(
stack
.root_handler()
Expand All @@ -54,26 +59,34 @@ fn main() -> Result<(), Error> {
);

// 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
// 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 matter = pin!(stack.run(
EspSystemEventLoop::take()?, // The Matter stack needs (a clone of) the system event loop
EspTimerService::take()?, // The Matter stack needs (a clone of) the timer service
EspDefaultNvsPartition::take()?, // The Matter stack needs (a clone of) the default ESP IDF NVS partition too
Peripherals::take()?.modem, // The Matter stack needs the BT/Wifi modem peripheral
// The Matter stack needs (a clone of) the system event loop
EspSystemEventLoop::take()?,
// The Matter stack needs (a clone of) the timer service
EspTaskTimerService::new()?,
// The Matter stack needs (a clone of) the default ESP IDF NVS partition
EspDefaultNvsPartition::take()?,
// 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`
Peripherals::take()?.modem,
// Hard-coded for demo purposes
CommissioningData {
// Hard-coded for demo purposes
verifier: VerifierData::new_with_pw(123456, *stack.matter().borrow()),
discriminator: 250,
},
// Our `AsyncHandler` + `AsyncMetadata` impl
(NODE, handler),
));

// Just for demoing purposes:
//
// Run a sample loop that simulates state changes triggered by the HAL
// Changes will be properly communicated to the Matter controllers (i.e. Google Home, Alexa)
// and other Matter devices thanks to subscriptions
// Changes will be properly communicated to the Matter controllers
// (i.e. Google Home, Alexa) and other Matter devices thanks to subscriptions
let device = pin!(async {
loop {
// Simulate user toggling the light with a physical switch every 5 seconds
Expand All @@ -82,7 +95,8 @@ fn main() -> Result<(), Error> {
// Toggle
on_off.set(!on_off.get());

// Let the Matter stack know that we have changed the state of our Lamp device
// Let the Matter stack know that we have changed
// the state of our Lamp device
stack.notify_changed();

info!("Lamp toggled");
Expand All @@ -95,8 +109,9 @@ fn main() -> Result<(), Error> {
Ok(())
}

/// The Matter stack is allocated statically to avoid program stack blowups
/// It is also a mandatory requirement when the `WifiBle` stack variation is used
/// 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<MatterStack<WifiBle>> =
ConstStaticCell::new(MatterStack::new(
&BasicInfoConfig {
Expand All @@ -113,7 +128,8 @@ static MATTER_STACK: ConstStaticCell<MatterStack<WifiBle>> =
&dev_att::HardCodedDevAtt::new(),
));

/// Endpoint 0 (the root endpoint) always runs the hidden Matter system clusters, so we pick ID=1
/// Endpoint 0 (the root endpoint) always runs
/// the hidden Matter system clusters, so we pick ID=1
const LIGHT_ENDPOINT_ID: u16 = 1;

/// The Matter Light device Node
Expand Down
8 changes: 5 additions & 3 deletions src/ble.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use core::borrow::Borrow;
use core::cell::RefCell;

use alloc::borrow::ToOwned;

use embassy_sync::blocking_mutex::Mutex;

use enumset::enum_set;
Expand Down Expand Up @@ -122,7 +124,7 @@ where

unsafe {
gap.subscribe_nonstatic(|event| {
let ctx = GattExecContext::new(&gap, &gatts, &self.context);
let ctx = GattExecContext::new(&gap, &gatts, self.context);

ctx.check_esp_status(ctx.on_gap_event(event));
})?;
Expand All @@ -133,7 +135,7 @@ where

unsafe {
gatts.subscribe_nonstatic(|(gatt_if, event)| {
let ctx = GattExecContext::new(&gap, &gatts, &self.context);
let ctx = GattExecContext::new(&gap, &gatts, self.context);

ctx.check_esp_status(ctx.on_gatts_event(
&service_name,
Expand All @@ -148,7 +150,7 @@ where
loop {
let mut ind = self.context.ind.lock_if(|ind| ind.data.is_empty()).await;

let ctx = GattExecContext::new(&gap, &gatts, &self.context);
let ctx = GattExecContext::new(&gap, &gatts, self.context);

// TODO: Is this asynchronous?
ctx.indicate(&ind.data, ind.addr)?;
Expand Down
38 changes: 19 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
#![no_std]
#![allow(async_fn_in_trait)]
#![allow(unknown_lints)]
#![allow(renamed_and_removed_lints)]
#![allow(clippy::declare_interior_mutable_const)]
#![warn(clippy::large_futures)]

#[cfg(feature = "std")]
#[allow(unused_imports)]
#[macro_use]
extern crate std;

//#[cfg(feature = "alloc")]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;

use core::net::{Ipv4Addr, Ipv6Addr};
use core::pin::pin;

use std::net::UdpSocket;

use ble::BtpGattContext;

use embassy_futures::select::{select, select3};
use embassy_sync::blocking_mutex::raw::{NoopRawMutex, RawMutex};
use embassy_sync::mutex::Mutex;
Expand All @@ -14,7 +29,6 @@ use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::modem::Modem;
use esp_idf_svc::hal::peripheral::Peripheral;
use esp_idf_svc::hal::task::embassy_sync::EspRawMutex;
use esp_idf_svc::netif::EspNetif;
use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvs, EspNvsPartition, NvsPartitionId};
use esp_idf_svc::timer::EspTaskTimerService;
use esp_idf_svc::wifi::{AsyncWifi, EspWifi};
Expand All @@ -39,9 +53,7 @@ use rs_matter::{CommissioningData, Matter, MATTER_PORT};
use wifi::mgmt::WifiManager;
use wifi::WifiContext;

extern crate alloc;

use crate::ble::BtpGattPeripheral;
use crate::ble::{BtpGattContext, BtpGattPeripheral};
use crate::error::Error;
use crate::multicast::{join_multicast_v4, join_multicast_v6};
use crate::netif::{get_ips, NetifAccess};
Expand Down Expand Up @@ -308,18 +320,6 @@ impl<'a> MatterStack<'a, Eth> {
}
}

impl<'d, M> NetifAccess for &Mutex<M, AsyncWifi<&mut EspWifi<'d>>>
where
M: RawMutex,
{
async fn with_netif<F, R>(&self, f: F) -> R
where
F: FnOnce(&EspNetif) -> R,
{
f(self.lock().await.wifi().sta_netif())
}
}

impl<'a> MatterStack<'a, WifiBle> {
pub const fn root_metadata() -> Endpoint<'static> {
root_endpoint::endpoint(0)
Expand All @@ -329,7 +329,7 @@ impl<'a> MatterStack<'a, WifiBle> {
root_endpoint::handler(0, self.matter())
}

pub async fn is_commissioned(&self, nvs: EspDefaultNvsPartition) -> Result<bool, Error> {
pub async fn is_commissioned(&self, _nvs: EspDefaultNvsPartition) -> Result<bool, Error> {
todo!()
}

Expand Down
2 changes: 1 addition & 1 deletion src/mdns.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@

// TODO
Loading

0 comments on commit 8ba5a79

Please sign in to comment.