Everything necessary to run rs-matter with Embassy:
- Implementation of
rs-matter'sGattPeripheralfor BLE comissioning support based ontrouble. rs-matter-stacksupport withNetif,Gatt,Wireless(for both Wifi and Thread) andKvBlobStoreimplementations.
(See All examples and how to build them)
//! An example utilizing the `EmbassyWifiMatterStack` struct.
//!
//! As the name suggests, this Matter stack assembly uses Wifi as the main transport,
//! and thus BLE for commissioning.
//!
//! If you want to use Ethernet, utilize `EmbassyEthMatterStack` instead.
//! If you want to use non-concurrent commissioning, call `run` instead of `run_coex`
//! and provision a higher `BUMP_SIZE` because the non-concurrent commissioning has slightly higher
//! memory requirements on the futures' sizes.
//! (Note: Alexa does not work (yet) with non-concurrent commissioning.)
//!
//! The example implements a fictitious Light device (an On-Off Matter cluster).
#![no_std]
#![no_main]
#![recursion_limit = "256"]
use core::pin::pin;
use embassy_executor::Spawner;
use esp_alloc::heap_allocator;
use esp_backtrace as _;
use esp_hal::ram;
use esp_hal::timer::timg::TimerGroup;
use esp_metadata_generated::memory_range;
use log::info;
use rs_matter_embassy::epoch::epoch;
use rs_matter_embassy::matter::dm::clusters::desc::{self, ClusterHandler as _};
use rs_matter_embassy::matter::dm::clusters::on_off::test::TestOnOffDeviceLogic;
use rs_matter_embassy::matter::dm::clusters::on_off::{self, OnOffHooks};
use rs_matter_embassy::matter::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET};
use rs_matter_embassy::matter::dm::devices::DEV_TYPE_ON_OFF_LIGHT;
use rs_matter_embassy::matter::dm::{Async, Dataver, EmptyHandler, Endpoint, EpClMatcher, Node};
use rs_matter_embassy::matter::utils::init::InitMaybeUninit;
use rs_matter_embassy::matter::{clusters, devices};
use rs_matter_embassy::rand::esp::{esp_init_rand, esp_rand};
use rs_matter_embassy::stack::persist::DummyKvBlobStore;
use rs_matter_embassy::wireless::esp::EspWifiDriver;
use rs_matter_embassy::wireless::{EmbassyWifi, EmbassyWifiMatterStack};
extern crate alloc;
macro_rules! mk_static {
($t:ty) => {{
#[cfg(not(feature = "esp32"))]
{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
STATIC_CELL.uninit()
}
#[cfg(feature = "esp32")]
alloc::boxed::Box::leak(alloc::boxed::Box::<$t>::new_uninit())
}};
}
/// The amount of memory for allocating all `rs-matter-stack` futures created during
/// the execution of the `run*` methods.
/// This does NOT include the rest of the Matter stack.
///
/// The futures of `rs-matter-stack` created during the execution of the `run*` methods
/// are allocated in a special way using a small bump allocator which results
/// in a much lower memory usage by those.
///
/// If - for your platform - this size is not enough, increase it until
/// the program runs without panics during the stack initialization.
const BUMP_SIZE: usize = 16500;
/// Heap strictly necessary only for Wifi+BLE and for the only Matter dependency which needs (~4KB) alloc - `x509`
#[cfg(not(feature = "esp32"))]
const HEAP_SIZE: usize = 100 * 1024;
/// On the esp32, we allocate the Matter Stack from heap as well, due to the non-contiguous memory regions on that chip
#[cfg(feature = "esp32")]
const HEAP_SIZE: usize = 140 * 1024;
const RECLAIMED_RAM: usize =
memory_range!("DRAM2_UNINIT").end - memory_range!("DRAM2_UNINIT").start;
esp_bootloader_esp_idf::esp_app_desc!();
#[esp_rtos::main]
async fn main(_s: Spawner) {
esp_println::logger::init_logger(log::LevelFilter::Info);
info!("Starting...");
heap_allocator!(size: HEAP_SIZE - RECLAIMED_RAM);
heap_allocator!(#[ram(reclaimed)] size: RECLAIMED_RAM);
// == Step 1: ==
// Necessary `esp-hal` and `esp-wifi` initialization boilerplate
let peripherals = esp_hal::init(esp_hal::Config::default());
// To erase generics, `Matter` takes a rand `fn` rather than a trait or a closure,
// so we need to initialize the global `rand` fn once
esp_init_rand(esp_hal::rng::Rng::new());
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_rtos::start(
timg0.timer0,
#[cfg(target_arch = "riscv32")]
esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT)
.software_interrupt0,
);
let init = esp_radio::init().unwrap();
// == Step 2: ==
// Allocate the Matter stack.
// For MCUs, it is best to allocate it statically, so as to avoid program stack blowups (its memory footprint is ~ 35 to 50KB).
// It is also (currently) a mandatory requirement when the wireless stack variation is used.
let stack = mk_static!(EmbassyWifiMatterStack::<BUMP_SIZE, ()>).init_with(
EmbassyWifiMatterStack::init(&TEST_DEV_DET, TEST_DEV_COMM, &TEST_DEV_ATT, epoch, esp_rand),
);
// == Step 3: ==
// Our "light" on-off cluster.
// It will toggle the light state every 5 seconds
let on_off = on_off::OnOffHandler::new_standalone(
Dataver::new_rand(stack.matter().rand()),
LIGHT_ENDPOINT_ID,
TestOnOffDeviceLogic::new(true),
);
// Chain our endpoint clusters
let handler = EmptyHandler
// Our on-off cluster, on Endpoint 1
.chain(
EpClMatcher::new(
Some(LIGHT_ENDPOINT_ID),
Some(TestOnOffDeviceLogic::CLUSTER.id),
),
on_off::HandlerAsyncAdaptor(&on_off),
)
// Each Endpoint needs a Descriptor cluster too
// Just use the one that `rs-matter` provides out of the box
.chain(
EpClMatcher::new(Some(LIGHT_ENDPOINT_ID), Some(desc::DescHandler::CLUSTER.id)),
Async(desc::DescHandler::new(Dataver::new_rand(stack.matter().rand())).adapt()),
);
// Create the persister & load any previously saved state
// `EmbassyPersist`+`EmbassyKvBlobStore` saves to a user-supplied NOR Flash region
// However, for this demo and for simplicity, we use a dummy persister that does nothing
let persist = stack
.create_persist_with_comm_window(DummyKvBlobStore)
.await
.unwrap();
// == Step 4: ==
// Run the Matter stack with our handler
// Using `pin!` is completely optional, but reduces the size of the final future
//
// This step can be repeated in that the stack can be stopped and started multiple times, as needed.
let matter = pin!(stack.run_coex(
// The Matter stack needs to instantiate an `embassy-net` `Driver` and `Controller`
EmbassyWifi::new(
EspWifiDriver::new(&init, peripherals.WIFI, peripherals.BT),
stack
),
// The Matter stack needs a persister to store its state
&persist,
// Our `AsyncHandler` + `AsyncMetadata` impl
(NODE, handler),
// No user future to run
(),
));
// Run Matter
matter.await.unwrap();
}
/// 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 = Node {
id: 0,
endpoints: &[
EmbassyWifiMatterStack::<0, ()>::root_endpoint(),
Endpoint {
id: LIGHT_ENDPOINT_ID,
device_types: devices!(DEV_TYPE_ON_OFF_LIGHT),
clusters: clusters!(desc::DescHandler::CLUSTER, TestOnOffDeviceLogic::CLUSTER),
},
],
};- Device Attestation data support using secure flash storage
- Setting system time via Matter
- Matter OTA support