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