use alloc::{boxed::Box, collections::vec_deque::VecDeque};
use core::{
fmt::Debug,
marker::PhantomData,
task::{Context, Poll},
};
use docsplay::Display;
use esp_hal::time::Duration;
use esp_sync::NonReentrantMutex;
use portable_atomic::{AtomicBool, AtomicU8, Ordering};
use super::*;
#[cfg(feature = "csi")]
use crate::wifi::csi::CsiConfig;
use crate::{
asynch::AtomicWaker,
sys::include::*,
wifi::{RxControlInfo, WifiError},
};
const RECEIVE_QUEUE_SIZE: usize = 10;
pub const ESP_NOW_MAX_DATA_LEN: usize = 250;
pub const BROADCAST_ADDRESS: [u8; 6] = [0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8];
struct EspNowState {
rx_queue: VecDeque<ReceivedData>,
}
static STATE: NonReentrantMutex<EspNowState> = NonReentrantMutex::new(EspNowState {
rx_queue: VecDeque::new(),
});
static ESP_NOW_SEND_CB_INVOKED: AtomicBool = AtomicBool::new(false);
static ESP_NOW_SEND_STATUS: AtomicBool = AtomicBool::new(true);
static ESP_NOW_TX_WAKER: AtomicWaker = AtomicWaker::new();
static ESP_NOW_RX_WAKER: AtomicWaker = AtomicWaker::new();
macro_rules! check_error {
($block:block) => {
match unsafe { $block } {
0 => Ok(()),
res => Err(EspNowError::Error(Error::from_code(res as u32))),
}
};
}
macro_rules! check_error_expect {
($block:block, $msg:literal) => {
match unsafe { $block } {
0 => (),
res => panic!(
"{}: {:?}",
$msg,
EspNowError::Error(Error::from_code(res as u32))
),
}
};
}
#[repr(u32)]
#[derive(Display, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum Error {
NotInitialized = 12389,
InvalidArgument = 12390,
OutOfMemory = 12391,
PeerListFull = 12392,
NotFound = 12393,
Internal = 12394,
PeerExists = 12395,
InterfaceMismatch = 12396,
Other(u32),
}
impl Error {
fn from_code(code: u32) -> Error {
match code {
12389 => Error::NotInitialized,
12390 => Error::InvalidArgument,
12391 => Error::OutOfMemory,
12392 => Error::PeerListFull,
12393 => Error::NotFound,
12394 => Error::Internal,
12395 => Error::PeerExists,
12396 => Error::InterfaceMismatch,
_ => Error::Other(code),
}
}
}
impl core::error::Error for Error {}
#[derive(Display, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum EspNowError {
Error(Error),
SendFailed,
DuplicateInstance,
Initialization(WifiError),
}
impl core::error::Error for EspNowError {}
impl From<WifiError> for EspNowError {
fn from(f: WifiError) -> Self {
Self::Initialization(f)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct PeerCount {
pub total_count: i32,
pub encrypted_count: i32,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum WifiPhyRate {
Rate1mL = 0,
Rate2m,
Rate5mL,
Rate11mL,
Rate2mS,
Rate5mS,
Rate11mS,
Rate48m,
Rate24m,
Rate12m,
Rate6m,
Rate54m,
Rate36m,
Rate18m,
Rate9m,
RateMcs0Lgi,
RateMcs1Lgi,
RateMcs2Lgi,
RateMcs3Lgi,
RateMcs4Lgi,
RateMcs5Lgi,
RateMcs6Lgi,
RateMcs7Lgi,
RateMcs0Sgi,
RateMcs1Sgi,
RateMcs2Sgi,
RateMcs3Sgi,
RateMcs4Sgi,
RateMcs5Sgi,
RateMcs6Sgi,
RateMcs7Sgi,
RateLora250k,
RateLora500k,
RateMax,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct PeerInfo {
pub interface: EspNowWifiInterface,
pub peer_address: [u8; 6],
pub lmk: Option<[u8; 16]>,
pub channel: Option<u8>,
pub encrypt: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct ReceiveInfo {
pub src_address: [u8; 6],
pub dst_address: [u8; 6],
pub rx_control: RxControlInfo,
}
#[derive(Clone)]
#[instability::unstable]
pub struct ReceivedData {
data: Box<[u8]>,
pub info: ReceiveInfo,
}
impl ReceivedData {
#[instability::unstable]
pub fn data(&self) -> &[u8] {
&self.data
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for ReceivedData {
fn format(&self, fmt: defmt::Formatter<'_>) {
defmt::write!(fmt, "ReceivedData {}, Info {}", &self.data[..], &self.info,)
}
}
impl Debug for ReceivedData {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ReceivedData")
.field("data", &self.data())
.field("info", &self.info)
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum EspNowWifiInterface {
AccessPoint,
Station,
}
impl EspNowWifiInterface {
fn as_wifi_interface(&self) -> wifi_interface_t {
match self {
EspNowWifiInterface::AccessPoint => wifi_interface_t_WIFI_IF_AP,
EspNowWifiInterface::Station => wifi_interface_t_WIFI_IF_STA,
}
}
fn from_wifi_interface(interface: wifi_interface_t) -> Self {
#[allow(non_upper_case_globals)]
match interface {
wifi_interface_t_WIFI_IF_AP => EspNowWifiInterface::AccessPoint,
wifi_interface_t_WIFI_IF_STA => EspNowWifiInterface::Station,
wifi_interface_t_WIFI_IF_NAN => panic!("NAN is unsupported"),
_ => unreachable!("Unknown interface"),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct EspNowManager<'d> {
_rc: EspNowRc<'d>,
}
impl EspNowManager<'_> {
#[instability::unstable]
pub fn set_channel(&self, channel: u8) -> Result<(), EspNowError> {
check_error!({ esp_wifi_set_channel(channel, 0) })
}
#[instability::unstable]
pub fn version(&self) -> Result<u32, EspNowError> {
let mut version = 0u32;
check_error!({ esp_now_get_version(&mut version as *mut u32) })?;
Ok(version)
}
#[instability::unstable]
pub fn add_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
let raw_peer = esp_now_peer_info_t {
peer_addr: peer.peer_address,
lmk: peer.lmk.unwrap_or([0u8; 16]),
channel: peer.channel.unwrap_or(0),
ifidx: peer.interface.as_wifi_interface(),
encrypt: peer.encrypt,
priv_: core::ptr::null_mut(),
};
check_error!({ esp_now_add_peer(&raw_peer as *const _) })
}
#[cfg(feature = "csi")]
#[instability::unstable]
pub fn set_csi(
&mut self,
mut csi: CsiConfig,
cb: impl FnMut(crate::wifi::csi::WifiCsiInfo<'_>) + Send,
) -> Result<(), WifiError> {
csi.apply_config()?;
csi.set_receive_cb(cb)?;
csi.set_csi(true)?;
Ok(())
}
#[instability::unstable]
pub fn remove_peer(&self, peer_address: &[u8; 6]) -> Result<(), EspNowError> {
check_error!({ esp_now_del_peer(peer_address.as_ptr()) })
}
#[instability::unstable]
pub fn modify_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
let raw_peer = esp_now_peer_info_t {
peer_addr: peer.peer_address,
lmk: peer.lmk.unwrap_or([0u8; 16]),
channel: peer.channel.unwrap_or(0),
ifidx: peer.interface.as_wifi_interface(),
encrypt: peer.encrypt,
priv_: core::ptr::null_mut(),
};
check_error!({ esp_now_mod_peer(&raw_peer as *const _) })
}
#[instability::unstable]
pub fn peer(&self, peer_address: &[u8; 6]) -> Result<PeerInfo, EspNowError> {
let mut raw_peer = esp_now_peer_info_t {
peer_addr: [0u8; 6],
lmk: [0u8; 16],
channel: 0,
ifidx: 0,
encrypt: false,
priv_: core::ptr::null_mut(),
};
check_error!({ esp_now_get_peer(peer_address.as_ptr(), &mut raw_peer as *mut _) })?;
Ok(PeerInfo {
interface: EspNowWifiInterface::from_wifi_interface(raw_peer.ifidx),
peer_address: raw_peer.peer_addr,
lmk: if raw_peer.lmk.is_empty() {
None
} else {
Some(raw_peer.lmk)
},
channel: if raw_peer.channel != 0 {
Some(raw_peer.channel)
} else {
None
},
encrypt: raw_peer.encrypt,
})
}
#[instability::unstable]
pub fn fetch_peer(&self, from_head: bool) -> Result<PeerInfo, EspNowError> {
let mut raw_peer = esp_now_peer_info_t {
peer_addr: [0u8; 6],
lmk: [0u8; 16],
channel: 0,
ifidx: 0,
encrypt: false,
priv_: core::ptr::null_mut(),
};
check_error!({ esp_now_fetch_peer(from_head, &mut raw_peer as *mut _) })?;
Ok(PeerInfo {
interface: EspNowWifiInterface::from_wifi_interface(raw_peer.ifidx),
peer_address: raw_peer.peer_addr,
lmk: if raw_peer.lmk.is_empty() {
None
} else {
Some(raw_peer.lmk)
},
channel: if raw_peer.channel != 0 {
Some(raw_peer.channel)
} else {
None
},
encrypt: raw_peer.encrypt,
})
}
#[instability::unstable]
pub fn peer_exists(&self, peer_address: &[u8; 6]) -> bool {
unsafe { esp_now_is_peer_exist(peer_address.as_ptr()) }
}
#[instability::unstable]
pub fn peer_count(&self) -> Result<PeerCount, EspNowError> {
let mut peer_num = esp_now_peer_num_t {
total_num: 0,
encrypt_num: 0,
};
check_error!({ esp_now_get_peer_num(&mut peer_num as *mut _) })?;
Ok(PeerCount {
total_count: peer_num.total_num,
encrypted_count: peer_num.encrypt_num,
})
}
#[instability::unstable]
pub fn set_pmk(&self, pmk: &[u8; 16]) -> Result<(), EspNowError> {
check_error!({ esp_now_set_pmk(pmk.as_ptr()) })
}
#[instability::unstable]
pub fn set_wake_window(&self, wake_window: Duration) -> Result<(), EspNowError> {
let ms = wake_window.as_millis();
if ms > u16::MAX as u64 {
return Err(EspNowError::Error(Error::InvalidArgument));
}
check_error!({ esp_now_set_wake_window(ms as u16) })
}
#[instability::unstable]
pub fn set_rate(&self, rate: WifiPhyRate) -> Result<(), EspNowError> {
check_error!({ esp_wifi_config_espnow_rate(wifi_interface_t_WIFI_IF_STA, rate as u32,) })
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct EspNowSender<'d> {
_rc: EspNowRc<'d>,
}
impl EspNowSender<'_> {
#[instability::unstable]
pub fn send<'s>(
&'s mut self,
dst_addr: &[u8; 6],
data: &[u8],
) -> Result<SendWaiter<'s>, EspNowError> {
ESP_NOW_SEND_CB_INVOKED.store(false, Ordering::Release);
check_error!({ esp_now_send(dst_addr.as_ptr(), data.as_ptr(), data.len()) })?;
Ok(SendWaiter(PhantomData))
}
}
#[allow(unknown_lints)]
#[allow(clippy::too_long_first_doc_paragraph)]
#[must_use]
#[instability::unstable]
pub struct SendWaiter<'s>(PhantomData<&'s mut EspNowSender<'s>>);
impl SendWaiter<'_> {
#[instability::unstable]
pub fn wait(self) -> Result<(), EspNowError> {
core::mem::forget(self);
while !ESP_NOW_SEND_CB_INVOKED.load(Ordering::Acquire) {}
if ESP_NOW_SEND_STATUS.load(Ordering::Relaxed) {
Ok(())
} else {
Err(EspNowError::SendFailed)
}
}
}
impl Drop for SendWaiter<'_> {
fn drop(&mut self) {
while !ESP_NOW_SEND_CB_INVOKED.load(Ordering::Acquire) {}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct EspNowReceiver<'d> {
_rc: EspNowRc<'d>,
}
impl EspNowReceiver<'_> {
#[instability::unstable]
pub fn receive(&self) -> Option<ReceivedData> {
STATE.with(|state| state.rx_queue.pop_front())
}
}
#[derive(Debug)]
struct EspNowRc<'d> {
rc: &'static AtomicU8,
inner: PhantomData<EspNow<'d>>,
}
#[cfg(feature = "defmt")]
impl defmt::Format for EspNowRc<'_> {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(
f,
"EspNowRc {{ rc: {}, inner: ... }}",
self.rc.load(Ordering::Relaxed)
);
}
}
impl EspNowRc<'_> {
fn new() -> Self {
static ESP_NOW_RC: AtomicU8 = AtomicU8::new(0);
assert!(ESP_NOW_RC.fetch_add(1, Ordering::AcqRel) == 0);
Self {
rc: &ESP_NOW_RC,
inner: PhantomData,
}
}
}
impl Clone for EspNowRc<'_> {
fn clone(&self) -> Self {
self.rc.fetch_add(1, Ordering::Release);
Self {
rc: self.rc,
inner: PhantomData,
}
}
}
impl Drop for EspNowRc<'_> {
fn drop(&mut self) {
if self.rc.fetch_sub(1, Ordering::AcqRel) == 1 {
unsafe {
esp_now_unregister_recv_cb();
esp_now_deinit();
}
}
}
}
#[allow(unknown_lints)]
#[allow(clippy::too_long_first_doc_paragraph)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct EspNow<'d> {
manager: EspNowManager<'d>,
sender: EspNowSender<'d>,
receiver: EspNowReceiver<'d>,
_phantom: PhantomData<&'d ()>,
}
impl<'d> EspNow<'d> {
pub(crate) fn new_internal() -> EspNow<'d> {
let espnow_rc = EspNowRc::new();
let esp_now = EspNow {
manager: EspNowManager {
_rc: espnow_rc.clone(),
},
sender: EspNowSender {
_rc: espnow_rc.clone(),
},
receiver: EspNowReceiver { _rc: espnow_rc },
_phantom: PhantomData,
};
check_error_expect!({ esp_now_init() }, "esp-now-init failed");
check_error_expect!(
{ esp_now_register_recv_cb(Some(rcv_cb)) },
"receiving callback failed"
);
check_error_expect!(
{ esp_now_register_send_cb(Some(send_cb)) },
"sending callback failed"
);
esp_now
.add_peer(PeerInfo {
interface: EspNowWifiInterface::Station,
peer_address: BROADCAST_ADDRESS,
lmk: None,
channel: None,
encrypt: false,
})
.expect("adding peer failed");
esp_now
}
#[instability::unstable]
pub fn split(self) -> (EspNowManager<'d>, EspNowSender<'d>, EspNowReceiver<'d>) {
(self.manager, self.sender, self.receiver)
}
#[instability::unstable]
pub fn set_channel(&self, channel: u8) -> Result<(), EspNowError> {
self.manager.set_channel(channel)
}
#[instability::unstable]
pub fn version(&self) -> Result<u32, EspNowError> {
self.manager.version()
}
#[instability::unstable]
pub fn add_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
self.manager.add_peer(peer)
}
#[instability::unstable]
pub fn remove_peer(&self, peer_address: &[u8; 6]) -> Result<(), EspNowError> {
self.manager.remove_peer(peer_address)
}
#[instability::unstable]
pub fn modify_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
self.manager.modify_peer(peer)
}
#[instability::unstable]
pub fn peer(&self, peer_address: &[u8; 6]) -> Result<PeerInfo, EspNowError> {
self.manager.peer(peer_address)
}
#[instability::unstable]
pub fn fetch_peer(&self, from_head: bool) -> Result<PeerInfo, EspNowError> {
self.manager.fetch_peer(from_head)
}
#[instability::unstable]
pub fn peer_exists(&self, peer_address: &[u8; 6]) -> bool {
self.manager.peer_exists(peer_address)
}
#[instability::unstable]
pub fn peer_count(&self) -> Result<PeerCount, EspNowError> {
self.manager.peer_count()
}
#[instability::unstable]
pub fn set_pmk(&self, pmk: &[u8; 16]) -> Result<(), EspNowError> {
self.manager.set_pmk(pmk)
}
#[instability::unstable]
pub fn set_wake_window(&self, wake_window: Duration) -> Result<(), EspNowError> {
self.manager.set_wake_window(wake_window)
}
#[instability::unstable]
pub fn set_rate(&self, rate: WifiPhyRate) -> Result<(), EspNowError> {
self.manager.set_rate(rate)
}
#[instability::unstable]
pub fn send<'s>(
&'s mut self,
dst_addr: &[u8; 6],
data: &[u8],
) -> Result<SendWaiter<'s>, EspNowError> {
self.sender.send(dst_addr, data)
}
#[instability::unstable]
pub fn receive(&self) -> Option<ReceivedData> {
self.receiver.receive()
}
}
unsafe extern "C" fn send_cb(_tx_info: *const esp_now_send_info_t, status: esp_now_send_status_t) {
let is_success = status == esp_now_send_status_t_ESP_NOW_SEND_SUCCESS;
ESP_NOW_SEND_STATUS.store(is_success, Ordering::Relaxed);
ESP_NOW_SEND_CB_INVOKED.store(true, Ordering::Release);
ESP_NOW_TX_WAKER.wake();
}
unsafe extern "C" fn rcv_cb(
esp_now_info: *const esp_now_recv_info_t,
data: *const u8,
data_len: i32,
) {
let src = unsafe {
[
(*esp_now_info).src_addr.offset(0).read(),
(*esp_now_info).src_addr.offset(1).read(),
(*esp_now_info).src_addr.offset(2).read(),
(*esp_now_info).src_addr.offset(3).read(),
(*esp_now_info).src_addr.offset(4).read(),
(*esp_now_info).src_addr.offset(5).read(),
]
};
let dst = unsafe {
[
(*esp_now_info).des_addr.offset(0).read(),
(*esp_now_info).des_addr.offset(1).read(),
(*esp_now_info).des_addr.offset(2).read(),
(*esp_now_info).des_addr.offset(3).read(),
(*esp_now_info).des_addr.offset(4).read(),
(*esp_now_info).des_addr.offset(5).read(),
]
};
let rx_cntl = unsafe { (*esp_now_info).rx_ctrl };
let rx_control = unsafe { RxControlInfo::from_raw(rx_cntl) };
let info = ReceiveInfo {
src_address: src,
dst_address: dst,
rx_control,
};
let slice = unsafe { core::slice::from_raw_parts(data, data_len as usize) };
STATE.with(|state| {
let data = Box::from(slice);
if state.rx_queue.len() >= RECEIVE_QUEUE_SIZE {
state.rx_queue.pop_front();
}
state.rx_queue.push_back(ReceivedData { data, info });
ESP_NOW_RX_WAKER.wake();
});
}
impl EspNowReceiver<'_> {
#[instability::unstable]
pub fn receive_async(&mut self) -> ReceiveFuture<'_> {
ReceiveFuture(PhantomData)
}
}
impl EspNowSender<'_> {
#[instability::unstable]
pub fn send_async<'s, 'r>(
&'s mut self,
addr: &'r [u8; 6],
data: &'r [u8],
) -> SendFuture<'s, 'r> {
SendFuture {
_sender: PhantomData,
addr,
data,
sent: false,
}
}
}
impl EspNow<'_> {
#[instability::unstable]
pub fn receive_async(&mut self) -> ReceiveFuture<'_> {
self.receiver.receive_async()
}
#[instability::unstable]
pub fn send_async<'s, 'r>(
&'s mut self,
dst_addr: &'r [u8; 6],
data: &'r [u8],
) -> SendFuture<'s, 'r> {
self.sender.send_async(dst_addr, data)
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[instability::unstable]
pub struct SendFuture<'s, 'r> {
_sender: PhantomData<&'s mut EspNowSender<'s>>,
addr: &'r [u8; 6],
data: &'r [u8],
sent: bool,
}
impl core::future::Future for SendFuture<'_, '_> {
type Output = Result<(), EspNowError>;
fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if !self.sent {
ESP_NOW_TX_WAKER.register(cx.waker());
ESP_NOW_SEND_CB_INVOKED.store(false, Ordering::Release);
if let Err(e) = check_error!({
esp_now_send(self.addr.as_ptr(), self.data.as_ptr(), self.data.len())
}) {
return Poll::Ready(Err(e));
}
self.sent = true;
}
if !ESP_NOW_SEND_CB_INVOKED.load(Ordering::Acquire) {
Poll::Pending
} else {
Poll::Ready(if ESP_NOW_SEND_STATUS.load(Ordering::Relaxed) {
Ok(())
} else {
Err(EspNowError::SendFailed)
})
}
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[instability::unstable]
pub struct ReceiveFuture<'r>(PhantomData<&'r mut EspNowReceiver<'r>>);
impl core::future::Future for ReceiveFuture<'_> {
type Output = ReceivedData;
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
ESP_NOW_RX_WAKER.register(cx.waker());
if let Some(data) = STATE.with(|state| state.rx_queue.pop_front()) {
Poll::Ready(data)
} else {
Poll::Pending
}
}
}