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