esp_wifi/esp_now/
mod.rs

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