Skip to main content

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