Skip to content

Commit

Permalink
Docu, mDNS type
Browse files Browse the repository at this point in the history
  • Loading branch information
ivmarkov committed May 3, 2024
1 parent a45d066 commit 022d99e
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 70 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ Users are expected to provide implementations for various `rs-matter` abstractio

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 addresses these issues by providing an all-in-one [`MatterStack`](https://github.com/ivmarkov/esp-idf-matter/blob/master/src/stack.rs#L57) assembly that configures `rs-matter` for reliably operating on top of the ESP IDF SDK.
**This crate addresses these issues by providing an all-in-one [`MatterStack`](https://github.com/ivmarkov/esp-idf-matter/blob/master/src/stack.rs#L57) assembly that configures `rs-matter` for reliably 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,
//! 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 you want to use Ethernet, utilize `MatterStack<Eth>` instead.
//! If you want to use Ethernet, utilize `MatterStack<Eth, ..>` instead.
//!
//! The example implements a fictitious Light device (an On-Off Matter cluster).

Expand All @@ -32,7 +32,7 @@ use core::pin::pin;
use embassy_futures::select::select;
use embassy_time::{Duration, Timer};

use esp_idf_matter::{init_async_io, Error, MatterStack, WifiBle};
use esp_idf_matter::{init_async_io, BuiltinMdns, Error, MatterStack, WifiBle};

use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripherals::Peripherals;
Expand Down Expand Up @@ -172,7 +172,7 @@ async fn matter() -> 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<MatterStack<WifiBle>> =
static MATTER_STACK: ConstStaticCell<MatterStack<WifiBle, BuiltinMdns>> =
ConstStaticCell::new(MatterStack::new(
&BasicInfoConfig {
vid: 0xFFF1,
Expand All @@ -196,7 +196,7 @@ const LIGHT_ENDPOINT_ID: u16 = 1;
const NODE: Node<'static> = Node {
id: 0,
endpoints: &[
MatterStack::<WifiBle>::root_metadata(),
MatterStack::<WifiBle, BuiltinMdns>::root_metadata(),
Endpoint {
id: LIGHT_ENDPOINT_ID,
device_type: DEV_TYPE_ON_OFF_LIGHT,
Expand Down
13 changes: 7 additions & 6 deletions examples/light.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! An example utilizing the `MatterStack<WifiBle>` struct.
//! An example utilizing the `WifiBleMatterStack` struct.
//! As the name suggests, this Matter stack assembly uses Wifi as the main transport,
//! and BLE for commissioning.
//! If you want to use Ethernet, utilize `MatterStack<Eth>` instead.
//! If you want to use Ethernet, utilize `EthMatterStack` instead.
//!
//! The example implements a fictitious Light device (an On-Off Matter cluster).
Expand All @@ -11,7 +11,7 @@ use core::pin::pin;
use embassy_futures::select::select;
use embassy_time::{Duration, Timer};

use esp_idf_matter::{init_async_io, Error, MatterStack, WifiBle};
use esp_idf_matter::{init_async_io, Error, MdnsType, WifiBleMatterStack};

use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripherals::Peripherals;
Expand Down Expand Up @@ -151,8 +151,8 @@ async fn matter() -> 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<MatterStack<WifiBle>> =
ConstStaticCell::new(MatterStack::new(
static MATTER_STACK: ConstStaticCell<WifiBleMatterStack> =
ConstStaticCell::new(WifiBleMatterStack::new(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8000,
Expand All @@ -165,6 +165,7 @@ static MATTER_STACK: ConstStaticCell<MatterStack<WifiBle>> =
vendor_name: "ACME",
},
&dev_att::HardCodedDevAtt::new(),
MdnsType::default(),
));

/// Endpoint 0 (the root endpoint) always runs
Expand All @@ -175,7 +176,7 @@ const LIGHT_ENDPOINT_ID: u16 = 1;
const NODE: Node<'static> = Node {
id: 0,
endpoints: &[
MatterStack::<WifiBle>::root_metadata(),
WifiBleMatterStack::root_metadata(),
Endpoint {
id: LIGHT_ENDPOINT_ID,
device_type: DEV_TYPE_ON_OFF_LIGHT,
Expand Down
9 changes: 5 additions & 4 deletions examples/light_eth.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! An example utilizing the `MatterStack<Eth>` struct.
//! An example utilizing the `EthMatterStack` 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.
Expand All @@ -13,7 +13,7 @@ use core::pin::pin;
use embassy_futures::select::select;
use embassy_time::{Duration, Timer};

use esp_idf_matter::{init_async_io, Error, Eth, MatterStack, WifiBle};
use esp_idf_matter::{init_async_io, Error, EthMatterStack, MdnsType};

use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripherals::Peripherals;
Expand Down Expand Up @@ -180,7 +180,7 @@ async fn matter() -> Result<(), Error> {

/// The Matter stack is allocated statically to avoid
/// program stack blowups.
static MATTER_STACK: ConstStaticCell<MatterStack<Eth>> = ConstStaticCell::new(MatterStack::new(
static MATTER_STACK: ConstStaticCell<EthMatterStack> = ConstStaticCell::new(EthMatterStack::new(
&BasicInfoConfig {
vid: 0xFFF1,
pid: 0x8000,
Expand All @@ -193,6 +193,7 @@ static MATTER_STACK: ConstStaticCell<MatterStack<Eth>> = ConstStaticCell::new(Ma
vendor_name: "ACME",
},
&dev_att::HardCodedDevAtt::new(),
MdnsType::default(),
));

/// Endpoint 0 (the root endpoint) always runs
Expand All @@ -203,7 +204,7 @@ const LIGHT_ENDPOINT_ID: u16 = 1;
const NODE: Node<'static> = Node {
id: 0,
endpoints: &[
MatterStack::<WifiBle>::root_metadata(),
EthMatterStack::root_metadata(),
Endpoint {
id: LIGHT_ENDPOINT_ID,
device_type: DEV_TYPE_ON_OFF_LIGHT,
Expand Down
35 changes: 31 additions & 4 deletions src/ble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,17 @@ struct IndBuffer {
data: heapless::Vec<u8, MAX_MTU_SIZE>,
}

/// The `'static` state of the `BtpGattPeripheral` struct.
/// Isolated as a separate struct to allow for `const fn` construction
/// and static allocation.
pub struct BtpGattContext {
state: Mutex<EspRawMutex, RefCell<State>>,
ind: IfMutex<EspRawMutex, IndBuffer>,
ind_in_flight: Signal<EspRawMutex, bool>,
}

impl BtpGattContext {
/// Create a new instance.
pub const fn new() -> Self {
Self {
state: Mutex::new(RefCell::new(State {
Expand All @@ -90,7 +94,7 @@ impl BtpGattContext {
}
}

pub fn reset(&self) {
pub(crate) fn reset(&self) -> Result<(), Error> {
self.state.lock(|state| {
let mut state = state.borrow_mut();

Expand All @@ -100,6 +104,17 @@ impl BtpGattContext {
state.c2_handle = None;
state.c2_cccd_handle = None;
});

self.ind_in_flight.modify(|ind_inf_flight| {
*ind_inf_flight = false;
(false, ())
});

self.ind.try_lock().map(|mut ind| {
ind.data.clear();
})?;

Ok(())
}
}

Expand All @@ -109,6 +124,8 @@ impl Default for BtpGattContext {
}
}

/// A GATT peripheral implementation for the BTP protocol in `rs-matter`.
/// Implements the `GattPeripheral` trait.
pub struct BtpGattPeripheral<'a, 'd, M>
where
M: BleEnabled,
Expand All @@ -123,14 +140,24 @@ where
M: BleEnabled,
{
/// Create a new instance.
pub fn new(app_id: u16, driver: &'a BtDriver<'d, M>, context: &'a BtpGattContext) -> Self {
Self {
///
/// Creation might fail if the GATT context cannot be reset, so user should ensure
/// that there are no other GATT peripherals running before calling this function.
pub fn new(
app_id: u16,
driver: &'a BtDriver<'d, M>,
context: &'a BtpGattContext,
) -> Result<Self, Error> {
context.reset()?;

Ok(Self {
app_id,
driver,
context,
}
})
}

/// Run the GATT peripheral.
pub async fn run<F>(
&self,
service_name: &str,
Expand Down
17 changes: 17 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
use core::fmt::{self, Display};

use embassy_sync::mutex::TryLockError;
use esp_idf_svc::sys::EspError;

/// The error used throughout this crate.
/// A composition of `rs_matter::error::Error` and `EspError`.
#[derive(Debug)]
pub enum Error {
Matter(rs_matter::error::Error),
Esp(EspError),
InvalidState,
}

impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Matter(e) => write!(f, "Matter error: {}", e),
Error::Esp(e) => write!(f, "ESP error: {}", e),
Error::InvalidState => write!(f, "Invalid state"),
}
}
}
Expand All @@ -38,6 +43,18 @@ impl From<EspError> for Error {
}
}

impl From<TryLockError> for Error {
fn from(_: TryLockError) -> Self {
Error::InvalidState
}
}

impl From<rs_matter::utils::ifmutex::TryLockError> for Error {
fn from(_: rs_matter::utils::ifmutex::TryLockError) -> Self {
Error::InvalidState
}
}

#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Expand Down
5 changes: 5 additions & 0 deletions src/mdns.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#![cfg(any(esp_idf_comp_mdns_enabled, esp_idf_comp_espressif__mdns_enabled))]

// /// A type representing the ESP IDF mDNS service.
// pub struct EspIdfMdns(());

// impl MdnsType for EspIdfMdns {}

// TODO
2 changes: 2 additions & 0 deletions src/multicast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use log::info;

use rs_matter::error::{Error, ErrorCode};

/// Join an IPV6 multicast group on a specific interface
pub fn join_multicast_v6(
socket: &Async<UdpSocket>,
multiaddr: Ipv6Addr,
Expand All @@ -22,6 +23,7 @@ pub fn join_multicast_v6(
Ok(())
}

/// Join an IPV4 multicast group on a specific interface
pub fn join_multicast_v4(
socket: &Async<UdpSocket>,
multiaddr: Ipv4Addr,
Expand Down
23 changes: 22 additions & 1 deletion src/netif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,28 @@ use rs_matter::utils::notification::Notification;

use crate::error::Error;

const TIMEOUT_PERIOD_SECS: u8 = 5;

/// Async trait for accessing the `EspNetif` network interface (netif) of a driver.
///
/// Allows sharing the network interface between multiple tasks, where one task
/// may be waiting for the network interface to be ready, while the other might
/// be mutable operating on the L2 driver below the netif, or on the netif itself.
pub trait NetifAccess {
/// Waits until the network interface is available and then
/// calls the provided closure with a reference to the network interface.
async fn with_netif<F, R>(&self, f: F) -> R
where
F: FnOnce(&EspNetif) -> R;

/// Waits until a certain condition `f` becomes `Some` for a network interface
/// and then returns the result.
///
/// The condition is checked every 5 seconds and every time a new `IpEvent` is
/// generated on the ESP IDF system event loop.
///
/// The main use case of this method is to wait and listen the netif for changes
/// (netif up/down, IP address changes, etc.)
async fn wait<F, R>(&self, sysloop: EspSystemEventLoop, mut f: F) -> Result<R, Error>
where
F: FnMut(&EspNetif) -> Result<Option<R>, Error>,
Expand All @@ -45,7 +62,7 @@ pub trait NetifAccess {
}

let mut events = pin!(notification.wait());
let mut timer = pin!(Timer::after(Duration::from_secs(5)));
let mut timer = pin!(Timer::after(Duration::from_secs(TIMEOUT_PERIOD_SECS as _)));

select(&mut events, &mut timer).await;
}
Expand Down Expand Up @@ -100,6 +117,9 @@ where
}
}

/// Get the IP addresses and the interface index of the network interface.
///
/// Return an error when some of the IP addresses are unspecified.
pub fn get_ips(netif: &EspNetif) -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
let ip_info = netif.get_ip_info()?;

Expand Down Expand Up @@ -137,6 +157,7 @@ pub fn get_ips(netif: &EspNetif) -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
Ok((ipv4, ipv6, interface))
}

/// Implementation of `NetifAccess` for the `EspEth` and `AsyncEth` drivers.
#[cfg(esp_idf_comp_esp_eth_enabled)]
#[cfg(any(
all(esp32, esp_idf_eth_use_esp32_emac),
Expand Down
Loading

0 comments on commit 022d99e

Please sign in to comment.