1use core::marker::PhantomData;
10
11use ::log::info;
12
13use alloc::boxed::Box;
14
15use crate::sys::*;
16
17use crate::private::mutex::Mutex;
18
19type Singleton<T> = Mutex<Option<Box<T>>>;
20
21pub const BROADCAST: [u8; 6] = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
22
23#[derive(Debug, Clone)]
24pub struct ReceiveInfo<'a> {
25 pub src_addr: &'a [u8; 6],
26 pub dst_addr: &'a [u8; 6],
27}
28
29#[allow(clippy::type_complexity)]
30static RECV_CALLBACK: Singleton<dyn FnMut(&ReceiveInfo, &[u8]) + Send + 'static> = Mutex::new(None);
31#[allow(clippy::type_complexity)]
32static SEND_CALLBACK: Singleton<dyn FnMut(&[u8], SendStatus) + Send + 'static> = Mutex::new(None);
33
34static TAKEN: Mutex<bool> = Mutex::new(false);
35
36#[derive(Debug)]
37pub enum SendStatus {
38 SUCCESS = 0,
39 FAIL,
40}
41
42impl From<u32> for SendStatus {
43 fn from(val: u32) -> Self {
44 match val {
45 0 => SendStatus::SUCCESS,
46 1 => SendStatus::FAIL,
47 _ => panic!("Wrong status code"),
48 }
49 }
50}
51
52pub type PeerInfo = esp_now_peer_info_t;
53
54pub struct EspNow<'a>(PhantomData<&'a ()>);
55
56impl EspNow<'static> {
57 pub fn take() -> Result<Self, EspError> {
58 Self::internal_take()
59 }
60}
61
62impl<'a> EspNow<'a> {
63 pub unsafe fn take_nonstatic() -> Result<Self, EspError> {
87 Self::internal_take()
88 }
89
90 fn internal_take() -> Result<Self, EspError> {
91 let mut taken = TAKEN.lock();
92
93 if *taken {
94 return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
95 }
96
97 esp!(unsafe { esp_wifi_set_ps(0) })?;
101
102 info!("Initializing ESP NOW");
103 esp!(unsafe { esp_now_init() })?;
104
105 *taken = true;
106
107 Ok(Self(PhantomData))
108 }
109
110 pub fn send(&self, peer_addr: [u8; 6], data: &[u8]) -> Result<(), EspError> {
111 esp!(unsafe { crate::sys::esp_now_send(peer_addr.as_ptr(), data.as_ptr(), data.len(),) })?;
112
113 Ok(())
114 }
115
116 pub fn add_peer(&self, peer_info: PeerInfo) -> Result<(), EspError> {
117 esp!(unsafe { esp_now_add_peer(&peer_info) })?;
118
119 Ok(())
120 }
121
122 pub fn del_peer(&self, peer_addr: [u8; 6]) -> Result<(), EspError> {
123 esp!(unsafe { esp_now_del_peer(&peer_addr as *const u8) })?;
124
125 Ok(())
126 }
127
128 pub fn mod_peer(&self, peer_info: PeerInfo) -> Result<(), EspError> {
129 esp!(unsafe { esp_now_mod_peer(&peer_info) })?;
130
131 Ok(())
132 }
133
134 pub fn get_peer(&self, peer_addr: [u8; 6]) -> Result<PeerInfo, EspError> {
135 let mut peer_info = PeerInfo::default();
136 esp!(unsafe {
137 esp_now_get_peer(
138 &peer_addr as *const u8,
139 &mut peer_info as *mut esp_now_peer_info_t,
140 )
141 })?;
142
143 Ok(peer_info)
144 }
145
146 pub fn peer_exists(&self, peer_addr: [u8; 6]) -> Result<bool, EspError> {
147 Ok(unsafe { esp_now_is_peer_exist(&peer_addr as *const u8) })
148 }
149
150 pub fn get_peers_number(&self) -> Result<(usize, usize), EspError> {
151 let mut num = esp_now_peer_num_t::default();
152 esp!(unsafe { esp_now_get_peer_num(&mut num as *mut esp_now_peer_num_t) })?;
153 Ok((num.total_num as usize, num.encrypt_num as usize))
154 }
155
156 pub fn fetch_peer(&self, from_head: bool) -> Result<PeerInfo, EspError> {
157 let mut peer_info = PeerInfo::default();
158 esp!(unsafe { esp_now_fetch_peer(from_head, &mut peer_info as *mut esp_now_peer_info_t) })?;
159
160 Ok(peer_info)
161 }
162
163 pub fn set_pmk(&self, pmk: &[u8]) -> Result<(), EspError> {
164 esp!(unsafe { esp_now_set_pmk(pmk.as_ptr()) })?;
165
166 Ok(())
167 }
168
169 pub fn get_version(&self) -> Result<u32, EspError> {
170 let mut version: u32 = 0;
171 esp!(unsafe { esp_now_get_version(&mut version as *mut u32) })?;
172 Ok(version)
173 }
174
175 pub fn register_recv_cb<F>(&self, callback: F) -> Result<(), EspError>
176 where
177 F: FnMut(&ReceiveInfo, &[u8]) + Send + 'a,
178 {
179 #[allow(clippy::type_complexity)]
180 let callback: Box<dyn FnMut(&ReceiveInfo, &[u8]) + Send + 'a> = Box::new(callback);
181 #[allow(clippy::type_complexity)]
182 let callback: Box<dyn FnMut(&ReceiveInfo, &[u8]) + Send + 'static> =
183 unsafe { core::mem::transmute(callback) };
184
185 *RECV_CALLBACK.lock() = Some(Box::new(callback));
186 esp!(unsafe { esp_now_register_recv_cb(Some(Self::recv_callback)) })?;
187
188 Ok(())
189 }
190
191 pub fn unregister_recv_cb(&self) -> Result<(), EspError> {
192 esp!(unsafe { esp_now_unregister_recv_cb() })?;
193 *RECV_CALLBACK.lock() = None;
194
195 Ok(())
196 }
197
198 pub fn register_send_cb<F>(&self, callback: F) -> Result<(), EspError>
199 where
200 F: FnMut(&[u8], SendStatus) + Send + 'a,
201 {
202 #[allow(clippy::type_complexity)]
203 let callback: Box<dyn FnMut(&[u8], SendStatus) + Send + 'a> = Box::new(callback);
204 #[allow(clippy::type_complexity)]
205 let callback: Box<dyn FnMut(&[u8], SendStatus) + Send + 'static> =
206 unsafe { core::mem::transmute(callback) };
207
208 *SEND_CALLBACK.lock() = Some(Box::new(callback));
209 esp!(unsafe { esp_now_register_send_cb(Some(Self::send_callback)) })?;
210
211 Ok(())
212 }
213
214 pub fn unregister_send_cb(&self) -> Result<(), EspError> {
215 esp!(unsafe { esp_now_unregister_send_cb() })?;
216 *SEND_CALLBACK.lock() = None;
217
218 Ok(())
219 }
220
221 extern "C" fn send_callback(
222 #[cfg(esp_idf_version_at_least_5_5_0)] tx_info: *const esp_now_send_info_t,
223 #[cfg(not(esp_idf_version_at_least_5_5_0))] dst_addr: *const u8,
224 status: esp_now_send_status_t,
225 ) {
226 #[cfg(esp_idf_version_at_least_5_5_0)]
227 let c_mac = unsafe { core::slice::from_raw_parts((*tx_info).des_addr, 6usize) };
228
229 #[cfg(not(esp_idf_version_at_least_5_5_0))]
230 let c_mac = unsafe { core::slice::from_raw_parts(dst_addr, 6usize) };
231
232 if let Some(ref mut callback) = *SEND_CALLBACK.lock() {
233 callback(c_mac, status.into())
234 } else {
235 panic!("EspNow callback not available");
236 }
237 }
238
239 extern "C" fn recv_callback(
240 #[cfg(esp_idf_version_major = "4")] src_addr: *const u8,
241 #[cfg(not(esp_idf_version_major = "4"))] esp_now_info: *const esp_now_recv_info_t,
242 data: *const u8,
243 data_len: core::ffi::c_int,
244 ) {
245 #[cfg(not(any(esp_idf_version_major = "4")))]
246 let src_addr = unsafe { *esp_now_info }.src_addr.cast_const();
247 #[cfg(not(any(esp_idf_version_major = "4")))]
248 let dst_addr = unsafe { *esp_now_info }.des_addr.cast_const();
249 let c_src_addr = unsafe { &*(src_addr as *const [u8; 6]) };
250 #[cfg(not(any(esp_idf_version_major = "4")))]
251 let c_dst_addr = unsafe { &*(dst_addr as *const [u8; 6]) };
252 let c_data = unsafe { core::slice::from_raw_parts(data, data_len as usize) };
253
254 if let Some(ref mut callback) = *RECV_CALLBACK.lock() {
255 callback(
256 &ReceiveInfo {
257 src_addr: c_src_addr,
258 #[cfg(esp_idf_version_major = "4")]
259 dst_addr: &[0u8; 6],
260 #[cfg(not(any(esp_idf_version_major = "4")))]
261 dst_addr: c_dst_addr,
262 },
263 c_data,
264 )
265 } else {
266 panic!("EspNow callback not available");
267 }
268 }
269}
270
271impl Drop for EspNow<'_> {
272 fn drop(&mut self) {
273 let mut taken = TAKEN.lock();
274
275 esp!(unsafe { esp_now_deinit() }).unwrap();
276
277 let send_cb = &mut *SEND_CALLBACK.lock();
278 *send_cb = None;
279
280 let recv_cb = &mut *RECV_CALLBACK.lock();
281 *recv_cb = None;
282
283 *taken = false;
284 }
285}