esp_wifi/esp_now/
mod.rs

1//! ESP-NOW is a kind of connectionless Wi-Fi communication protocol that is
2//! defined by Espressif.
3//!
4//! In ESP-NOW, application data is encapsulated in a vendor-specific action
5//! frame and then transmitted from one Wi-Fi device to another without
6//! connection. CTR with CBC-MAC Protocol(CCMP) is used to protect the action
7//! frame for security. ESP-NOW is widely used in smart light, remote
8//! controlling, sensor, etc.
9//!
10//! For more information see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html
11
12use alloc::{boxed::Box, collections::vec_deque::VecDeque};
13use core::{cell::RefCell, fmt::Debug, marker::PhantomData};
14
15use critical_section::Mutex;
16use portable_atomic::{AtomicBool, AtomicU8, Ordering};
17
18#[cfg(feature = "csi")]
19use crate::wifi::CsiConfig;
20use crate::{
21    binary::include::*,
22    wifi::{RxControlInfo, WifiError},
23};
24
25const RECEIVE_QUEUE_SIZE: usize = 10;
26
27/// Maximum payload length
28pub const ESP_NOW_MAX_DATA_LEN: usize = 250;
29
30/// Broadcast address
31pub const BROADCAST_ADDRESS: [u8; 6] = [0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8];
32
33// Stores received packets until dequeued by the user
34static RECEIVE_QUEUE: Mutex<RefCell<VecDeque<ReceivedData>>> =
35    Mutex::new(RefCell::new(VecDeque::new()));
36
37/// This atomic behaves like a guard, so we need strict memory ordering when
38/// operating it.
39///
40/// This flag indicates whether the send callback has been called after a
41/// sending.
42static ESP_NOW_SEND_CB_INVOKED: AtomicBool = AtomicBool::new(false);
43/// Status of esp now send, true for success, false for failure
44static ESP_NOW_SEND_STATUS: AtomicBool = AtomicBool::new(true);
45
46macro_rules! check_error {
47    ($block:block) => {
48        match unsafe { $block } {
49            0 => Ok(()),
50            res => Err(EspNowError::Error(Error::from_code(res as u32))),
51        }
52    };
53}
54
55/// Internal errors that can occur with ESP-NOW.
56#[repr(u32)]
57#[derive(Debug)]
58#[cfg_attr(feature = "defmt", derive(defmt::Format))]
59#[allow(clippy::enum_variant_names)] // FIXME avoid Error suffix, could use better names
60pub enum Error {
61    /// ESP-NOW is not initialized.
62    NotInitialized  = 12389,
63
64    /// Invalid argument.
65    InvalidArgument = 12390,
66
67    /// Indicates that there was insufficient memory to complete the operation.
68    OutOfMemory     = 12391,
69
70    /// ESP-NOW peer list is full.
71    PeerListFull    = 12392,
72
73    /// ESP-NOW peer is not found.
74    NotFound        = 12393,
75
76    /// Internal error.
77    InternalError   = 12394,
78
79    /// ESP-NOW peer already exists.
80    PeerExists      = 12395,
81
82    /// Interface error.
83    InterfaceError  = 12396,
84
85    /// Represents any other error not covered by the above variants, with an
86    /// associated error code.
87    Other(u32),
88}
89
90impl Error {
91    pub fn from_code(code: u32) -> Error {
92        match code {
93            12389 => Error::NotInitialized,
94            12390 => Error::InvalidArgument,
95            12391 => Error::OutOfMemory,
96            12392 => Error::PeerListFull,
97            12393 => Error::NotFound,
98            12394 => Error::InternalError,
99            12395 => Error::PeerExists,
100            12396 => Error::InterfaceError,
101            _ => Error::Other(code),
102        }
103    }
104}
105
106/// Common errors that can occur while using ESP-NOW driver.
107#[derive(Debug)]
108#[cfg_attr(feature = "defmt", derive(defmt::Format))]
109pub enum EspNowError {
110    /// Internal Error.
111    Error(Error),
112    /// Failed to send an ESP-NOW message.
113    SendFailed,
114    /// Attempt to create `EspNow` instance twice.
115    DuplicateInstance,
116    /// Initialization error
117    Initialization(WifiError),
118}
119
120impl From<WifiError> for EspNowError {
121    fn from(f: WifiError) -> Self {
122        Self::Initialization(f)
123    }
124}
125
126/// Holds the count of peers in an ESP-NOW communication context.
127#[derive(Debug)]
128#[cfg_attr(feature = "defmt", derive(defmt::Format))]
129pub struct PeerCount {
130    /// The total number of peers.
131    pub total_count: i32,
132
133    /// The number of encrypted peers.
134    pub encrypted_count: i32,
135}
136
137/// ESP-NOW rate of specified interface.
138#[repr(u32)]
139#[cfg_attr(feature = "defmt", derive(defmt::Format))]
140pub enum WifiPhyRate {
141    /// < 1 Mbps with long preamble
142    Rate1mL = 0,
143    /// < 2 Mbps with long preamble
144    Rate2m,
145    /// < 5.5 Mbps with long preamble
146    Rate5mL,
147    /// < 11 Mbps with long preamble
148    Rate11mL,
149    /// < 2 Mbps with short preamble
150    Rate2mS,
151    /// < 5.5 Mbps with short preamble
152    Rate5mS,
153    /// < 11 Mbps with short preamble
154    Rate11mS,
155    /// < 48 Mbps
156    Rate48m,
157    /// < 24 Mbps
158    Rate24m,
159    /// < 12 Mbps
160    Rate12m,
161    /// < 6 Mbps
162    Rate6m,
163    /// < 54 Mbps
164    Rate54m,
165    /// < 36 Mbps
166    Rate36m,
167    /// < 18 Mbps
168    Rate18m,
169    /// < 9 Mbps
170    Rate9m,
171    /// < MCS0 with long GI, 6.5 Mbps for 20MHz, 13.5 Mbps for 40MHz
172    RateMcs0Lgi,
173    /// < MCS1 with long GI, 13 Mbps for 20MHz, 27 Mbps for 40MHz
174    RateMcs1Lgi,
175    /// < MCS2 with long GI, 19.5 Mbps for 20MHz, 40.5 Mbps for 40MHz
176    RateMcs2Lgi,
177    /// < MCS3 with long GI, 26 Mbps for 20MHz, 54 Mbps for 40MHz
178    RateMcs3Lgi,
179    /// < MCS4 with long GI, 39 Mbps for 20MHz, 81 Mbps for 40MHz
180    RateMcs4Lgi,
181    /// < MCS5 with long GI, 52 Mbps for 20MHz, 108 Mbps for 40MHz
182    RateMcs5Lgi,
183    /// < MCS6 with long GI, 58.5 Mbps for 20MHz, 121.5 Mbps for 40MHz
184    RateMcs6Lgi,
185    /// < MCS7 with long GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz
186    RateMcs7Lgi,
187    /// < MCS0 with short GI, 7.2 Mbps for 20MHz, 15 Mbps for 40MHz
188    RateMcs0Sgi,
189    /// < MCS1 with short GI, 14.4 Mbps for 20MHz, 30 Mbps for 40MHz
190    RateMcs1Sgi,
191    /// < MCS2 with short GI, 21.7 Mbps for 20MHz, 45 Mbps for 40MHz
192    RateMcs2Sgi,
193    /// < MCS3 with short GI, 28.9 Mbps for 20MHz, 60 Mbps for 40MHz
194    RateMcs3Sgi,
195    /// < MCS4 with short GI, 43.3 Mbps for 20MHz, 90 Mbps for 40MHz
196    RateMcs4Sgi,
197    /// < MCS5 with short GI, 57.8 Mbps for 20MHz, 120 Mbps for 40MHz
198    RateMcs5Sgi,
199    /// < MCS6 with short GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz
200    RateMcs6Sgi,
201    /// < MCS7 with short GI, 72.2 Mbps for 20MHz, 150 Mbps for 40MHz
202    RateMcs7Sgi,
203    /// < 250 Kbps
204    RateLora250k,
205    /// < 500 Kbps
206    RateLora500k,
207    /// Max
208    RateMax,
209}
210
211/// ESP-NOW peer information parameters.
212#[derive(Debug, Clone, Copy)]
213#[cfg_attr(feature = "defmt", derive(defmt::Format))]
214pub struct PeerInfo {
215    /// Interface to use
216    pub interface: EspNowWifiInterface,
217
218    /// ESP-NOW peer MAC address that is also the MAC address of station or
219    /// softap.
220    pub peer_address: [u8; 6],
221
222    /// ESP-NOW peer local master key that is used to encrypt data.
223    pub lmk: Option<[u8; 16]>,
224
225    /// Wi-Fi channel that peer uses to send/receive ESP-NOW data.
226    pub channel: Option<u8>,
227
228    /// Whether the data sent/received by this peer is encrypted.
229    pub encrypt: bool,
230    // we always use STA for now
231}
232
233/// Information about a received packet.
234#[derive(Debug, Clone, Copy)]
235#[cfg_attr(feature = "defmt", derive(defmt::Format))]
236pub struct ReceiveInfo {
237    /// The source address of the received packet.
238    pub src_address: [u8; 6],
239
240    /// The destination address of the received packet.
241    pub dst_address: [u8; 6],
242
243    /// Rx control info of ESP-NOW packet.
244    pub rx_control: RxControlInfo,
245}
246
247/// Stores information about the received data, including the packet content and
248/// associated information.
249#[derive(Clone)]
250pub struct ReceivedData {
251    data: Box<[u8]>,
252    pub info: ReceiveInfo,
253}
254
255impl ReceivedData {
256    /// Returns the received payload.
257    pub fn data(&self) -> &[u8] {
258        &self.data
259    }
260}
261
262#[cfg(feature = "defmt")]
263impl defmt::Format for ReceivedData {
264    fn format(&self, fmt: defmt::Formatter<'_>) {
265        defmt::write!(fmt, "ReceivedData {}, Info {}", &self.data[..], &self.info,)
266    }
267}
268
269impl Debug for ReceivedData {
270    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
271        f.debug_struct("ReceivedData")
272            .field("data", &self.data())
273            .field("info", &self.info)
274            .finish()
275    }
276}
277
278/// The interface to use for this peer
279#[derive(Debug, Clone, Copy, PartialEq)]
280#[cfg_attr(feature = "defmt", derive(defmt::Format))]
281pub enum EspNowWifiInterface {
282    /// Use the AP interface
283    Ap,
284    /// Use the STA interface
285    Sta,
286}
287
288impl EspNowWifiInterface {
289    fn as_wifi_interface(&self) -> wifi_interface_t {
290        match self {
291            EspNowWifiInterface::Ap => wifi_interface_t_WIFI_IF_AP,
292            EspNowWifiInterface::Sta => wifi_interface_t_WIFI_IF_STA,
293        }
294    }
295
296    fn from_wifi_interface(interface: wifi_interface_t) -> Self {
297        #[allow(non_upper_case_globals)]
298        match interface {
299            wifi_interface_t_WIFI_IF_AP => EspNowWifiInterface::Ap,
300            wifi_interface_t_WIFI_IF_STA => EspNowWifiInterface::Sta,
301            wifi_interface_t_WIFI_IF_NAN => panic!("NAN is unsupported"),
302            _ => unreachable!("Unknown interface"),
303        }
304    }
305}
306
307/// Manages the `EspNow` instance lifecycle while ensuring it remains active.
308pub struct EspNowManager<'d> {
309    _rc: EspNowRc<'d>,
310}
311
312impl EspNowManager<'_> {
313    /// Set primary WiFi channel.
314    /// Should only be used when using ESP-NOW without AP or STA.
315    pub fn set_channel(&self, channel: u8) -> Result<(), EspNowError> {
316        check_error!({ esp_wifi_set_channel(channel, 0) })
317    }
318
319    /// Get the version of ESP-NOW.
320    pub fn version(&self) -> Result<u32, EspNowError> {
321        let mut version = 0u32;
322        check_error!({ esp_now_get_version(&mut version as *mut u32) })?;
323        Ok(version)
324    }
325
326    /// Add a peer to the list of known peers.
327    pub fn add_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
328        let raw_peer = esp_now_peer_info_t {
329            peer_addr: peer.peer_address,
330            lmk: peer.lmk.unwrap_or([0u8; 16]),
331            channel: peer.channel.unwrap_or(0),
332            ifidx: peer.interface.as_wifi_interface(),
333            encrypt: peer.encrypt,
334            priv_: core::ptr::null_mut(),
335        };
336        check_error!({ esp_now_add_peer(&raw_peer as *const _) })
337    }
338
339    /// Set CSI configuration and register the receiving callback.
340    #[cfg(feature = "csi")]
341    pub fn set_csi(
342        &mut self,
343        mut csi: CsiConfig,
344        cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send,
345    ) -> Result<(), WifiError> {
346        csi.apply_config()?;
347        csi.set_receive_cb(cb)?;
348        csi.set_csi(true)?;
349
350        Ok(())
351    }
352
353    /// Remove the given peer.
354    pub fn remove_peer(&self, peer_address: &[u8; 6]) -> Result<(), EspNowError> {
355        check_error!({ esp_now_del_peer(peer_address.as_ptr()) })
356    }
357
358    /// Modify a peer information.
359    pub fn modify_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
360        let raw_peer = esp_now_peer_info_t {
361            peer_addr: peer.peer_address,
362            lmk: peer.lmk.unwrap_or([0u8; 16]),
363            channel: peer.channel.unwrap_or(0),
364            ifidx: peer.interface.as_wifi_interface(),
365            encrypt: peer.encrypt,
366            priv_: core::ptr::null_mut(),
367        };
368        check_error!({ esp_now_mod_peer(&raw_peer as *const _) })
369    }
370
371    /// Get peer by MAC address.
372    pub fn peer(&self, peer_address: &[u8; 6]) -> Result<PeerInfo, EspNowError> {
373        let mut raw_peer = esp_now_peer_info_t {
374            peer_addr: [0u8; 6],
375            lmk: [0u8; 16],
376            channel: 0,
377            ifidx: 0,
378            encrypt: false,
379            priv_: core::ptr::null_mut(),
380        };
381        check_error!({ esp_now_get_peer(peer_address.as_ptr(), &mut raw_peer as *mut _) })?;
382
383        Ok(PeerInfo {
384            interface: EspNowWifiInterface::from_wifi_interface(raw_peer.ifidx),
385            peer_address: raw_peer.peer_addr,
386            lmk: if raw_peer.lmk.is_empty() {
387                None
388            } else {
389                Some(raw_peer.lmk)
390            },
391            channel: if raw_peer.channel != 0 {
392                Some(raw_peer.channel)
393            } else {
394                None
395            },
396            encrypt: raw_peer.encrypt,
397        })
398    }
399
400    /// Fetch a peer from peer list.
401    ///
402    /// Only returns peers which address is unicast, for multicast/broadcast
403    /// addresses, the function will skip the entry and find the next in the
404    /// peer list.
405    pub fn fetch_peer(&self, from_head: bool) -> Result<PeerInfo, EspNowError> {
406        let mut raw_peer = esp_now_peer_info_t {
407            peer_addr: [0u8; 6],
408            lmk: [0u8; 16],
409            channel: 0,
410            ifidx: 0,
411            encrypt: false,
412            priv_: core::ptr::null_mut(),
413        };
414        check_error!({ esp_now_fetch_peer(from_head, &mut raw_peer as *mut _) })?;
415
416        Ok(PeerInfo {
417            interface: EspNowWifiInterface::from_wifi_interface(raw_peer.ifidx),
418            peer_address: raw_peer.peer_addr,
419            lmk: if raw_peer.lmk.is_empty() {
420                None
421            } else {
422                Some(raw_peer.lmk)
423            },
424            channel: if raw_peer.channel != 0 {
425                Some(raw_peer.channel)
426            } else {
427                None
428            },
429            encrypt: raw_peer.encrypt,
430        })
431    }
432
433    /// Check is peer is known.
434    pub fn peer_exists(&self, peer_address: &[u8; 6]) -> bool {
435        unsafe { esp_now_is_peer_exist(peer_address.as_ptr()) }
436    }
437
438    /// Get the number of peers.
439    pub fn peer_count(&self) -> Result<PeerCount, EspNowError> {
440        let mut peer_num = esp_now_peer_num_t {
441            total_num: 0,
442            encrypt_num: 0,
443        };
444        check_error!({ esp_now_get_peer_num(&mut peer_num as *mut _) })?;
445
446        Ok(PeerCount {
447            total_count: peer_num.total_num,
448            encrypted_count: peer_num.encrypt_num,
449        })
450    }
451
452    /// Set the primary master key.
453    pub fn set_pmk(&self, pmk: &[u8; 16]) -> Result<(), EspNowError> {
454        check_error!({ esp_now_set_pmk(pmk.as_ptr()) })
455    }
456
457    /// Set wake window for esp_now to wake up in interval unit.
458    ///
459    /// Window is milliseconds the chip keep waked each interval, from 0 to
460    /// 65535.
461    pub fn set_wake_window(&self, wake_window: u16) -> Result<(), EspNowError> {
462        check_error!({ esp_now_set_wake_window(wake_window) })
463    }
464
465    /// Configure ESP-NOW rate.
466    pub fn set_rate(&self, rate: WifiPhyRate) -> Result<(), EspNowError> {
467        check_error!({ esp_wifi_config_espnow_rate(wifi_interface_t_WIFI_IF_STA, rate as u32,) })
468    }
469}
470
471/// This is the sender part of ESP-NOW. You can get this sender by splitting
472/// a `EspNow` instance.
473///
474/// You need a lock when using this sender in multiple tasks.
475/// **DO NOT USE** a lock implementation that disables interrupts since the
476/// completion of a sending requires waiting for a callback invoked in an
477/// interrupt.
478pub struct EspNowSender<'d> {
479    _rc: EspNowRc<'d>,
480}
481
482impl EspNowSender<'_> {
483    /// Send data to peer
484    ///
485    /// The peer needs to be added to the peer list first.
486    pub fn send<'s>(
487        &'s mut self,
488        dst_addr: &[u8; 6],
489        data: &[u8],
490    ) -> Result<SendWaiter<'s>, EspNowError> {
491        ESP_NOW_SEND_CB_INVOKED.store(false, Ordering::Release);
492        check_error!({ esp_now_send(dst_addr.as_ptr(), data.as_ptr(), data.len()) })?;
493        Ok(SendWaiter(PhantomData))
494    }
495}
496
497#[allow(unknown_lints)]
498#[allow(clippy::too_long_first_doc_paragraph)]
499/// This struct is returned by a sync esp now send. Invoking `wait` method of
500/// this struct will block current task until the callback function of esp now
501/// send is called and return the status of previous sending.
502///
503/// This waiter borrows the sender, so when used in multiple tasks, the lock
504/// will only be released when the waiter is dropped or consumed via `wait`.
505///
506/// When using a lock that disables interrupts, the waiter will block forever
507/// since the callback which signals the completion of sending will never be
508/// invoked.
509#[must_use]
510pub struct SendWaiter<'s>(PhantomData<&'s mut EspNowSender<'s>>);
511
512impl SendWaiter<'_> {
513    /// Wait for the previous sending to complete, i.e. the send callback is
514    /// invoked with status of the sending.
515    pub fn wait(self) -> Result<(), EspNowError> {
516        // prevent redundant waiting since we waits for the callback in the Drop
517        // implementation
518        core::mem::forget(self);
519        while !ESP_NOW_SEND_CB_INVOKED.load(Ordering::Acquire) {}
520
521        if ESP_NOW_SEND_STATUS.load(Ordering::Relaxed) {
522            Ok(())
523        } else {
524            Err(EspNowError::SendFailed)
525        }
526    }
527}
528
529impl Drop for SendWaiter<'_> {
530    /// wait for the send to complete to prevent the lock on `EspNowSender` get
531    /// unlocked before a callback is invoked.
532    fn drop(&mut self) {
533        while !ESP_NOW_SEND_CB_INVOKED.load(Ordering::Acquire) {}
534    }
535}
536
537/// This is the receiver part of ESP-NOW. You can get this receiver by splitting
538/// an `EspNow` instance.
539pub struct EspNowReceiver<'d> {
540    _rc: EspNowRc<'d>,
541}
542
543impl EspNowReceiver<'_> {
544    /// Receives data from the ESP-NOW queue.
545    pub fn receive(&self) -> Option<ReceivedData> {
546        critical_section::with(|cs| {
547            let mut queue = RECEIVE_QUEUE.borrow_ref_mut(cs);
548            queue.pop_front()
549        })
550    }
551}
552
553/// The reference counter for properly deinit espnow after all parts are
554/// dropped.
555struct EspNowRc<'d> {
556    rc: &'static AtomicU8,
557    inner: PhantomData<EspNow<'d>>,
558}
559
560impl EspNowRc<'_> {
561    fn new() -> Self {
562        static ESP_NOW_RC: AtomicU8 = AtomicU8::new(0);
563        assert!(ESP_NOW_RC.fetch_add(1, Ordering::AcqRel) == 0);
564
565        Self {
566            rc: &ESP_NOW_RC,
567            inner: PhantomData,
568        }
569    }
570}
571
572impl Clone for EspNowRc<'_> {
573    fn clone(&self) -> Self {
574        self.rc.fetch_add(1, Ordering::Release);
575        Self {
576            rc: self.rc,
577            inner: PhantomData,
578        }
579    }
580}
581
582impl Drop for EspNowRc<'_> {
583    fn drop(&mut self) {
584        if self.rc.fetch_sub(1, Ordering::AcqRel) == 1 {
585            unsafe {
586                esp_now_unregister_recv_cb();
587                esp_now_deinit();
588            }
589        }
590    }
591}
592
593#[allow(unknown_lints)]
594#[allow(clippy::too_long_first_doc_paragraph)]
595/// ESP-NOW is a kind of connection-less Wi-Fi communication protocol that is
596/// defined by Espressif. In ESP-NOW, application data is encapsulated in a
597/// vendor-specific action frame and then transmitted from one Wi-Fi device to
598/// another without connection. CTR with CBC-MAC Protocol(CCMP) is used to
599/// protect the action frame for security. ESP-NOW is widely used in smart
600/// light, remote controlling, sensor, etc.
601///
602/// For convenience, by default there will be a broadcast peer added on the STA
603/// interface.
604pub struct EspNow<'d> {
605    manager: EspNowManager<'d>,
606    sender: EspNowSender<'d>,
607    receiver: EspNowReceiver<'d>,
608    _phantom: PhantomData<&'d ()>,
609}
610
611impl<'d> EspNow<'d> {
612    pub(crate) fn new_internal() -> EspNow<'d> {
613        let espnow_rc = EspNowRc::new();
614        let esp_now = EspNow {
615            manager: EspNowManager {
616                _rc: espnow_rc.clone(),
617            },
618            sender: EspNowSender {
619                _rc: espnow_rc.clone(),
620            },
621            receiver: EspNowReceiver { _rc: espnow_rc },
622            _phantom: PhantomData,
623        };
624
625        check_error!({ esp_now_init() }).expect("esp-now-init failed");
626        check_error!({ esp_now_register_recv_cb(Some(rcv_cb)) }).ok();
627        check_error!({ esp_now_register_send_cb(Some(send_cb)) }).ok();
628
629        esp_now
630            .add_peer(PeerInfo {
631                interface: EspNowWifiInterface::Sta,
632                peer_address: BROADCAST_ADDRESS,
633                lmk: None,
634                channel: None,
635                encrypt: false,
636            })
637            .ok();
638
639        esp_now
640    }
641
642    /// Splits the `EspNow` instance into its manager, sender, and receiver
643    /// components.
644    pub fn split(self) -> (EspNowManager<'d>, EspNowSender<'d>, EspNowReceiver<'d>) {
645        (self.manager, self.sender, self.receiver)
646    }
647
648    /// Set primary WiFi channel.
649    /// Should only be used when using ESP-NOW without AP or STA.
650    pub fn set_channel(&self, channel: u8) -> Result<(), EspNowError> {
651        self.manager.set_channel(channel)
652    }
653
654    /// Get the version of ESP-NOW.
655    pub fn version(&self) -> Result<u32, EspNowError> {
656        self.manager.version()
657    }
658
659    /// Add a peer to the list of known peers.
660    pub fn add_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
661        self.manager.add_peer(peer)
662    }
663
664    /// Remove the given peer.
665    pub fn remove_peer(&self, peer_address: &[u8; 6]) -> Result<(), EspNowError> {
666        self.manager.remove_peer(peer_address)
667    }
668
669    /// Modify a peer information.
670    pub fn modify_peer(&self, peer: PeerInfo) -> Result<(), EspNowError> {
671        self.manager.modify_peer(peer)
672    }
673
674    /// Get peer by MAC address.
675    pub fn peer(&self, peer_address: &[u8; 6]) -> Result<PeerInfo, EspNowError> {
676        self.manager.peer(peer_address)
677    }
678
679    /// Fetch a peer from peer list.
680    ///
681    /// Only returns peers which address is unicast, for multicast/broadcast
682    /// addresses, the function will skip the entry and find the next in the
683    /// peer list.
684    pub fn fetch_peer(&self, from_head: bool) -> Result<PeerInfo, EspNowError> {
685        self.manager.fetch_peer(from_head)
686    }
687
688    /// Check is peer is known.
689    pub fn peer_exists(&self, peer_address: &[u8; 6]) -> bool {
690        self.manager.peer_exists(peer_address)
691    }
692
693    /// Get the number of peers.
694    pub fn peer_count(&self) -> Result<PeerCount, EspNowError> {
695        self.manager.peer_count()
696    }
697
698    /// Set the primary master key.
699    pub fn set_pmk(&self, pmk: &[u8; 16]) -> Result<(), EspNowError> {
700        self.manager.set_pmk(pmk)
701    }
702
703    /// Set wake window for esp_now to wake up in interval unit.
704    ///
705    /// Window is milliseconds the chip keep waked each interval, from 0 to
706    /// 65535.
707    pub fn set_wake_window(&self, wake_window: u16) -> Result<(), EspNowError> {
708        self.manager.set_wake_window(wake_window)
709    }
710
711    /// Configure ESP-NOW rate.
712    pub fn set_rate(&self, rate: WifiPhyRate) -> Result<(), EspNowError> {
713        self.manager.set_rate(rate)
714    }
715
716    /// Send data to peer.
717    ///
718    /// The peer needs to be added to the peer list first.
719    pub fn send<'s>(
720        &'s mut self,
721        dst_addr: &[u8; 6],
722        data: &[u8],
723    ) -> Result<SendWaiter<'s>, EspNowError> {
724        self.sender.send(dst_addr, data)
725    }
726
727    /// Receive data.
728    pub fn receive(&self) -> Option<ReceivedData> {
729        self.receiver.receive()
730    }
731}
732
733unsafe extern "C" fn send_cb(_mac_addr: *const u8, status: esp_now_send_status_t) {
734    critical_section::with(|_| {
735        let is_success = status == esp_now_send_status_t_ESP_NOW_SEND_SUCCESS;
736        ESP_NOW_SEND_STATUS.store(is_success, Ordering::Relaxed);
737
738        ESP_NOW_SEND_CB_INVOKED.store(true, Ordering::Release);
739
740        asynch::ESP_NOW_TX_WAKER.wake();
741    })
742}
743
744unsafe extern "C" fn rcv_cb(
745    esp_now_info: *const esp_now_recv_info_t,
746    data: *const u8,
747    data_len: i32,
748) {
749    let src = unsafe {
750        [
751            (*esp_now_info).src_addr.offset(0).read(),
752            (*esp_now_info).src_addr.offset(1).read(),
753            (*esp_now_info).src_addr.offset(2).read(),
754            (*esp_now_info).src_addr.offset(3).read(),
755            (*esp_now_info).src_addr.offset(4).read(),
756            (*esp_now_info).src_addr.offset(5).read(),
757        ]
758    };
759
760    let dst = unsafe {
761        [
762            (*esp_now_info).des_addr.offset(0).read(),
763            (*esp_now_info).des_addr.offset(1).read(),
764            (*esp_now_info).des_addr.offset(2).read(),
765            (*esp_now_info).des_addr.offset(3).read(),
766            (*esp_now_info).des_addr.offset(4).read(),
767            (*esp_now_info).des_addr.offset(5).read(),
768        ]
769    };
770
771    let rx_cntl = unsafe { (*esp_now_info).rx_ctrl };
772    let rx_control = unsafe { RxControlInfo::from_raw(rx_cntl) };
773
774    let info = ReceiveInfo {
775        src_address: src,
776        dst_address: dst,
777        rx_control,
778    };
779    let slice = unsafe { core::slice::from_raw_parts(data, data_len as usize) };
780    critical_section::with(|cs| {
781        let mut queue = RECEIVE_QUEUE.borrow_ref_mut(cs);
782        let data = Box::from(slice);
783
784        if queue.len() >= RECEIVE_QUEUE_SIZE {
785            queue.pop_front();
786        }
787
788        queue.push_back(ReceivedData { data, info });
789
790        asynch::ESP_NOW_RX_WAKER.wake();
791    });
792}
793
794pub use asynch::SendFuture;
795
796mod asynch {
797    use core::task::{Context, Poll};
798
799    use esp_hal::asynch::AtomicWaker;
800
801    use super::*;
802
803    pub(super) static ESP_NOW_TX_WAKER: AtomicWaker = AtomicWaker::new();
804    pub(super) static ESP_NOW_RX_WAKER: AtomicWaker = AtomicWaker::new();
805
806    impl EspNowReceiver<'_> {
807        /// This function takes mutable reference to self because the
808        /// implementation of `ReceiveFuture` is not logically thread
809        /// safe.
810        pub fn receive_async(&mut self) -> ReceiveFuture<'_> {
811            ReceiveFuture(PhantomData)
812        }
813    }
814
815    impl EspNowSender<'_> {
816        /// Sends data asynchronously to a peer (using its MAC) using ESP-NOW.
817        pub fn send_async<'s, 'r>(
818            &'s mut self,
819            addr: &'r [u8; 6],
820            data: &'r [u8],
821        ) -> SendFuture<'s, 'r> {
822            SendFuture {
823                _sender: PhantomData,
824                addr,
825                data,
826                sent: false,
827            }
828        }
829    }
830
831    impl EspNow<'_> {
832        /// This function takes mutable reference to self because the
833        /// implementation of `ReceiveFuture` is not logically thread
834        /// safe.
835        pub fn receive_async(&mut self) -> ReceiveFuture<'_> {
836            self.receiver.receive_async()
837        }
838
839        /// The returned future must not be dropped before it's ready to avoid
840        /// getting wrong status for sendings.
841        pub fn send_async<'s, 'r>(
842            &'s mut self,
843            dst_addr: &'r [u8; 6],
844            data: &'r [u8],
845        ) -> SendFuture<'s, 'r> {
846            self.sender.send_async(dst_addr, data)
847        }
848    }
849
850    /// A `future` representing the result of an asynchronous ESP-NOW send
851    /// operation.
852    #[must_use = "futures do nothing unless you `.await` or poll them"]
853    pub struct SendFuture<'s, 'r> {
854        _sender: PhantomData<&'s mut EspNowSender<'s>>,
855        addr: &'r [u8; 6],
856        data: &'r [u8],
857        sent: bool,
858    }
859
860    impl core::future::Future for SendFuture<'_, '_> {
861        type Output = Result<(), EspNowError>;
862
863        fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
864            if !self.sent {
865                ESP_NOW_TX_WAKER.register(cx.waker());
866                ESP_NOW_SEND_CB_INVOKED.store(false, Ordering::Release);
867                if let Err(e) = check_error!({
868                    esp_now_send(self.addr.as_ptr(), self.data.as_ptr(), self.data.len())
869                }) {
870                    return Poll::Ready(Err(e));
871                }
872                self.sent = true;
873            }
874
875            if !ESP_NOW_SEND_CB_INVOKED.load(Ordering::Acquire) {
876                Poll::Pending
877            } else {
878                Poll::Ready(if ESP_NOW_SEND_STATUS.load(Ordering::Relaxed) {
879                    Ok(())
880                } else {
881                    Err(EspNowError::SendFailed)
882                })
883            }
884        }
885    }
886
887    /// It's not logically safe to poll multiple instances of `ReceiveFuture`
888    /// simultaneously since the callback can only wake one future, leaving
889    /// the rest of them unwakable.
890    #[must_use = "futures do nothing unless you `.await` or poll them"]
891    pub struct ReceiveFuture<'r>(PhantomData<&'r mut EspNowReceiver<'r>>);
892
893    impl core::future::Future for ReceiveFuture<'_> {
894        type Output = ReceivedData;
895
896        fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
897            ESP_NOW_RX_WAKER.register(cx.waker());
898
899            if let Some(data) = critical_section::with(|cs| {
900                let mut queue = RECEIVE_QUEUE.borrow_ref_mut(cs);
901                queue.pop_front()
902            }) {
903                Poll::Ready(data)
904            } else {
905                Poll::Pending
906            }
907        }
908    }
909}