pub struct ThreadDriver<'d, T>where
T: Mode,{ /* private fields */ }Expand description
This struct provides a safe wrapper over the ESP IDF Thread C driver.
The driver works on Layer 2 (Data Link) in the OSI model, in that it provides facilities for sending and receiving ethernet packets over the Thread radio.
For most use cases, utilizing EspThread - which provides a networking (IP)
layer as well - should be preferred. Using ThreadDriver directly is beneficial
only when one would like to utilize a custom, non-STD network stack like smoltcp.
The driver can work in two modes:
- RCP (Radio Co-Processor) mode: The driver operates as a co-processor to the host, which is expected to be another chip connected to ours via SPI or UART. This is of course only supported with MCUs that do have a Thread radio, like esp32c2 and esp32c6
- Host mode: The driver operates as a host, and if the chip does not have a Thread radio it has to be connected via SPI or USB to a chip which runs the Thread stack in RCP mode
Implementations§
Source§impl<T> ThreadDriver<'_, T>where
T: Mode,
impl<T> ThreadDriver<'_, T>where
T: Mode,
Sourcepub fn srp_conf<F, R>(&self, f: F) -> Result<R, EspError>
pub fn srp_conf<F, R>(&self, f: F) -> Result<R, EspError>
Return the current SRP client configuration and SRP client host state to the provided closure.
Arguments:
f: A closure that takes the SRP configuration and SRP host state as arguments.
Sourcepub fn srp_is_empty(&self) -> Result<bool, EspError>
pub fn srp_is_empty(&self) -> Result<bool, EspError>
Return true if there is neither host, nor any service currently registered with the SRP client.
Sourcepub fn srp_set_conf(&self, conf: &SrpConf<'_>) -> Result<(), EspError>
pub fn srp_set_conf(&self, conf: &SrpConf<'_>) -> Result<(), EspError>
Set the SRP client configuration.
Arguments:
conf: The SRP configuration.
Returns:
Ok(())if the configuration was set successfully.Err(OtError)if the configuration could not be set. One reason why the configuration setting might fail is if the configuration had already been set and then not removed withsrp_remove_all.
Sourcepub fn srp_running(&self) -> Result<bool, EspError>
pub fn srp_running(&self) -> Result<bool, EspError>
Return true if the SRP client is running, false otherwise.
Sourcepub fn srp_autostart_enabled(&self) -> Result<bool, EspError>
pub fn srp_autostart_enabled(&self) -> Result<bool, EspError>
Return true if the SRP client is in auto-start mode, false otherwise.
Sourcepub fn srp_autostart(&self) -> Result<(), EspError>
pub fn srp_autostart(&self) -> Result<(), EspError>
Auto-starts the SRP client.
Sourcepub fn srp_start(&self, server_addr: SocketAddrV6) -> Result<(), EspError>
pub fn srp_start(&self, server_addr: SocketAddrV6) -> Result<(), EspError>
Start the SRP client for the given SRP server address.
Arguments:
server_addr: The SRP server address.
Sourcepub fn srp_server_addr(&self) -> Result<Option<SocketAddrV6>, EspError>
pub fn srp_server_addr(&self) -> Result<Option<SocketAddrV6>, EspError>
Return the SRP server address, if the SRP client is running and had connected to a server.
Sourcepub fn srp_services<F>(&self, f: F) -> Result<(), EspError>
pub fn srp_services<F>(&self, f: F) -> Result<(), EspError>
Iterate over the SRP services registered with the SRP client.
Arguments:
f: A closure that receives a tuple of the next SRP service, SRP service state, and SRP service ID. If there are no more SRP services, the closure will receiveNone.
Sourcepub fn srp_add_service<'a, SI, TI>(
&self,
service: &'a SrpService<'a, SI, TI>,
) -> Result<SrpServiceSlot, EspError>where
SI: Iterator<Item = &'a str> + Clone + 'a,
TI: Iterator<Item = (&'a str, &'a [u8])> + Clone + 'a,
pub fn srp_add_service<'a, SI, TI>(
&self,
service: &'a SrpService<'a, SI, TI>,
) -> Result<SrpServiceSlot, EspError>where
SI: Iterator<Item = &'a str> + Clone + 'a,
TI: Iterator<Item = (&'a str, &'a [u8])> + Clone + 'a,
Add an SRP service to the SRP client.
Arguments:
service: The SRP service to add.
Returns:
- The SRP service slot, if the service was added successfully.
Err(OtError)if the service could not be added. One reason why the service addition might fail is if there are no more slots available for services. This can happen even if all services had been removed, as the slots are not freed until the SRP client propagates the removal info to the SRP server.
Sourcepub fn srp_remove_service(
&self,
slot: SrpServiceSlot,
immediate: bool,
) -> Result<(), EspError>
pub fn srp_remove_service( &self, slot: SrpServiceSlot, immediate: bool, ) -> Result<(), EspError>
Remove an SRP service from the SRP client.
Arguments:
slot: The SRP service to remove.immediate: Iftrue, the service will be removed immediately, otherwise, the service will be removed gracefully by propagating the removal info to the SRP server.
Sourcepub fn srp_remove_all(&self, immediate: bool) -> Result<(), EspError>
pub fn srp_remove_all(&self, immediate: bool) -> Result<(), EspError>
Remove the SRP hostname and all SRP services from the SRP client.
Arguments:
immediate: Iftrue, the hostname and services will be removed immediately, otherwise, the hostname and services will be removed gracefully by propagating the removal info to the SRP server.
Source§impl<'d> ThreadDriver<'d, Host>
impl<'d> ThreadDriver<'d, Host>
Sourcepub fn new_spi<S: Spi + 'd>(
spi: S,
mosi: impl InputPin + 'd,
miso: impl OutputPin + 'd,
sclk: impl InputPin + OutputPin + 'd,
cs: Option<impl InputPin + OutputPin + 'd>,
intr: Option<impl InputPin + OutputPin + 'd>,
config: &Config,
sysloop: EspSystemEventLoop,
nvs: EspDefaultNvsPartition,
mounted_event_fs: Arc<MountedEventfs>,
) -> Result<Self, EspError>
pub fn new_spi<S: Spi + 'd>( spi: S, mosi: impl InputPin + 'd, miso: impl OutputPin + 'd, sclk: impl InputPin + OutputPin + 'd, cs: Option<impl InputPin + OutputPin + 'd>, intr: Option<impl InputPin + OutputPin + 'd>, config: &Config, sysloop: EspSystemEventLoop, nvs: EspDefaultNvsPartition, mounted_event_fs: Arc<MountedEventfs>, ) -> Result<Self, EspError>
Create a new Thread Host driver instance utilizing an SPI connection to another MCU running the Thread stack in RCP mode.
Sourcepub fn new_uart<U: Uart + 'd>(
uart: U,
tx: impl OutputPin + 'd,
rx: impl InputPin + 'd,
config: &Config,
sysloop: EspSystemEventLoop,
nvs: EspDefaultNvsPartition,
mounted_event_fs: Arc<MountedEventfs>,
) -> Result<Self, EspError>
pub fn new_uart<U: Uart + 'd>( uart: U, tx: impl OutputPin + 'd, rx: impl InputPin + 'd, config: &Config, sysloop: EspSystemEventLoop, nvs: EspDefaultNvsPartition, mounted_event_fs: Arc<MountedEventfs>, ) -> Result<Self, EspError>
Create a new Thread Host driver instance utilizing a UART connection to another MCU running the Thread stack in RCP mode.
Sourcepub fn enable_ipv6(&self, enable: bool) -> Result<(), EspError>
pub fn enable_ipv6(&self, enable: bool) -> Result<(), EspError>
Enable or disable the network interface of the Thread driver
Sourcepub fn enable_thread(&self, enable: bool) -> Result<(), EspError>
pub fn enable_thread(&self, enable: bool) -> Result<(), EspError>
Enable or disable Thread
When enabling, this should be called after the network interface is enabled
Sourcepub fn role(&self) -> Result<Role, EspError>
pub fn role(&self) -> Result<Role, EspError>
Retrieve the current role of the device in the Thread network
Sourcepub fn init_cli(&mut self) -> Result<(), EspError>
pub fn init_cli(&mut self) -> Result<(), EspError>
Initialize the Thread command-line interface (CLI) for debugging purposes.
NOTE: This function can only be called once.
Sourcepub fn tod(&self, buf: &mut [u8]) -> Result<usize, EspError>
pub fn tod(&self, buf: &mut [u8]) -> Result<usize, EspError>
Retrieve the active TOD (Thread Operational Dataset) in the user-supplied buffer
Return the size of the TOD data written to the buffer
The TOD is in Thread TLV format.
Sourcepub fn pending_tod(&self, buf: &mut [u8]) -> Result<usize, EspError>
pub fn pending_tod(&self, buf: &mut [u8]) -> Result<usize, EspError>
Retrieve the pending TOD (Thread Operational Dataset) in the user-supplied buffer
Return the size of the TOD data written to the buffer
The TOD is in Thread TLV format.
Sourcepub fn set_tod(&self, tod: &[u8]) -> Result<(), EspError>
pub fn set_tod(&self, tod: &[u8]) -> Result<(), EspError>
Set the active TOD (Thread Operational Dataset) to the provided data
The TOD data should be in Thread TLV format.
Sourcepub fn set_pending_tod(&self, tod: &[u8]) -> Result<(), EspError>
pub fn set_pending_tod(&self, tod: &[u8]) -> Result<(), EspError>
Set the pending TOD (Thread Operational Dataset) to the provided data
The TOD data should be in Thread TLV format.
Sourcepub fn set_tod_hexstr(&self, tod: &str) -> Result<(), EspError>
pub fn set_tod_hexstr(&self, tod: &str) -> Result<(), EspError>
Set the active TOD (Thread Operational Dataset) to the provided data
The TOD data should be in Thread TLV format.
Sourcepub fn set_pending_tod_hexstr(&self, tod: &str) -> Result<(), EspError>
pub fn set_pending_tod_hexstr(&self, tod: &str) -> Result<(), EspError>
Set the pending TOD (Thread Operational Dataset) to the provided data
The TOD data should be in Thread TLV format.
Sourcepub fn set_tod_from_cfg(&self) -> Result<(), EspError>
pub fn set_tod_from_cfg(&self) -> Result<(), EspError>
Set the active TOD (Thread Operational Dataset) according to the
CONFIG_OPENTHREAD_ TOD-related parameters compiled into the app
during build (via sdkconfig*)
Sourcepub fn scan<F: FnMut(Option<ActiveScanResult<'_>>) + Send + 'static>(
&self,
callback: F,
) -> Result<(), EspError>
pub fn scan<F: FnMut(Option<ActiveScanResult<'_>>) + Send + 'static>( &self, callback: F, ) -> Result<(), EspError>
Perform an active scan for Thread networks
The callback will be called for each found network
At the end of the scan, the callback will be called with None
Sourcepub fn is_scan_in_progress(&self) -> Result<bool, EspError>
pub fn is_scan_in_progress(&self) -> Result<bool, EspError>
Check if an active scan is in progress
Sourcepub fn energy_scan<F: FnMut(Option<EnergyScanResult<'_>>) + Send + 'static>(
&self,
callback: F,
) -> Result<(), EspError>
pub fn energy_scan<F: FnMut(Option<EnergyScanResult<'_>>) + Send + 'static>( &self, callback: F, ) -> Result<(), EspError>
Perform an energy scan for Thread networks
The callback will be called for each found network
At the end of the scan, the callback will be called with None
Sourcepub fn is_energy_scan_in_progress(&self) -> Result<bool, EspError>
pub fn is_energy_scan_in_progress(&self) -> Result<bool, EspError>
Check if an energy scan is in progress
Sourcepub fn set_rx_callback<R>(&self, callback: Option<R>) -> Result<(), EspError>where
R: FnMut(Ipv6Incoming<'_>) + Send + 'static,
pub fn set_rx_callback<R>(&self, callback: Option<R>) -> Result<(), EspError>where
R: FnMut(Ipv6Incoming<'_>) + Send + 'static,
Set a callback function for receiving Ipv6 raw packets from Thread
Sourcepub fn set_nonstatic_rx_callback<R>(
&self,
callback: Option<R>,
) -> Result<(), EspError>where
R: FnMut(Ipv6Incoming<'_>) + Send + 'd,
pub fn set_nonstatic_rx_callback<R>(
&self,
callback: Option<R>,
) -> Result<(), EspError>where
R: FnMut(Ipv6Incoming<'_>) + Send + 'd,
Set a callback function for receiving Ipv6 raw packets from Thread
§Safety
This method - in contrast to method set_rx_callback - allows the user to pass
non-static callback/closure. This enables users to borrow
- in the closure - variables that live on the stack - or more generally - in the same scope where the service is created.
HOWEVER: care should be taken NOT to call core::mem::forget() on the service,
as that would immediately lead to an UB (crash).
Also note that forgetting the service might happen with Rc and Arc
when circular references are introduced: https://github.com/rust-lang/rust/issues/24456
The reason is that the closure is actually sent to a hidden ESP IDF thread. This means that if the service is forgotten, Rust is free to e.g. unwind the stack and the closure now owned by this other thread will end up with references to variables that no longer exist.
The destructor of the service takes care - prior to the service being dropped and e.g. the stack being unwind - to remove the closure from the hidden thread and destroy it. Unfortunately, when the service is forgotten, the un-subscription does not happen and invalid references are left dangling.
This “local borrowing” will only be possible to express in a safe way once/if !Leak types
are introduced to Rust (i.e. the impossibility to “forget” a type and thus not call its destructor).
Source§impl<T> ThreadDriver<'_, T>where
T: Mode,
impl<T> ThreadDriver<'_, T>where
T: Mode,
Sourcepub fn start(&mut self) -> Result<(), EspError>
pub fn start(&mut self) -> Result<(), EspError>
Start the Thread driver
If the driver is already started, an error is returned.
Sourcepub fn stop(&mut self) -> Result<(), EspError>
pub fn stop(&mut self) -> Result<(), EspError>
Stop the Thread driver
If the driver is not started, an error is returned.
Sourcepub fn is_started(&self) -> Result<bool, EspError>
pub fn is_started(&self) -> Result<bool, EspError>
Check if the Thread driver is started