-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split stack.rs, remove code duplication
- Loading branch information
Showing
5 changed files
with
698 additions
and
403 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
//! An example utilizing the `MatterStack<Eth>` struct. | ||
//! As the name suggests, this Matter stack assembly uses Ethernet as the main transport, as well as for commissioning. | ||
//! | ||
//! Notice thart we actually don't use Ethernet for real, as ESP32s don't have Ethernet ports out of the box. | ||
//! Instead, we utilize Wifi, which - from the POV of Matter - is indistinguishable from Ethernet as long as the Matter | ||
//! stack is not concerned with connecting to the Wifi network, managing its credentials etc. and can assume it "pre-exists". | ||
//! | ||
//! The example implements a fictitious Light device (an on-off cluster). | ||
|
||
use core::borrow::Borrow; | ||
use core::pin::pin; | ||
|
||
use embassy_futures::select::select; | ||
use embassy_time::{Duration, Timer}; | ||
|
||
use embedded_svc::wifi; | ||
use esp_idf_matter::{init_async_io, Error, Eth, MatterStack, WifiBle}; | ||
|
||
use esp_idf_svc::eventloop::EspSystemEventLoop; | ||
use esp_idf_svc::hal::peripherals::Peripherals; | ||
use esp_idf_svc::hal::task::block_on; | ||
use esp_idf_svc::log::EspLogger; | ||
use esp_idf_svc::nvs::EspDefaultNvsPartition; | ||
use esp_idf_svc::timer::EspTaskTimerService; | ||
|
||
use esp_idf_svc::wifi::{AsyncWifi, EspWifi}; | ||
use log::{error, info}; | ||
|
||
use rs_matter::data_model::cluster_basic_information::BasicInfoConfig; | ||
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::{Endpoint, HandlerCompat, Node}; | ||
use rs_matter::data_model::system_model::descriptor; | ||
use rs_matter::secure_channel::spake2p::VerifierData; | ||
use rs_matter::utils::select::Coalesce; | ||
use rs_matter::CommissioningData; | ||
|
||
use static_cell::ConstStaticCell; | ||
|
||
#[path = "dev_att/dev_att.rs"] | ||
mod dev_att; | ||
|
||
const WIFI_SSID: &str = env!("WIFI_SSID"); | ||
const WIFI_PASS: &str = env!("WIFI_PASS"); | ||
|
||
fn main() -> Result<(), Error> { | ||
EspLogger::initialize_default(); | ||
|
||
info!("Starting..."); | ||
|
||
// Run in a higher-prio thread to avoid issues with `async-io` getting | ||
// 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(65 * 1024) | ||
.spawn(|| { | ||
// Eagerly initialize `async-io` to minimize the risk of stack blowups later on | ||
init_async_io()?; | ||
|
||
run() | ||
}) | ||
.unwrap(); | ||
|
||
thread.join().unwrap() | ||
} | ||
|
||
#[inline(never)] | ||
#[cold] | ||
fn run() -> Result<(), Error> { | ||
let result = block_on(matter()); | ||
|
||
if let Err(e) = &result { | ||
error!("Matter aborted execution with error: {:?}", e); | ||
} | ||
{ | ||
info!("Matter finished execution successfully"); | ||
} | ||
|
||
result | ||
} | ||
|
||
async fn matter() -> Result<(), Error> { | ||
let sysloop = EspSystemEventLoop::take()?; | ||
let nvs = EspDefaultNvsPartition::take()?; | ||
|
||
// Configure and start the Wifi first | ||
let mut wifi = Box::new(AsyncWifi::wrap( | ||
EspWifi::new( | ||
Peripherals::take()?.modem, | ||
sysloop.clone(), | ||
Some(nvs.clone()), | ||
)?, | ||
sysloop.clone(), | ||
EspTaskTimerService::new()?, | ||
)?); | ||
wifi.set_configuration(&wifi::Configuration::Client(wifi::ClientConfiguration { | ||
ssid: WIFI_SSID.try_into().unwrap(), | ||
password: WIFI_PASS.try_into().unwrap(), | ||
..Default::default() | ||
}))?; | ||
wifi.start().await?; | ||
wifi.connect().await?; | ||
|
||
// 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` | ||
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 | ||
let handler = stack | ||
.root_handler() | ||
// Our on-off cluster, on Endpoint 1 | ||
.chain( | ||
LIGHT_ENDPOINT_ID, | ||
cluster_on_off::ID, | ||
HandlerCompat(&on_off), | ||
) | ||
// Each Endpoint needs a Descriptor cluster too | ||
// Just use the one that `rs-matter` provides out of the box | ||
.chain( | ||
LIGHT_ENDPOINT_ID, | ||
descriptor::ID, | ||
HandlerCompat(descriptor::DescriptorCluster::new(*stack.matter().borrow())), | ||
); | ||
|
||
// 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 (a clone of) the system event loop | ||
sysloop, | ||
// The Matter stack needs (a clone of) the default ESP IDF NVS partition | ||
nvs, | ||
// For the Ethernet case, the Matter stack needs access to the network | ||
// interface (i.e. something that implements the `NetifAccess` trait) | ||
// so that it can track when the interface goes up/down. `EspNetif` obviously | ||
// does implement this trait | ||
wifi.wifi().sta_netif(), | ||
// Hard-coded for demo purposes | ||
CommissioningData { | ||
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 | ||
let mut device = pin!(async { | ||
loop { | ||
// Simulate user toggling the light with a physical switch every 5 seconds | ||
Timer::after(Duration::from_secs(5)).await; | ||
|
||
// Toggle | ||
on_off.set(!on_off.get()); | ||
|
||
// Let the Matter stack know that we have changed | ||
// the state of our Light device | ||
stack.notify_changed(); | ||
|
||
info!("Light toggled"); | ||
} | ||
}); | ||
|
||
// Schedule the Matter run & the device loop together | ||
select(&mut matter, &mut device).coalesce().await | ||
} | ||
|
||
/// The Matter stack is allocated statically to avoid | ||
/// program stack blowups. | ||
static MATTER_STACK: ConstStaticCell<MatterStack<Eth>> = ConstStaticCell::new(MatterStack::new( | ||
&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(), | ||
)); | ||
|
||
/// 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 | ||
const NODE: Node<'static> = Node { | ||
id: 0, | ||
endpoints: &[ | ||
MatterStack::<WifiBle>::root_metadata(), | ||
Endpoint { | ||
id: LIGHT_ENDPOINT_ID, | ||
device_type: DEV_TYPE_ON_OFF_LIGHT, | ||
clusters: &[descriptor::CLUSTER, cluster_on_off::CLUSTER], | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.