1use core::marker::PhantomData;
3use core::str::Utf8Error;
4use core::time::Duration;
5use core::{cmp, ffi, fmt, ops};
6
7extern crate alloc;
8use alloc::boxed::Box;
9use alloc::sync::Arc;
10
11use enumset::*;
12
13use embedded_svc::wifi::Wifi;
14
15use crate::hal::modem::WifiModemPeripheral;
16
17use crate::sys::*;
18
19use crate::eventloop::EspEventLoop;
20use crate::eventloop::{
21 EspEventDeserializer, EspEventSource, EspSubscription, EspSystemEventLoop, System, Wait,
22};
23use crate::handle::RawHandle;
24#[cfg(esp_idf_comp_esp_netif_enabled)]
25use crate::netif::*;
26use crate::nvs::EspDefaultNvsPartition;
27use crate::private::common::*;
28use crate::private::cstr::*;
29use crate::private::mutex;
30#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
31use crate::timer::EspTaskTimerService;
32
33pub use embedded_svc::wifi::{
34 AccessPointConfiguration, AccessPointInfo, AuthMethod, Capability, ClientConfiguration,
35 Configuration, PmfConfiguration, Protocol, ScanMethod, ScanSortMethod, SecondaryChannel,
36};
37
38pub mod config {
39 use core::time::Duration;
40
41 use crate::sys::*;
42
43 #[derive(Clone, Debug, PartialEq, Eq)]
44 pub enum ScanType {
45 Active { min: Duration, max: Duration },
46 Passive(Duration),
47 }
48
49 impl ScanType {
50 pub const fn new() -> Self {
51 Self::Active {
52 min: Duration::from_secs(0),
53 max: Duration::from_secs(0),
54 }
55 }
56 }
57
58 impl Default for ScanType {
59 fn default() -> Self {
60 Self::new()
61 }
62 }
63
64 #[derive(Clone, Debug, PartialEq, Eq)]
65 pub struct ScanConfig {
66 pub bssid: Option<[u8; 6]>,
67 pub ssid: Option<heapless::String<32>>,
68 pub channel: Option<u8>,
69 pub scan_type: ScanType,
70 pub show_hidden: bool,
71 }
72
73 impl ScanConfig {
74 pub const fn new() -> Self {
75 Self {
76 bssid: None,
77 ssid: None,
78 channel: None,
79 scan_type: ScanType::new(),
80 show_hidden: false,
81 }
82 }
83 }
84
85 impl Default for ScanConfig {
86 fn default() -> Self {
87 Self::new()
88 }
89 }
90
91 impl From<&ScanConfig> for wifi_scan_config_t {
92 fn from(s: &ScanConfig) -> Self {
93 #[allow(clippy::needless_update)]
94 Self {
95 bssid: s.bssid.map_or(core::ptr::null(), |v| v.as_ptr()) as *mut u8,
96 ssid: s.ssid.as_ref().map_or(core::ptr::null(), |v| v.as_ptr()) as *mut u8,
97 scan_time: wifi_scan_time_t {
98 active: wifi_active_scan_time_t {
99 min: match s.scan_type {
100 ScanType::Active { min, .. } => min.as_millis() as _,
101 _ => 0,
102 },
103 max: match s.scan_type {
104 ScanType::Active { max, .. } => max.as_millis() as _,
105 _ => 0,
106 },
107 },
108 passive: match s.scan_type {
109 ScanType::Passive(time) => time.as_millis() as _,
110 _ => 0,
111 },
112 },
113 channel: s.channel.unwrap_or_default(),
114 scan_type: matches!(s.scan_type, ScanType::Passive { .. }).into(),
115 show_hidden: s.show_hidden,
116 ..Default::default()
117 }
118 }
119 }
120}
121
122impl From<AuthMethod> for Newtype<wifi_auth_mode_t> {
123 fn from(method: AuthMethod) -> Self {
124 Newtype(match method {
125 AuthMethod::None => wifi_auth_mode_t_WIFI_AUTH_OPEN,
126 AuthMethod::WEP => wifi_auth_mode_t_WIFI_AUTH_WEP,
127 AuthMethod::WPA => wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
128 AuthMethod::WPA2Personal => wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
129 AuthMethod::WPAWPA2Personal => wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK,
130 AuthMethod::WPA2Enterprise => wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE,
131 AuthMethod::WPA3Personal => wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
132 AuthMethod::WPA2WPA3Personal => wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK,
133 AuthMethod::WAPIPersonal => wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
134 })
135 }
136}
137
138impl From<Newtype<wifi_auth_mode_t>> for Option<AuthMethod> {
139 #[allow(non_upper_case_globals)]
140 #[allow(non_snake_case)]
141 fn from(mode: Newtype<wifi_auth_mode_t>) -> Self {
142 match mode.0 {
143 wifi_auth_mode_t_WIFI_AUTH_OPEN => Some(AuthMethod::None),
144 wifi_auth_mode_t_WIFI_AUTH_WEP => Some(AuthMethod::WEP),
145 wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => Some(AuthMethod::WPA),
146 wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => Some(AuthMethod::WPA2Personal),
147 wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => Some(AuthMethod::WPAWPA2Personal),
148 wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => Some(AuthMethod::WPA2Enterprise),
149 wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => Some(AuthMethod::WPA3Personal),
150 wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => Some(AuthMethod::WPA2WPA3Personal),
151 wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => Some(AuthMethod::WAPIPersonal),
152 _ => None,
153 }
154 }
155}
156
157impl TryFrom<&ClientConfiguration> for Newtype<wifi_sta_config_t> {
158 type Error = EspError;
159
160 fn try_from(conf: &ClientConfiguration) -> Result<Self, Self::Error> {
161 let bssid: [u8; 6] = match &conf.bssid {
162 Some(bssid_ref) => *bssid_ref,
163 None => [0; 6],
164 };
165
166 #[allow(clippy::needless_update)]
167 let mut result = wifi_sta_config_t {
168 ssid: [0; 32],
169 password: [0; 64],
170 scan_method: match conf.scan_method {
171 ScanMethod::CompleteScan(_) => wifi_scan_method_t_WIFI_ALL_CHANNEL_SCAN,
172 ScanMethod::FastScan => wifi_scan_method_t_WIFI_FAST_SCAN,
173 _ => wifi_scan_method_t_WIFI_ALL_CHANNEL_SCAN,
174 },
175 bssid_set: conf.bssid.is_some(),
176 bssid,
177 channel: conf.channel.unwrap_or(0u8),
178 listen_interval: 0,
179 sort_method: match conf.scan_method {
180 ScanMethod::CompleteScan(ScanSortMethod::Signal) => {
181 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL
182 }
183 ScanMethod::CompleteScan(ScanSortMethod::Security) => {
184 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SECURITY
185 }
186 _ => wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
187 },
188 threshold: wifi_scan_threshold_t {
189 rssi: -127,
190 authmode: Newtype::<wifi_auth_mode_t>::from(conf.auth_method).0,
191 ..Default::default()
192 },
193 pmf_cfg: wifi_pmf_config_t {
194 capable: false,
195 required: false,
196 },
197 ..Default::default()
198 };
199
200 set_str_no_termination_requirement(&mut result.ssid, conf.ssid.as_ref())?;
201 set_str_no_termination_requirement(&mut result.password, conf.password.as_ref())?;
202
203 Ok(Newtype(result))
204 }
205}
206
207impl From<Newtype<wifi_sta_config_t>> for ClientConfiguration {
208 fn from(conf: Newtype<wifi_sta_config_t>) -> Self {
209 Self {
210 ssid: array_to_heapless_string(conf.0.ssid),
211 bssid: if conf.0.bssid_set {
212 Some(conf.0.bssid)
213 } else {
214 None
215 },
216 auth_method: Option::<AuthMethod>::from(Newtype(conf.0.threshold.authmode)).unwrap(),
217 password: array_to_heapless_string(conf.0.password),
218 channel: if conf.0.channel != 0 {
219 Some(conf.0.channel)
220 } else {
221 None
222 },
223 #[allow(non_upper_case_globals)]
224 #[allow(non_snake_case)]
225 scan_method: match conf.0.scan_method {
226 wifi_scan_method_t_WIFI_FAST_SCAN => ScanMethod::FastScan,
227 wifi_scan_method_t_WIFI_ALL_CHANNEL_SCAN => match conf.0.sort_method {
228 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL => {
229 ScanMethod::CompleteScan(ScanSortMethod::Signal)
230 }
231 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SECURITY => {
232 ScanMethod::CompleteScan(ScanSortMethod::Security)
233 }
234 _ => ScanMethod::default(),
235 },
236 _ => ScanMethod::default(),
237 },
238 pmf_cfg: match conf.0.pmf_cfg {
239 wifi_pmf_config_t {
240 capable: false,
241 required: _,
242 } => PmfConfiguration::NotCapable,
243 wifi_pmf_config_t {
244 capable: true,
245 required,
246 } => PmfConfiguration::Capable { required },
247 },
248 }
249 }
250}
251
252impl TryFrom<&AccessPointConfiguration> for Newtype<wifi_ap_config_t> {
253 type Error = EspError;
254
255 fn try_from(conf: &AccessPointConfiguration) -> Result<Self, Self::Error> {
256 let mut result = wifi_ap_config_t {
257 ssid: [0; 32],
258 password: [0; 64],
259 ssid_len: conf.ssid.len() as u8,
260 channel: conf.channel,
261 authmode: Newtype::<wifi_auth_mode_t>::from(conf.auth_method).0,
262 ssid_hidden: u8::from(conf.ssid_hidden),
263 max_connection: cmp::min(conf.max_connections, 16) as u8,
264 beacon_interval: 100,
265 ..Default::default()
266 };
267
268 set_str(&mut result.ssid, conf.ssid.as_ref())?;
269 set_str(&mut result.password, conf.password.as_ref())?;
270
271 Ok(Newtype(result))
272 }
273}
274
275impl From<Newtype<wifi_ap_config_t>> for AccessPointConfiguration {
276 fn from(conf: Newtype<wifi_ap_config_t>) -> Self {
277 Self {
278 ssid: if conf.0.ssid_len == 0 {
279 Default::default()
280 } else {
281 unsafe {
282 core::str::from_utf8_unchecked(&conf.0.ssid[0..conf.0.ssid_len as usize])
283 .try_into()
284 .unwrap()
285 }
286 },
287 ssid_hidden: conf.0.ssid_hidden != 0,
288 channel: conf.0.channel,
289 secondary_channel: None,
290 auth_method: Option::<AuthMethod>::from(Newtype(conf.0.authmode)).unwrap(),
291 protocols: EnumSet::<Protocol>::empty(), password: array_to_heapless_string(conf.0.password),
293 max_connections: conf.0.max_connection as u16,
294 }
295 }
296}
297
298impl TryFrom<Newtype<&wifi_ap_record_t>> for AccessPointInfo {
299 type Error = Utf8Error;
300
301 #[allow(non_upper_case_globals)]
302 #[allow(non_snake_case)]
303 fn try_from(ap_info: Newtype<&wifi_ap_record_t>) -> Result<Self, Self::Error> {
304 let a = ap_info.0;
305
306 Ok(Self {
307 ssid: from_cstr_fallible(&a.ssid)?.try_into().unwrap(),
308 bssid: a.bssid,
309 channel: a.primary,
310 secondary_channel: match a.second {
311 wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
312 wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
313 wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
314 _ => panic!(),
315 },
316 signal_strength: a.rssi,
317 protocols: EnumSet::<Protocol>::empty(), auth_method: Option::<AuthMethod>::from(Newtype::<wifi_auth_mode_t>(a.authmode)),
319 })
320 }
321}
322
323#[derive(Copy, Clone, Debug, Eq, PartialEq)]
324pub enum WifiDeviceId {
325 Ap,
326 Sta,
327}
328
329impl From<WifiDeviceId> for wifi_interface_t {
330 fn from(id: WifiDeviceId) -> Self {
331 match id {
332 WifiDeviceId::Ap => wifi_interface_t_WIFI_IF_AP,
333 WifiDeviceId::Sta => wifi_interface_t_WIFI_IF_STA,
334 }
335 }
336}
337
338#[allow(non_upper_case_globals)]
339impl From<wifi_interface_t> for WifiDeviceId {
340 fn from(id: wifi_interface_t) -> Self {
341 match id {
342 wifi_interface_t_WIFI_IF_AP => WifiDeviceId::Ap,
343 wifi_interface_t_WIFI_IF_STA => WifiDeviceId::Sta,
344 _ => unreachable!(),
345 }
346 }
347}
348
349extern "C" {
350 fn esp_wifi_internal_reg_rxcb(
351 ifx: wifi_interface_t,
352 rxcb: Option<
353 unsafe extern "C" fn(
354 buffer: *mut ffi::c_void,
355 len: u16,
356 eb: *mut ffi::c_void,
357 ) -> esp_err_t,
358 >,
359 ) -> esp_err_t;
360
361 fn esp_wifi_internal_free_rx_buffer(buffer: *mut ffi::c_void);
362
363 fn esp_wifi_internal_tx(
364 wifi_if: wifi_interface_t,
365 buffer: *mut ffi::c_void,
366 len: u16,
367 ) -> esp_err_t;
368}
369
370#[allow(clippy::type_complexity)]
371static mut RX_CALLBACK: Option<
372 Box<dyn FnMut(WifiDeviceId, WifiFrame) -> Result<(), EspError> + 'static>,
373> = None;
374#[allow(clippy::type_complexity)]
375static mut TX_CALLBACK: Option<Box<dyn FnMut(WifiDeviceId, &[u8], bool) + 'static>> = None;
376
377pub trait NonBlocking {
378 fn is_scan_done(&self) -> Result<bool, EspError>;
379
380 fn start_scan(
381 &mut self,
382 scan_config: &config::ScanConfig,
383 blocking: bool,
384 ) -> Result<(), EspError>;
385
386 fn stop_scan(&mut self) -> Result<(), EspError>;
387
388 fn get_scan_result_n<const N: usize>(
389 &mut self,
390 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError>;
391
392 #[cfg(feature = "alloc")]
393 fn get_scan_result(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError>;
394
395 fn start_wps(&mut self, config: &WpsConfig) -> Result<(), EspError>;
396
397 fn stop_wps(&mut self) -> Result<WpsStatus, EspError>;
398
399 fn is_wps_finished(&self) -> Result<bool, EspError>;
400}
401
402impl<T> NonBlocking for &mut T
403where
404 T: NonBlocking,
405{
406 fn is_scan_done(&self) -> Result<bool, EspError> {
407 (**self).is_scan_done()
408 }
409
410 fn start_scan(
411 &mut self,
412 scan_config: &config::ScanConfig,
413 blocking: bool,
414 ) -> Result<(), EspError> {
415 (**self).start_scan(scan_config, blocking)
416 }
417
418 fn stop_scan(&mut self) -> Result<(), EspError> {
419 (**self).stop_scan()
420 }
421
422 fn get_scan_result_n<const N: usize>(
423 &mut self,
424 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
425 (**self).get_scan_result_n()
426 }
427
428 #[cfg(feature = "alloc")]
429 fn get_scan_result(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
430 (**self).get_scan_result()
431 }
432
433 fn start_wps(&mut self, config: &WpsConfig) -> Result<(), EspError> {
434 (**self).start_wps(config)
435 }
436
437 fn stop_wps(&mut self) -> Result<WpsStatus, EspError> {
438 (**self).stop_wps()
439 }
440
441 fn is_wps_finished(&self) -> Result<bool, EspError> {
442 (**self).is_wps_finished()
443 }
444}
445
446pub struct WifiDriver<'d> {
455 status: Arc<mutex::Mutex<WifiDriverStatus>>,
456 _subscription: EspSubscription<'static, System>,
457 #[cfg(all(feature = "alloc", esp_idf_comp_nvs_flash_enabled))]
458 _nvs: Option<EspDefaultNvsPartition>,
459 _p: PhantomData<&'d mut ()>,
460}
461
462#[derive(Clone, Debug)]
463struct WifiDriverStatus {
464 pub sta: WifiStaStatus,
465 pub scan: WifiScanStatus,
466 pub ap: WifiApStatus,
467 pub wps: Option<WpsStatus>,
468}
469
470impl<'d> WifiDriver<'d> {
471 #[cfg(all(feature = "alloc", esp_idf_comp_nvs_flash_enabled))]
472 pub fn new<M: WifiModemPeripheral + 'd>(
473 _modem: M,
474 sysloop: EspSystemEventLoop,
475 nvs: Option<EspDefaultNvsPartition>,
476 ) -> Result<Self, EspError> {
477 Self::init(nvs.is_some())?;
478
479 let (status, subscription) = Self::subscribe(&sysloop)?;
480
481 Ok(Self {
482 status,
483 _subscription: subscription,
484 _nvs: nvs,
485 _p: PhantomData,
486 })
487 }
488
489 #[cfg(not(all(feature = "alloc", esp_idf_comp_nvs_flash_enabled)))]
490 pub fn new<M: WifiModemPeripheral + 'd>(
491 _modem: M,
492 sysloop: EspSystemEventLoop,
493 ) -> Result<Self, EspError> {
494 Self::init(false)?;
495
496 let (status, subscription) = Self::subscribe(&sysloop)?;
497
498 Ok(Self {
499 status,
500 _subscription: subscription,
501 _p: PhantomData,
502 })
503 }
504
505 #[allow(clippy::type_complexity)]
506 fn subscribe(
507 sysloop: &EspEventLoop<System>,
508 ) -> Result<
509 (
510 Arc<mutex::Mutex<WifiDriverStatus>>,
511 EspSubscription<'static, System>,
512 ),
513 EspError,
514 > {
515 let status = Arc::new(mutex::Mutex::new(WifiDriverStatus {
516 sta: WifiStaStatus::Stopped,
517 ap: WifiApStatus::Stopped,
518 scan: WifiScanStatus::Idle,
519 wps: None,
520 }));
521 let s_status = status.clone();
522
523 let subscription = sysloop.subscribe::<WifiEvent, _>(move |event: WifiEvent| {
524 let mut guard = s_status.lock();
525
526 match event {
527 WifiEvent::ApStarted => guard.ap = WifiApStatus::Started,
528 WifiEvent::ApStopped => guard.ap = WifiApStatus::Stopped,
529 WifiEvent::StaStarted => guard.sta = WifiStaStatus::Started,
530 WifiEvent::StaStopped => guard.sta = WifiStaStatus::Stopped,
531 WifiEvent::StaConnected(_) => guard.sta = WifiStaStatus::Connected,
532 WifiEvent::StaDisconnected(_) => guard.sta = WifiStaStatus::Started,
533 WifiEvent::ScanDone(_) => guard.scan = WifiScanStatus::Done,
534 WifiEvent::StaWpsSuccess(_)
535 | WifiEvent::StaWpsFailed
536 | WifiEvent::StaWpsTimeout
537 | WifiEvent::StaWpsPin(_)
538 | WifiEvent::StaWpsPbcOverlap => guard.wps = Some((&event).try_into().unwrap()),
539 _ => (),
540 };
541 })?;
542
543 Ok((status, subscription))
544 }
545
546 fn init(nvs_enabled: bool) -> Result<(), EspError> {
547 #[allow(clippy::needless_update)]
548 #[allow(unused_unsafe)]
549 let cfg = wifi_init_config_t {
550 #[cfg(esp_idf_version_major = "4")]
551 event_handler: Some(esp_event_send_internal),
552 osi_funcs: unsafe { core::ptr::addr_of_mut!(g_wifi_osi_funcs) },
553 wpa_crypto_funcs: unsafe { g_wifi_default_wpa_crypto_funcs },
554 static_rx_buf_num: CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM as _,
555 dynamic_rx_buf_num: CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM as _,
556 tx_buf_type: CONFIG_ESP32_WIFI_TX_BUFFER_TYPE as _,
557 static_tx_buf_num: WIFI_STATIC_TX_BUFFER_NUM as _,
558 dynamic_tx_buf_num: WIFI_DYNAMIC_TX_BUFFER_NUM as _,
559 rx_mgmt_buf_type: CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as _,
560 rx_mgmt_buf_num: WIFI_RX_MGMT_BUF_NUM_DEF as _,
561 cache_tx_buf_num: WIFI_CACHE_TX_BUFFER_NUM as _,
562 csi_enable: WIFI_CSI_ENABLED as _,
563 ampdu_rx_enable: WIFI_AMPDU_RX_ENABLED as _,
564 ampdu_tx_enable: WIFI_AMPDU_TX_ENABLED as _,
565 amsdu_tx_enable: WIFI_AMSDU_TX_ENABLED as _,
566 nvs_enable: i32::from(nvs_enabled),
567 nano_enable: WIFI_NANO_FORMAT_ENABLED as _,
568 rx_ba_win: WIFI_DEFAULT_RX_BA_WIN as _,
569 wifi_task_core_id: WIFI_TASK_CORE_ID as _,
570 beacon_max_len: WIFI_SOFTAP_BEACON_MAX_LEN as _,
571 mgmt_sbuf_num: WIFI_MGMT_SBUF_NUM as _,
572 #[cfg(any(
573 esp_idf_version_major = "4",
574 all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
575 esp_idf_version_full = "5.1.0",
576 esp_idf_version_full = "5.1.1",
577 esp_idf_version_full = "5.1.2"
578 ))]
579 feature_caps: unsafe { g_wifi_feature_caps },
580 #[cfg(not(any(
581 esp_idf_version_major = "4",
582 all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
583 esp_idf_version_full = "5.1.0",
584 esp_idf_version_full = "5.1.1",
585 esp_idf_version_full = "5.1.2"
586 )))]
587 feature_caps: WIFI_FEATURE_CAPS as _,
588 sta_disconnected_pm: WIFI_STA_DISCONNECTED_PM_ENABLED != 0,
589 #[cfg(any(
591 not(esp_idf_version_major = "4"),
592 all(
593 esp_idf_version_major = "4",
594 any(
595 not(esp_idf_version_minor = "4"),
596 all(
597 not(esp_idf_version_patch = "0"),
598 not(esp_idf_version_patch = "1"),
599 not(esp_idf_version_patch = "2"),
600 not(esp_idf_version_patch = "3")
601 )
602 )
603 )
604 ))]
605 espnow_max_encrypt_num: CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM as i32,
606 magic: WIFI_INIT_CONFIG_MAGIC as _,
607 #[cfg(any(
609 all(not(esp_idf_version_major = "4"), not(esp_idf_version_major = "5")),
610 all(
611 esp_idf_version_major = "5",
612 not(esp_idf_version = "5.0"),
613 not(esp_idf_version = "5.1"),
614 not(esp_idf_version = "5.2"),
615 ),
616 ))]
617 tx_hetb_queue_num: WIFI_TX_HETB_QUEUE_NUM as _,
618 #[cfg(any(
620 all(not(esp_idf_version_major = "4"), not(esp_idf_version_major = "5")),
621 all(
622 esp_idf_version_major = "5",
623 not(esp_idf_version = "5.0"),
624 not(esp_idf_version = "5.1"),
625 not(esp_idf_version = "5.2"),
626 ),
627 ))]
628 dump_hesigb_enable: WIFI_DUMP_HESIGB_ENABLED != 0,
629 ..Default::default()
630 };
631 esp!(unsafe { esp_wifi_init(&cfg) })?;
632
633 ::log::debug!("Driver initialized");
634
635 Ok(())
636 }
637
638 pub fn get_capabilities(&self) -> Result<EnumSet<Capability>, EspError> {
641 let caps = Capability::Client | Capability::AccessPoint | Capability::Mixed;
642
643 ::log::debug!("Providing capabilities: {caps:?}");
644
645 Ok(caps)
646 }
647
648 pub fn start(&mut self) -> Result<(), EspError> {
650 ::log::debug!("Start requested");
651
652 esp!(unsafe { esp_wifi_start() })?;
653
654 ::log::debug!("Starting");
655
656 Ok(())
657 }
658
659 pub fn stop(&mut self) -> Result<(), EspError> {
661 ::log::debug!("Stop requested");
662
663 esp!(unsafe { esp_wifi_stop() })?;
664
665 ::log::debug!("Stopping");
666
667 Ok(())
668 }
669
670 pub fn connect(&mut self) -> Result<(), EspError> {
672 ::log::debug!("Connect requested");
673
674 esp!(unsafe { esp_wifi_connect() })?;
675
676 ::log::debug!("Connecting");
677
678 Ok(())
679 }
680
681 pub fn disconnect(&mut self) -> Result<(), EspError> {
683 ::log::debug!("Disconnect requested");
684
685 esp!(unsafe { esp_wifi_disconnect() })?;
686
687 ::log::debug!("Disconnecting");
688
689 Ok(())
690 }
691
692 pub fn is_ap_enabled(&self) -> Result<bool, EspError> {
695 let mut mode: wifi_mode_t = 0;
696 esp!(unsafe { esp_wifi_get_mode(&mut mode) })?;
697
698 Ok(mode == wifi_mode_t_WIFI_MODE_AP || mode == wifi_mode_t_WIFI_MODE_APSTA)
699 }
700
701 pub fn is_sta_enabled(&self) -> Result<bool, EspError> {
704 let mut mode: wifi_mode_t = 0;
705 esp!(unsafe { esp_wifi_get_mode(&mut mode) })?;
706
707 Ok(mode == wifi_mode_t_WIFI_MODE_STA || mode == wifi_mode_t_WIFI_MODE_APSTA)
708 }
709
710 pub fn is_ap_started(&self) -> Result<bool, EspError> {
711 Ok(matches!(self.status.lock().ap, WifiApStatus::Started))
712 }
713
714 pub fn is_sta_started(&self) -> Result<bool, EspError> {
715 let guard = self.status.lock();
716
717 Ok(matches!(
718 guard.sta,
719 WifiStaStatus::Started | WifiStaStatus::Connected
720 ))
721 }
722
723 pub fn is_sta_connected(&self) -> Result<bool, EspError> {
724 Ok(matches!(self.status.lock().sta, WifiStaStatus::Connected))
725 }
726
727 pub fn is_started(&self) -> Result<bool, EspError> {
728 let ap_enabled = self.is_ap_enabled()?;
729 let sta_enabled = self.is_sta_enabled()?;
730
731 if !ap_enabled && !sta_enabled {
732 Ok(false)
733 } else {
734 Ok(
735 (!ap_enabled || self.is_ap_started()?)
736 && (!sta_enabled || self.is_sta_started()?),
737 )
738 }
739 }
740
741 pub fn is_connected(&self) -> Result<bool, EspError> {
742 let ap_enabled = self.is_ap_enabled()?;
743 let sta_enabled = self.is_sta_enabled()?;
744
745 if !ap_enabled && !sta_enabled {
746 Ok(false)
747 } else {
748 let guard = self.status.lock();
749
750 Ok((!ap_enabled || matches!(guard.ap, WifiApStatus::Started))
751 && (!sta_enabled || matches!(guard.sta, WifiStaStatus::Connected)))
752 }
753 }
754
755 pub fn is_scan_done(&self) -> Result<bool, EspError> {
756 let guard = self.status.lock();
757
758 Ok(matches!(guard.scan, WifiScanStatus::Done))
759 }
760
761 #[allow(non_upper_case_globals)]
762 #[allow(non_snake_case)]
763 pub fn get_configuration(&self) -> Result<Configuration, EspError> {
765 ::log::debug!("Getting configuration");
766
767 let mut mode: wifi_mode_t = 0;
768 esp!(unsafe { esp_wifi_get_mode(&mut mode) })?;
769
770 let conf = match mode {
771 wifi_mode_t_WIFI_MODE_NULL => Configuration::None,
772 wifi_mode_t_WIFI_MODE_AP => Configuration::AccessPoint(self.get_ap_conf()?),
773 wifi_mode_t_WIFI_MODE_STA => Configuration::Client(self.get_sta_conf()?),
774 wifi_mode_t_WIFI_MODE_APSTA => {
775 Configuration::Mixed(self.get_sta_conf()?, self.get_ap_conf()?)
776 }
777 _ => panic!(),
778 };
779
780 ::log::debug!("Configuration gotten: {:?}", conf);
781
782 Ok(conf)
783 }
784
785 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), EspError> {
791 ::log::debug!("Setting configuration: {conf:?}");
792
793 match conf {
794 Configuration::None => {
795 unsafe {
796 esp!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
797 }
798 ::log::debug!("Wifi mode NULL set");
799 }
800 Configuration::AccessPoint(ap_conf) => {
801 unsafe {
802 esp!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_AP))?;
803 }
804 ::log::debug!("Wifi mode AP set");
805
806 self.set_ap_conf(ap_conf)?;
807 }
808 Configuration::Client(client_conf) => {
809 unsafe {
810 esp!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_STA))?;
811 }
812 ::log::debug!("Wifi mode STA set");
813
814 self.set_sta_conf(client_conf)?;
815 }
816 Configuration::Mixed(client_conf, ap_conf) => {
817 unsafe {
818 esp!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_APSTA))?;
819 }
820 ::log::debug!("Wifi mode APSTA set");
821
822 self.set_sta_conf(client_conf)?;
823 self.set_ap_conf(ap_conf)?;
824 }
825 }
826
827 ::log::debug!("Configuration set");
828
829 Ok(())
830 }
831
832 pub fn scan_n<const N: usize>(
852 &mut self,
853 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
854 self.start_scan(&Default::default(), true)?;
855 self.get_scan_result_n()
856 }
857
858 #[cfg(feature = "alloc")]
866 pub fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
867 self.start_scan(&Default::default(), true)?;
868 self.get_scan_result()
869 }
870
871 pub fn start_scan(
908 &mut self,
909 scan_config: &config::ScanConfig,
910 blocking: bool,
911 ) -> Result<(), EspError> {
912 ::log::debug!("About to scan for access points");
913
914 let scan_config: wifi_scan_config_t = scan_config.into();
915 esp!(unsafe { esp_wifi_scan_start(&scan_config as *const wifi_scan_config_t, blocking) })?;
916
917 self.status.lock().scan = WifiScanStatus::Started;
918
919 Ok(())
920 }
921
922 pub fn stop_scan(&mut self) -> Result<(), EspError> {
924 ::log::debug!("About to stop scan for access points");
925
926 esp!(unsafe { esp_wifi_scan_stop() })?;
927
928 self.status.lock().scan = WifiScanStatus::Idle;
929
930 Ok(())
931 }
932
933 pub fn get_scan_result_n<const N: usize>(
938 &mut self,
939 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
940 let scanned_count = self.get_scan_count()?;
941
942 let mut ap_infos_raw: heapless::Vec<wifi_ap_record_t, N> = heapless::Vec::new();
943 unsafe {
944 ap_infos_raw.set_len(scanned_count.min(N));
945 }
946
947 let fetched_count = self.fetch_scan_result(&mut ap_infos_raw)?;
948
949 let result = ap_infos_raw[..fetched_count]
950 .iter()
951 .map::<Result<AccessPointInfo, Utf8Error>, _>(|ap_info_raw| {
952 Newtype(ap_info_raw).try_into()
953 })
954 .filter_map(|r| r.ok())
955 .inspect(|ap_info| ::log::debug!("Found access point {ap_info:?}"))
956 .collect();
957
958 Ok((result, scanned_count))
959 }
960
961 #[cfg(feature = "alloc")]
968 pub fn get_scan_result(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
969 let scanned_count = self.get_scan_count()?;
970
971 let mut ap_infos_raw: alloc::vec::Vec<wifi_ap_record_t> =
972 alloc::vec::Vec::with_capacity(scanned_count);
973 #[allow(clippy::uninit_vec)]
974 unsafe {
976 ap_infos_raw.set_len(scanned_count)
977 };
978
979 let fetched_count = self.fetch_scan_result(&mut ap_infos_raw)?;
980
981 let result = ap_infos_raw[..fetched_count]
982 .iter()
983 .map::<Result<AccessPointInfo, Utf8Error>, _>(|ap_info_raw| {
984 Newtype(ap_info_raw).try_into()
985 })
986 .filter_map(|r| r.ok())
987 .inspect(|ap_info| ::log::debug!("Found access point {ap_info:?}"))
988 .collect();
989
990 Ok(result)
991 }
992
993 pub fn set_callbacks<R, T>(&mut self, rx_callback: R, tx_callback: T) -> Result<(), EspError>
997 where
998 R: FnMut(WifiDeviceId, WifiFrame) -> Result<(), EspError> + Send + 'static,
999 T: FnMut(WifiDeviceId, &[u8], bool) + Send + 'static,
1000 {
1001 self.internal_set_callbacks(rx_callback, tx_callback)
1002 }
1003
1004 pub unsafe fn set_nonstatic_callbacks<R, T>(
1032 &mut self,
1033 rx_callback: R,
1034 tx_callback: T,
1035 ) -> Result<(), EspError>
1036 where
1037 R: FnMut(WifiDeviceId, WifiFrame) -> Result<(), EspError> + Send + 'd,
1038 T: FnMut(WifiDeviceId, &[u8], bool) + Send + 'd,
1039 {
1040 self.internal_set_callbacks(rx_callback, tx_callback)
1041 }
1042
1043 fn internal_set_callbacks<R, T>(
1044 &mut self,
1045 mut rx_callback: R,
1046 mut tx_callback: T,
1047 ) -> Result<(), EspError>
1048 where
1049 R: FnMut(WifiDeviceId, WifiFrame) -> Result<(), EspError> + Send + 'd,
1050 T: FnMut(WifiDeviceId, &[u8], bool) + Send + 'd,
1051 {
1052 let _ = self.disconnect();
1053 let _ = self.stop();
1054
1055 #[allow(clippy::type_complexity)]
1056 let rx_callback: Box<
1057 Box<dyn FnMut(WifiDeviceId, WifiFrame) -> Result<(), EspError> + Send + 'd>,
1058 > = Box::new(Box::new(move |device_id, data| {
1059 rx_callback(device_id, data)
1060 }));
1061
1062 #[allow(clippy::type_complexity)]
1063 let tx_callback: Box<Box<dyn FnMut(WifiDeviceId, &[u8], bool) + Send + 'd>> =
1064 Box::new(Box::new(move |device_id, data, status| {
1065 tx_callback(device_id, data, status)
1066 }));
1067
1068 #[allow(clippy::type_complexity)]
1069 let rx_callback: Box<
1070 Box<dyn FnMut(WifiDeviceId, WifiFrame) -> Result<(), EspError> + Send + 'static>,
1071 > = unsafe { core::mem::transmute(rx_callback) };
1072
1073 #[allow(clippy::type_complexity)]
1074 let tx_callback: Box<Box<dyn FnMut(WifiDeviceId, &[u8], bool) + Send + 'static>> =
1075 unsafe { core::mem::transmute(tx_callback) };
1076
1077 unsafe {
1078 RX_CALLBACK = Some(rx_callback);
1079 TX_CALLBACK = Some(tx_callback);
1080
1081 esp!(esp_wifi_internal_reg_rxcb(
1082 WifiDeviceId::Ap.into(),
1083 Some(Self::handle_rx_ap),
1084 ))?;
1085
1086 esp!(esp_wifi_internal_reg_rxcb(
1087 WifiDeviceId::Sta.into(),
1088 Some(Self::handle_rx_sta),
1089 ))?;
1090
1091 esp!(esp_wifi_set_tx_done_cb(Some(Self::handle_tx)))?;
1092 }
1093
1094 Ok(())
1095 }
1096
1097 pub fn send(&mut self, device_id: WifiDeviceId, frame: &[u8]) -> Result<(), EspError> {
1099 esp!(unsafe {
1100 esp_wifi_internal_tx(device_id.into(), frame.as_ptr() as *mut _, frame.len() as _)
1101 })
1102 }
1103
1104 pub fn get_ap_info(&self) -> Result<AccessPointInfo, EspError> {
1107 let mut ap_info_raw: wifi_ap_record_t = wifi_ap_record_t::default();
1108 esp!(unsafe { esp_wifi_sta_get_ap_info(&mut ap_info_raw) })?;
1110 let ap_info: AccessPointInfo = Newtype(&ap_info_raw).try_into().unwrap();
1111
1112 ::log::debug!("AP Info: {ap_info:?}");
1113 Ok(ap_info)
1114 }
1115
1116 pub fn set_rssi_threshold(&mut self, rssi_threshold: i8) -> Result<(), EspError> {
1153 esp!(unsafe { esp_wifi_set_rssi_threshold(rssi_threshold.into()) })
1154 }
1155
1156 pub fn get_mac(&self, interface: WifiDeviceId) -> Result<[u8; 6], EspError> {
1159 let mut mac = [0u8; 6];
1160
1161 esp!(unsafe { esp_wifi_get_mac(interface.into(), mac.as_mut_ptr() as *mut _) })?;
1162
1163 Ok(mac)
1164 }
1165
1166 pub fn set_mac(&mut self, interface: WifiDeviceId, mac: [u8; 6]) -> Result<(), EspError> {
1169 esp!(unsafe { esp_wifi_set_mac(interface.into(), mac.as_ptr() as *mut _) })
1170 }
1171
1172 pub fn start_wps(&mut self, config: &WpsConfig) -> Result<(), EspError> {
1174 let config = Newtype::<esp_wps_config_t>::try_from(config)?;
1175
1176 match self.get_configuration()? {
1177 Configuration::None => esp!(unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_STA) })?,
1178 Configuration::AccessPoint(_) => {
1179 esp!(unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_APSTA) })?
1180 }
1181 _ => (),
1182 }
1183
1184 esp!(unsafe { esp_wifi_wps_enable(&config.0 as *const _) })?;
1185 #[cfg(not(esp_idf_version_at_least_6_0_0))]
1186 esp!(unsafe { esp_wifi_wps_start(0) })?;
1187 #[cfg(esp_idf_version_at_least_6_0_0)]
1188 esp!(unsafe { esp_wifi_wps_start() })?;
1189
1190 self.status.lock().wps = None;
1191
1192 Ok(())
1193 }
1194
1195 pub fn set_promiscuous(&mut self, state: bool) -> Result<(), EspError> {
1201 esp!(unsafe { esp_wifi_set_promiscuous(state) })?;
1202
1203 if state {
1204 ::log::info!("Driver set in promiscuous mode");
1205 } else {
1206 ::log::info!("Driver set in non-promiscuous mode");
1207 }
1208
1209 Ok(())
1210 }
1211
1212 pub fn is_promiscuous(&self) -> Result<bool, EspError> {
1214 let mut en: bool = false;
1215
1216 esp!(unsafe { esp_wifi_get_promiscuous(&mut en) })?;
1217
1218 Ok(en)
1219 }
1220
1221 fn stop_wps(&mut self) -> Result<WpsStatus, EspError> {
1228 let mut status = self.status.lock();
1229 if let Some(status) = status.wps.take() {
1230 esp!(unsafe { esp_wifi_wps_disable() })?;
1231 Ok(status)
1232 } else {
1233 Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>())
1234 }
1235 }
1236
1237 fn is_wps_finished(&self) -> Result<bool, EspError> {
1238 Ok(self.status.lock().wps.is_some())
1239 }
1240
1241 fn get_sta_conf(&self) -> Result<ClientConfiguration, EspError> {
1242 let mut wifi_config: wifi_config_t = Default::default();
1243 esp!(unsafe { esp_wifi_get_config(wifi_interface_t_WIFI_IF_STA, &mut wifi_config) })?;
1244
1245 let result: ClientConfiguration = unsafe { Newtype(wifi_config.sta).into() };
1246
1247 ::log::debug!("Providing STA configuration: {:?}", result);
1248
1249 Ok(result)
1250 }
1251
1252 fn set_sta_conf(&mut self, conf: &ClientConfiguration) -> Result<(), EspError> {
1253 ::log::debug!("Checking current STA configuration");
1254 let current_config = self.get_sta_conf()?;
1255
1256 if current_config != *conf {
1257 ::log::debug!("Setting STA configuration: {conf:?}");
1258
1259 let mut wifi_config = wifi_config_t {
1260 sta: Newtype::<wifi_sta_config_t>::try_from(conf)?.0,
1261 };
1262
1263 esp!(unsafe { esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut wifi_config) })?;
1264 } else {
1265 ::log::debug!("Same STA configuration already present");
1266 }
1267
1268 ::log::debug!("STA configuration done");
1269
1270 Ok(())
1271 }
1272
1273 fn get_ap_conf(&self) -> Result<AccessPointConfiguration, EspError> {
1274 let mut wifi_config: wifi_config_t = Default::default();
1275 esp!(unsafe { esp_wifi_get_config(wifi_interface_t_WIFI_IF_AP, &mut wifi_config) })?;
1276
1277 let result: AccessPointConfiguration = unsafe { Newtype(wifi_config.ap).into() };
1278
1279 ::log::debug!("Providing AP configuration: {:?}", result);
1280
1281 Ok(result)
1282 }
1283
1284 fn set_ap_conf(&mut self, conf: &AccessPointConfiguration) -> Result<(), EspError> {
1285 ::log::debug!("Checking current AP configuration");
1286 let current_config = self.get_ap_conf()?;
1287
1288 if current_config != *conf {
1289 ::log::debug!("Setting AP configuration: {conf:?}");
1290
1291 let mut wifi_config = wifi_config_t {
1292 ap: Newtype::<wifi_ap_config_t>::try_from(conf)?.0,
1293 };
1294
1295 esp!(unsafe { esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut wifi_config) })?;
1296 } else {
1297 ::log::debug!("Same AP configuration already present");
1298 }
1299
1300 ::log::debug!("AP configuration done");
1301
1302 Ok(())
1303 }
1304
1305 fn clear_all(&mut self) -> Result<(), EspError> {
1306 let _ = self.disconnect();
1307 let _ = self.stop();
1308
1309 unsafe {
1310 esp!(esp_wifi_deinit())?;
1311 }
1312
1313 unsafe {
1314 RX_CALLBACK = None;
1316 TX_CALLBACK = None;
1317 }
1318
1319 ::log::debug!("Driver deinitialized");
1320
1321 Ok(())
1322 }
1323
1324 fn get_scan_count(&mut self) -> Result<usize, EspError> {
1325 let mut found_ap: u16 = 0;
1326 esp!(unsafe { esp_wifi_scan_get_ap_num(&mut found_ap as *mut _) })?;
1327
1328 ::log::debug!("Found {found_ap} access points");
1329
1330 Ok(found_ap as usize)
1331 }
1332
1333 fn fetch_scan_result(
1334 &mut self,
1335 ap_infos_raw: &mut [wifi_ap_record_t],
1336 ) -> Result<usize, EspError> {
1337 ::log::debug!("About to get info for found access points");
1338
1339 let mut ap_count: u16 = ap_infos_raw.len() as u16;
1340
1341 esp!(unsafe { esp_wifi_scan_get_ap_records(&mut ap_count, ap_infos_raw.as_mut_ptr(),) })?;
1342
1343 ::log::debug!("Got info for {ap_count} access points");
1344
1345 Ok(ap_count as usize)
1346 }
1347
1348 unsafe extern "C" fn handle_rx_ap(
1349 buf: *mut ffi::c_void,
1350 len: u16,
1351 eb: *mut ffi::c_void,
1352 ) -> esp_err_t {
1353 Self::handle_rx(WifiDeviceId::Ap, buf, len, eb)
1354 }
1355
1356 unsafe extern "C" fn handle_rx_sta(
1357 buf: *mut ffi::c_void,
1358 len: u16,
1359 eb: *mut ffi::c_void,
1360 ) -> esp_err_t {
1361 Self::handle_rx(WifiDeviceId::Sta, buf, len, eb)
1362 }
1363
1364 unsafe fn handle_rx(
1365 device_id: WifiDeviceId,
1366 buf: *mut ffi::c_void,
1367 len: u16,
1368 eb: *mut ffi::c_void,
1369 ) -> esp_err_t {
1370 #[allow(static_mut_refs)]
1371 let res = RX_CALLBACK.as_mut().unwrap()(device_id, WifiFrame::new(buf.cast(), len, eb));
1372
1373 match res {
1374 Ok(_) => ESP_OK,
1375 Err(e) => e.code(),
1376 }
1377 }
1378
1379 unsafe extern "C" fn handle_tx(ifidx: u8, data: *mut u8, len: *mut u16, tx_status: bool) {
1380 #[allow(static_mut_refs)]
1381 TX_CALLBACK.as_mut().unwrap()(
1382 (ifidx as wifi_interface_t).into(),
1383 core::slice::from_raw_parts(data as *const _, len as usize),
1384 tx_status,
1385 );
1386 }
1387
1388 pub fn get_rssi(&self) -> Result<i32, EspError> {
1389 let mut rssi: core::ffi::c_int = 0;
1390 unsafe {
1391 esp_wifi_sta_get_rssi(&mut rssi as *mut core::ffi::c_int);
1392 };
1393 Ok(rssi as i32)
1394 }
1395}
1396
1397unsafe impl Send for WifiDriver<'_> {}
1398
1399impl NonBlocking for WifiDriver<'_> {
1400 fn is_scan_done(&self) -> Result<bool, EspError> {
1401 WifiDriver::is_scan_done(self)
1402 }
1403
1404 fn start_scan(
1405 &mut self,
1406 scan_config: &config::ScanConfig,
1407 blocking: bool,
1408 ) -> Result<(), EspError> {
1409 WifiDriver::start_scan(self, scan_config, blocking)
1410 }
1411
1412 fn stop_scan(&mut self) -> Result<(), EspError> {
1413 WifiDriver::stop_scan(self)
1414 }
1415
1416 fn get_scan_result_n<const N: usize>(
1417 &mut self,
1418 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
1419 WifiDriver::get_scan_result_n(self)
1420 }
1421
1422 #[cfg(feature = "alloc")]
1423 fn get_scan_result(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
1424 WifiDriver::get_scan_result(self)
1425 }
1426
1427 fn start_wps(&mut self, config: &WpsConfig) -> Result<(), EspError> {
1428 WifiDriver::start_wps(self, config)
1429 }
1430
1431 fn stop_wps(&mut self) -> Result<WpsStatus, EspError> {
1432 WifiDriver::stop_wps(self)
1433 }
1434
1435 fn is_wps_finished(&self) -> Result<bool, EspError> {
1436 WifiDriver::is_wps_finished(self)
1437 }
1438}
1439
1440impl Drop for WifiDriver<'_> {
1441 fn drop(&mut self) {
1442 self.clear_all().unwrap();
1443
1444 ::log::debug!("WifiDriver Dropped");
1445 }
1446}
1447
1448impl Wifi for WifiDriver<'_> {
1449 type Error = EspError;
1450
1451 fn get_capabilities(&self) -> Result<EnumSet<Capability>, Self::Error> {
1452 WifiDriver::get_capabilities(self)
1453 }
1454
1455 fn is_started(&self) -> Result<bool, Self::Error> {
1456 WifiDriver::is_started(self)
1457 }
1458
1459 fn is_connected(&self) -> Result<bool, Self::Error> {
1460 WifiDriver::is_connected(self)
1461 }
1462
1463 fn get_configuration(&self) -> Result<Configuration, Self::Error> {
1464 WifiDriver::get_configuration(self)
1465 }
1466
1467 fn set_configuration(&mut self, conf: &Configuration) -> Result<(), Self::Error> {
1468 WifiDriver::set_configuration(self, conf)
1469 }
1470
1471 fn start(&mut self) -> Result<(), Self::Error> {
1472 WifiDriver::start(self)
1473 }
1474
1475 fn stop(&mut self) -> Result<(), Self::Error> {
1476 WifiDriver::stop(self)
1477 }
1478
1479 fn connect(&mut self) -> Result<(), Self::Error> {
1480 WifiDriver::connect(self)
1481 }
1482
1483 fn disconnect(&mut self) -> Result<(), Self::Error> {
1484 WifiDriver::disconnect(self)
1485 }
1486
1487 fn scan_n<const N: usize>(
1488 &mut self,
1489 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), Self::Error> {
1490 WifiDriver::scan_n(self)
1491 }
1492
1493 #[cfg(feature = "alloc")]
1494 fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, Self::Error> {
1495 WifiDriver::scan(self)
1496 }
1497}
1498
1499pub struct WifiFrame {
1500 buf: *mut u8,
1501 len: u16,
1502 eb: *mut ffi::c_void,
1503}
1504
1505unsafe impl Send for WifiFrame {}
1506
1507impl WifiFrame {
1508 const unsafe fn new(buf: *mut u8, len: u16, eb: *mut ffi::c_void) -> Self {
1509 Self { buf, len, eb }
1510 }
1511
1512 pub const fn as_slice(&self) -> &[u8] {
1513 unsafe { core::slice::from_raw_parts(self.buf, self.len as _) }
1514 }
1515
1516 pub fn as_mut_slice(&mut self) -> &mut [u8] {
1517 unsafe { core::slice::from_raw_parts_mut(self.buf, self.len as _) }
1518 }
1519}
1520
1521impl ops::Deref for WifiFrame {
1522 type Target = [u8];
1523
1524 fn deref(&self) -> &[u8] {
1525 unsafe { core::slice::from_raw_parts(self.buf, self.len as _) }
1526 }
1527}
1528
1529impl ops::DerefMut for WifiFrame {
1530 fn deref_mut(&mut self) -> &mut [u8] {
1531 unsafe { core::slice::from_raw_parts_mut(self.buf, self.len as _) }
1532 }
1533}
1534
1535impl Drop for WifiFrame {
1536 fn drop(&mut self) {
1537 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1538 }
1539}
1540
1541#[cfg(esp_idf_comp_esp_netif_enabled)]
1553pub struct EspWifi<'d> {
1554 #[cfg(esp_idf_esp_wifi_softap_support)]
1555 ap_netif: EspNetif,
1556 sta_netif: EspNetif,
1557 driver: WifiDriver<'d>,
1558}
1559
1560#[cfg(esp_idf_comp_esp_netif_enabled)]
1561impl<'d> EspWifi<'d> {
1562 #[cfg(all(feature = "alloc", esp_idf_comp_nvs_flash_enabled))]
1563 pub fn new<M: WifiModemPeripheral + 'd>(
1564 modem: M,
1565 sysloop: EspSystemEventLoop,
1566 nvs: Option<EspDefaultNvsPartition>,
1567 ) -> Result<Self, EspError> {
1568 Self::wrap(WifiDriver::new(modem, sysloop, nvs)?)
1569 }
1570
1571 #[cfg(not(all(feature = "alloc", esp_idf_comp_nvs_flash_enabled)))]
1572 pub fn new<M: WifiModemPeripheral + 'd>(
1573 modem: M,
1574 sysloop: EspSystemEventLoop,
1575 ) -> Result<Self, EspError> {
1576 Self::wrap(WifiDriver::new(modem, sysloop)?)
1577 }
1578
1579 pub fn wrap(driver: WifiDriver<'d>) -> Result<Self, EspError> {
1580 Self::wrap_all(
1581 driver,
1582 EspNetif::new(NetifStack::Sta)?,
1583 #[cfg(esp_idf_esp_wifi_softap_support)]
1584 EspNetif::new(NetifStack::Ap)?,
1585 )
1586 }
1587
1588 pub fn wrap_all(
1589 driver: WifiDriver<'d>,
1590 sta_netif: EspNetif,
1591 #[cfg(esp_idf_esp_wifi_softap_support)] ap_netif: EspNetif,
1592 ) -> Result<Self, EspError> {
1593 let mut this = Self {
1594 driver,
1595 sta_netif,
1596 #[cfg(esp_idf_esp_wifi_softap_support)]
1597 ap_netif,
1598 };
1599
1600 this.attach_netif()?;
1601
1602 Ok(this)
1603 }
1604
1605 #[cfg(esp_idf_esp_wifi_softap_support)]
1606 pub fn swap_netif(
1608 &mut self,
1609 sta_netif: EspNetif,
1610 ap_netif: EspNetif,
1611 ) -> Result<(EspNetif, EspNetif), EspError> {
1612 self.detach_netif()?;
1613
1614 let old_sta = core::mem::replace(&mut self.sta_netif, sta_netif);
1615 let old_ap = core::mem::replace(&mut self.ap_netif, ap_netif);
1616
1617 self.attach_netif()?;
1618
1619 Ok((old_sta, old_ap))
1620 }
1621
1622 pub fn swap_netif_sta(&mut self, sta_netif: EspNetif) -> Result<EspNetif, EspError> {
1625 self.detach_netif()?;
1626
1627 let old = core::mem::replace(&mut self.sta_netif, sta_netif);
1628
1629 self.attach_netif()?;
1630
1631 Ok(old)
1632 }
1633
1634 #[cfg(esp_idf_esp_wifi_softap_support)]
1635 pub fn swap_netif_ap(&mut self, ap_netif: EspNetif) -> Result<EspNetif, EspError> {
1638 self.detach_netif()?;
1639
1640 let old = core::mem::replace(&mut self.ap_netif, ap_netif);
1641
1642 self.attach_netif()?;
1643
1644 Ok(old)
1645 }
1646
1647 pub fn driver(&self) -> &WifiDriver<'d> {
1649 &self.driver
1650 }
1651
1652 pub fn driver_mut(&mut self) -> &mut WifiDriver<'d> {
1654 &mut self.driver
1655 }
1656
1657 pub fn sta_netif(&self) -> &EspNetif {
1659 &self.sta_netif
1660 }
1661
1662 pub fn sta_netif_mut(&mut self) -> &mut EspNetif {
1664 &mut self.sta_netif
1665 }
1666
1667 #[cfg(esp_idf_esp_wifi_softap_support)]
1668 pub fn ap_netif(&self) -> &EspNetif {
1670 &self.ap_netif
1671 }
1672
1673 #[cfg(esp_idf_esp_wifi_softap_support)]
1674 pub fn ap_netif_mut(&mut self) -> &mut EspNetif {
1676 &mut self.ap_netif
1677 }
1678
1679 pub fn get_capabilities(&self) -> Result<EnumSet<Capability>, EspError> {
1681 self.driver().get_capabilities()
1682 }
1683
1684 pub fn is_started(&self) -> Result<bool, EspError> {
1686 self.driver().is_started()
1687 }
1688
1689 pub fn is_connected(&self) -> Result<bool, EspError> {
1691 self.driver().is_connected()
1692 }
1693
1694 pub fn is_up(&self) -> Result<bool, EspError> {
1697 if !self.driver().is_connected()? {
1698 Ok(false)
1699 } else {
1700 let sta_enabled = self.driver().is_sta_enabled()?;
1701 let ok = !sta_enabled || self.sta_netif().is_up()?;
1702
1703 #[cfg(esp_idf_esp_wifi_softap_support)]
1704 let ok = ok && {
1705 let ap_enabled = self.driver().is_ap_enabled()?;
1706 !ap_enabled || self.ap_netif().is_up()?
1707 };
1708 Ok(ok)
1709 }
1710 }
1711
1712 pub fn get_configuration(&self) -> Result<Configuration, EspError> {
1714 self.driver().get_configuration()
1715 }
1716
1717 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), EspError> {
1719 self.driver_mut().set_configuration(conf)
1720 }
1721
1722 pub fn start(&mut self) -> Result<(), EspError> {
1724 self.driver_mut().start()
1725 }
1726
1727 pub fn stop(&mut self) -> Result<(), EspError> {
1729 self.driver_mut().stop()
1730 }
1731
1732 pub fn connect(&mut self) -> Result<(), EspError> {
1734 self.driver_mut().connect()
1735 }
1736
1737 pub fn disconnect(&mut self) -> Result<(), EspError> {
1739 self.driver_mut().disconnect()
1740 }
1741
1742 pub fn is_scan_done(&self) -> Result<bool, EspError> {
1744 self.driver().is_scan_done()
1745 }
1746
1747 pub fn scan_n<const N: usize>(
1749 &mut self,
1750 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
1751 self.driver_mut().scan_n()
1752 }
1753
1754 #[cfg(feature = "alloc")]
1756 pub fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
1757 self.driver_mut().scan()
1758 }
1759
1760 pub fn start_scan(
1762 &mut self,
1763 scan_config: &config::ScanConfig,
1764 blocking: bool,
1765 ) -> Result<(), EspError> {
1766 self.driver_mut().start_scan(scan_config, blocking)
1767 }
1768
1769 pub fn stop_scan(&mut self) -> Result<(), EspError> {
1771 self.driver_mut().stop_scan()
1772 }
1773
1774 pub fn get_scan_result_n<const N: usize>(
1776 &mut self,
1777 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
1778 self.driver_mut().get_scan_result_n()
1779 }
1780
1781 #[cfg(feature = "alloc")]
1783 pub fn get_scan_result(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
1784 self.driver_mut().get_scan_result()
1785 }
1786
1787 pub fn start_wps(&mut self, config: &WpsConfig) -> Result<(), EspError> {
1789 self.driver_mut().start_wps(config)
1790 }
1791
1792 pub fn stop_wps(&mut self) -> Result<WpsStatus, EspError> {
1793 self.driver_mut().stop_wps()
1794 }
1795
1796 pub fn is_wps_finished(&self) -> Result<bool, EspError> {
1797 self.driver().is_wps_finished()
1798 }
1799
1800 pub fn get_mac(&self, interface: WifiDeviceId) -> Result<[u8; 6], EspError> {
1802 self.driver().get_mac(interface)
1803 }
1804
1805 pub fn set_mac(&mut self, interface: WifiDeviceId, mac: [u8; 6]) -> Result<(), EspError> {
1807 self.driver_mut().set_mac(interface, mac)
1808 }
1809
1810 fn attach_netif(&mut self) -> Result<(), EspError> {
1811 let _ = self.driver.stop();
1812
1813 #[cfg(esp_idf_esp_wifi_softap_support)]
1814 {
1815 esp!(unsafe { esp_netif_attach_wifi_ap(self.ap_netif.handle()) })?;
1816 esp!(unsafe { esp_wifi_set_default_wifi_ap_handlers() })?;
1817 }
1818
1819 esp!(unsafe { esp_netif_attach_wifi_station(self.sta_netif.handle()) })?;
1820 esp!(unsafe { esp_wifi_set_default_wifi_sta_handlers() })?;
1821
1822 Ok(())
1823 }
1824
1825 fn detach_netif(&mut self) -> Result<(), EspError> {
1826 let _ = self.driver.stop();
1827
1828 #[cfg(esp_idf_esp_wifi_softap_support)]
1829 esp!(unsafe {
1830 esp_wifi_clear_default_wifi_driver_and_handlers(
1831 self.ap_netif.handle() as *mut ffi::c_void
1832 )
1833 })?;
1834
1835 esp!(unsafe {
1836 esp_wifi_clear_default_wifi_driver_and_handlers(
1837 self.sta_netif.handle() as *mut ffi::c_void
1838 )
1839 })?;
1840
1841 Ok(())
1842 }
1843
1844 pub fn get_rssi(&self) -> Result<i32, EspError> {
1845 self.driver().get_rssi()
1846 }
1847
1848 pub fn get_ap_info(&self) -> Result<AccessPointInfo, EspError> {
1851 self.driver().get_ap_info()
1852 }
1853}
1854
1855#[cfg(esp_idf_comp_esp_netif_enabled)]
1856impl Drop for EspWifi<'_> {
1857 fn drop(&mut self) {
1858 self.detach_netif().unwrap();
1859
1860 ::log::info!("EspWifi dropped");
1861 }
1862}
1863
1864#[cfg(esp_idf_comp_esp_netif_enabled)]
1865unsafe impl Send for EspWifi<'_> {}
1866
1867#[cfg(esp_idf_comp_esp_netif_enabled)]
1868impl NonBlocking for EspWifi<'_> {
1869 fn is_scan_done(&self) -> Result<bool, EspError> {
1870 EspWifi::is_scan_done(self)
1871 }
1872
1873 fn start_scan(
1874 &mut self,
1875 scan_config: &config::ScanConfig,
1876 blocking: bool,
1877 ) -> Result<(), EspError> {
1878 EspWifi::start_scan(self, scan_config, blocking)
1879 }
1880
1881 fn stop_scan(&mut self) -> Result<(), EspError> {
1882 EspWifi::stop_scan(self)
1883 }
1884
1885 fn get_scan_result_n<const N: usize>(
1886 &mut self,
1887 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
1888 EspWifi::get_scan_result_n(self)
1889 }
1890
1891 #[cfg(feature = "alloc")]
1892 fn get_scan_result(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
1893 EspWifi::get_scan_result(self)
1894 }
1895
1896 fn start_wps(&mut self, config: &WpsConfig) -> Result<(), EspError> {
1897 EspWifi::start_wps(self, config)
1898 }
1899
1900 fn stop_wps(&mut self) -> Result<WpsStatus, EspError> {
1901 EspWifi::stop_wps(self)
1902 }
1903
1904 fn is_wps_finished(&self) -> Result<bool, EspError> {
1905 EspWifi::is_wps_finished(self)
1906 }
1907}
1908
1909#[cfg(esp_idf_comp_esp_netif_enabled)]
1910impl Wifi for EspWifi<'_> {
1911 type Error = EspError;
1912
1913 fn get_capabilities(&self) -> Result<EnumSet<Capability>, Self::Error> {
1914 EspWifi::get_capabilities(self)
1915 }
1916
1917 fn is_started(&self) -> Result<bool, Self::Error> {
1918 EspWifi::is_started(self)
1919 }
1920
1921 fn is_connected(&self) -> Result<bool, Self::Error> {
1922 EspWifi::is_connected(self)
1923 }
1924
1925 fn get_configuration(&self) -> Result<Configuration, Self::Error> {
1926 EspWifi::get_configuration(self)
1927 }
1928
1929 fn set_configuration(&mut self, conf: &Configuration) -> Result<(), Self::Error> {
1930 EspWifi::set_configuration(self, conf)
1931 }
1932
1933 fn start(&mut self) -> Result<(), Self::Error> {
1934 EspWifi::start(self)
1935 }
1936
1937 fn stop(&mut self) -> Result<(), Self::Error> {
1938 EspWifi::stop(self)
1939 }
1940
1941 fn connect(&mut self) -> Result<(), Self::Error> {
1942 EspWifi::connect(self)
1943 }
1944
1945 fn disconnect(&mut self) -> Result<(), Self::Error> {
1946 EspWifi::disconnect(self)
1947 }
1948
1949 fn scan_n<const N: usize>(
1950 &mut self,
1951 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), Self::Error> {
1952 EspWifi::scan_n(self)
1953 }
1954
1955 fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, Self::Error> {
1956 EspWifi::scan(self)
1957 }
1958}
1959
1960#[cfg(esp_idf_comp_esp_netif_enabled)]
1961impl NetifStatus for EspWifi<'_> {
1962 fn is_up(&self) -> Result<bool, EspError> {
1963 EspWifi::is_up(self)
1964 }
1965}
1966
1967#[derive(Copy, Clone)]
1968#[repr(transparent)]
1969pub struct StaScanDoneRef(wifi_event_sta_scan_done_t);
1970
1971impl StaScanDoneRef {
1972 pub fn is_successful(&self) -> bool {
1974 self.0.status == 0
1975 }
1976
1977 pub fn len(&self) -> usize {
1979 self.0.number as usize
1980 }
1981
1982 pub fn is_empty(&self) -> bool {
1983 self.len() == 0
1984 }
1985
1986 pub fn id(&self) -> u8 {
1988 self.0.scan_id
1989 }
1990}
1991
1992impl fmt::Debug for StaScanDoneRef {
1993 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1994 f.debug_struct("StaScanDoneRef")
1995 .field("is_successful", &self.is_successful())
1996 .field("len", &self.len())
1997 .field("id", &self.id())
1998 .finish()
1999 }
2000}
2001
2002#[derive(Copy, Clone)]
2003#[repr(transparent)]
2004pub struct StaConnectedRef(wifi_event_sta_connected_t);
2005
2006impl StaConnectedRef {
2007 pub fn ssid(&self) -> &[u8] {
2009 &self.0.ssid.as_slice()[..self.0.ssid_len as usize]
2010 }
2011
2012 pub fn bssid(&self) -> [u8; 6] {
2014 self.0.bssid
2015 }
2016
2017 pub fn channel(&self) -> u8 {
2019 self.0.channel
2020 }
2021
2022 pub fn authmode(&self) -> AuthMethod {
2024 Option::<AuthMethod>::from(Newtype(self.0.authmode)).unwrap()
2025 }
2026
2027 #[cfg(not(esp_idf_version_major = "4"))]
2028 pub fn aid(&self) -> u16 {
2030 self.0.aid
2031 }
2032}
2033
2034impl fmt::Debug for StaConnectedRef {
2035 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2036 let mut d = f.debug_struct("StaConnectedRef");
2037
2038 let d = d
2039 .field("ssid", &alloc::string::String::from_utf8_lossy(self.ssid()))
2040 .field("bssid", &self.bssid())
2041 .field("channel", &self.channel())
2042 .field("authmode", &self.authmode());
2043
2044 #[cfg(not(esp_idf_version_major = "4"))]
2045 let d = d.field("aid", &self.aid());
2046
2047 d.finish()
2048 }
2049}
2050
2051#[derive(Copy, Clone)]
2052#[repr(transparent)]
2053pub struct StaDisconnectedRef(wifi_event_sta_disconnected_t);
2054
2055impl StaDisconnectedRef {
2056 pub fn ssid(&self) -> &[u8] {
2058 &self.0.ssid.as_slice()[..self.0.ssid_len as usize]
2059 }
2060
2061 pub fn bssid(&self) -> [u8; 6] {
2063 self.0.bssid
2064 }
2065
2066 pub fn reason(&self) -> u16 {
2068 self.0.reason as u16
2069 }
2070
2071 pub fn rssi(&self) -> i8 {
2073 self.0.rssi
2074 }
2075}
2076
2077impl fmt::Debug for StaDisconnectedRef {
2078 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2079 f.debug_struct("StaDisconnectedRef")
2080 .field("ssid", &alloc::string::String::from_utf8_lossy(self.ssid()))
2081 .field("bssid", &self.bssid())
2082 .field("reason", &self.reason())
2083 .field("rssi", &self.rssi())
2084 .finish()
2085 }
2086}
2087
2088#[derive(Copy, Clone)]
2089#[repr(transparent)]
2090pub struct ApStaConnectedRef(wifi_event_ap_staconnected_t);
2091
2092impl ApStaConnectedRef {
2093 pub fn mac(&self) -> [u8; 6] {
2095 self.0.mac
2096 }
2097
2098 pub fn aid(&self) -> u8 {
2100 self.0.aid
2101 }
2102
2103 pub fn is_mesh_child(&self) -> bool {
2105 self.0.is_mesh_child
2106 }
2107}
2108
2109#[derive(Copy, Clone)]
2110#[repr(transparent)]
2111pub struct ApStaDisconnectedRef(wifi_event_ap_stadisconnected_t);
2112
2113impl ApStaDisconnectedRef {
2114 pub fn mac(&self) -> [u8; 6] {
2116 self.0.mac
2117 }
2118
2119 pub fn aid(&self) -> u8 {
2121 self.0.aid
2122 }
2123
2124 pub fn is_mesh_child(&self) -> bool {
2126 self.0.is_mesh_child
2127 }
2128
2129 #[cfg(not(any(
2131 esp_idf_version_major = "4",
2132 all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
2133 )))]
2134 #[allow(clippy::unnecessary_cast)]
2135 pub fn reason(&self) -> u16 {
2136 self.0.reason as u16
2137 }
2138}
2139
2140impl fmt::Debug for ApStaDisconnectedRef {
2141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2142 let mut ds = f.debug_struct("ApStaDisconnectedRef");
2143 ds.field("mac", &self.mac())
2144 .field("aid", &self.aid())
2145 .field("is_mesh_child", &self.is_mesh_child());
2146
2147 #[cfg(not(any(
2148 esp_idf_version_major = "4",
2149 all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
2150 )))]
2151 ds.field("reason", &self.reason());
2152
2153 ds.finish()
2154 }
2155}
2156
2157#[cfg(not(any(
2158 esp_idf_version_major = "4",
2159 all(
2160 esp_idf_version_major = "5",
2161 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
2162 ),
2163)))]
2164#[derive(Copy, Clone, Debug)]
2165#[repr(C)]
2166pub struct HomeChannelChange {
2167 old_chan: u8,
2168 old_snd: Option<WifiSecondChan>,
2169 new_chan: u8,
2170 new_snd: Option<WifiSecondChan>,
2171}
2172
2173#[cfg(not(any(
2174 esp_idf_version_major = "4",
2175 all(
2176 esp_idf_version_major = "5",
2177 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
2178 ),
2179)))]
2180#[derive(Copy, Clone, Debug)]
2181enum WifiSecondChan {
2182 None = 0,
2183 Above,
2184 Below,
2185}
2186
2187#[cfg(not(any(
2188 esp_idf_version_major = "4",
2189 all(
2190 esp_idf_version_major = "5",
2191 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
2192 ),
2193)))]
2194impl TryFrom<u32> for WifiSecondChan {
2195 type Error = &'static str;
2196
2197 fn try_from(value: u32) -> Result<Self, Self::Error> {
2198 #![allow(non_upper_case_globals)]
2199 #[allow(non_snake_case)]
2200 match value {
2201 wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => Ok(Self::None),
2202 wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => Ok(Self::Above),
2203 wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => Ok(Self::Below),
2204 _ => Err("Invalid"),
2205 }
2206 }
2207}
2208
2209#[cfg(esp_idf_version_at_least_5_3_0)]
2214#[repr(transparent)]
2215pub struct StaNeighborRepRef(wifi_event_neighbor_report_t);
2216
2217#[cfg(esp_idf_version_at_least_5_3_0)]
2218impl StaNeighborRepRef {
2219 pub fn report_len(&self) -> usize {
2221 self.0.report_len as usize
2222 }
2223
2224 pub fn report(&self) -> &[u8] {
2229 unsafe { core::slice::from_raw_parts(self.0.n_report.as_ptr(), self.report_len()) }
2235 }
2236}
2237
2238#[cfg(esp_idf_version_at_least_5_3_0)]
2239impl fmt::Debug for StaNeighborRepRef {
2240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2241 f.debug_struct("StaNeighborRepRef")
2242 .field("report_len", &self.report_len())
2243 .field("report", &self.report())
2244 .finish()
2245 }
2246}
2247
2248#[cfg(any(
2250 esp_idf_version_patch_at_least_5_3_3,
2251 esp_idf_version_patch_at_least_5_4_1,
2252 esp_idf_version_at_least_5_5_0,
2253))]
2254#[derive(Copy, Clone)]
2255#[repr(transparent)]
2256pub struct ApWrongPasswordRef(wifi_event_ap_wrong_password_t);
2257
2258#[cfg(any(
2259 esp_idf_version_patch_at_least_5_3_3,
2260 esp_idf_version_patch_at_least_5_4_1,
2261 esp_idf_version_at_least_5_5_0,
2262))]
2263impl ApWrongPasswordRef {
2264 pub fn mac(&self) -> [u8; 6] {
2266 self.0.mac
2267 }
2268}
2269
2270#[cfg(any(
2271 esp_idf_version_patch_at_least_5_3_3,
2272 esp_idf_version_patch_at_least_5_4_1,
2273 esp_idf_version_at_least_5_5_0,
2274))]
2275impl fmt::Debug for ApWrongPasswordRef {
2276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2277 f.debug_struct("ApWrongPasswordRef")
2278 .field("mac", &self.mac())
2279 .finish()
2280 }
2281}
2282
2283#[cfg(esp_idf_version_at_least_5_5_0)]
2285#[derive(Copy, Clone)]
2286#[repr(transparent)]
2287pub struct StaBeaconOffsetUnstableRef(wifi_event_sta_beacon_offset_unstable_t);
2288
2289#[cfg(esp_idf_version_at_least_5_5_0)]
2290impl StaBeaconOffsetUnstableRef {
2291 pub fn beacon_success_rate(&self) -> f32 {
2293 self.0.beacon_success_rate
2294 }
2295}
2296
2297#[cfg(esp_idf_version_at_least_5_5_0)]
2298impl fmt::Debug for StaBeaconOffsetUnstableRef {
2299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2300 f.debug_struct("StaBeaconOffsetUnstableRef")
2301 .field("beacon_success_rate", &self.beacon_success_rate())
2302 .finish()
2303 }
2304}
2305
2306#[cfg(esp_idf_version_at_least_5_5_0)]
2312#[repr(transparent)]
2313pub struct DppUriReadyRef(wifi_event_dpp_uri_ready_t);
2314
2315#[cfg(esp_idf_version_at_least_5_5_0)]
2316impl DppUriReadyRef {
2317 pub fn uri_len(&self) -> usize {
2319 (self.0.uri_data_len as usize).saturating_sub(1)
2320 }
2321
2322 pub fn uri(&self) -> &str {
2331 unsafe {
2335 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
2336 self.0.uri.as_ptr().cast::<u8>(),
2337 self.uri_len(),
2338 ))
2339 }
2340 }
2341}
2342
2343#[cfg(esp_idf_version_at_least_5_5_0)]
2344impl fmt::Debug for DppUriReadyRef {
2345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2346 f.debug_struct("DppUriReadyRef")
2347 .field("uri", &self.uri())
2348 .finish()
2349 }
2350}
2351
2352#[cfg(esp_idf_version_at_least_5_5_0)]
2360#[repr(transparent)]
2361pub struct DppCfgRecvdRef(wifi_event_dpp_config_received_t);
2362
2363#[cfg(esp_idf_version_at_least_5_5_0)]
2364impl DppCfgRecvdRef {
2365 pub fn wifi_cfg(&self) -> &wifi_config_t {
2367 &self.0.wifi_cfg
2368 }
2369}
2370
2371#[cfg(esp_idf_version_at_least_5_5_0)]
2372impl fmt::Debug for DppCfgRecvdRef {
2373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2374 f.debug_struct("DppCfgRecvdRef").finish_non_exhaustive()
2375 }
2376}
2377
2378#[cfg(esp_idf_version_at_least_5_5_0)]
2380#[derive(Copy, Clone)]
2381#[repr(transparent)]
2382pub struct DppFailedRef(wifi_event_dpp_failed_t);
2383
2384#[cfg(esp_idf_version_at_least_5_5_0)]
2385impl DppFailedRef {
2386 pub fn failure_reason(&self) -> i32 {
2388 self.0.failure_reason
2389 }
2390}
2391
2392#[cfg(esp_idf_version_at_least_5_5_0)]
2393impl fmt::Debug for DppFailedRef {
2394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2395 f.debug_struct("DppFailedRef")
2396 .field("failure_reason", &self.failure_reason())
2397 .finish()
2398 }
2399}
2400
2401#[derive(Copy, Clone)]
2402#[repr(transparent)]
2403pub struct WpsCredentialsRef(wifi_event_sta_wps_er_success_t__bindgen_ty_1);
2404
2405impl WpsCredentialsRef {
2406 pub fn ssid(&self) -> &CStr {
2407 unsafe { CStr::from_ptr(self.0.ssid.as_ptr() as *const _) }
2408 }
2409
2410 pub fn passphrase(&self) -> &CStr {
2411 unsafe { CStr::from_ptr(self.0.passphrase.as_ptr() as *const _) }
2412 }
2413}
2414
2415impl fmt::Debug for WpsCredentialsRef {
2416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2417 f.debug_struct("WpsCredentialsRef")
2418 .field("ssid", &self.ssid())
2419 .finish()
2420 }
2421}
2422
2423#[derive(Clone, Debug, Eq, PartialEq)]
2424pub struct WpsCredentials {
2425 pub ssid: heapless::String<32>,
2426 pub passphrase: heapless::String<64>,
2427}
2428
2429impl TryFrom<&WpsCredentialsRef> for WpsCredentials {
2430 type Error = EspError;
2431
2432 fn try_from(credentials: &WpsCredentialsRef) -> Result<Self, Self::Error> {
2433 let err = EspError::from_infallible::<ESP_ERR_INVALID_ARG>();
2434
2435 Ok(Self {
2436 ssid: credentials
2437 .ssid()
2438 .to_str()
2439 .map_err(|_| err)
2440 .and_then(|credentials| credentials.try_into().map_err(|_| err))?,
2441 passphrase: credentials
2442 .passphrase()
2443 .to_str()
2444 .map_err(|_| err)
2445 .and_then(|credentials| credentials.try_into().map_err(|_| err))?,
2446 })
2447 }
2448}
2449
2450impl fmt::Debug for ApStaConnectedRef {
2451 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2452 f.debug_struct("ApStaConnectedRef")
2453 .field("mac", &self.mac())
2454 .field("aid", &self.aid())
2455 .field("is_mesh_child", &self.is_mesh_child())
2456 .finish()
2457 }
2458}
2459
2460#[derive(Copy, Clone, Debug)]
2461pub enum WifiEvent<'a> {
2462 Ready,
2463
2464 ScanDone(&'a StaScanDoneRef),
2465
2466 StaStarted,
2467 StaStopped,
2468 StaConnected(&'a StaConnectedRef),
2469 StaDisconnected(&'a StaDisconnectedRef),
2470 StaAuthmodeChanged,
2471 StaBssRssiLow,
2472 StaBeaconTimeout,
2473 StaWpsSuccess(&'a [WpsCredentialsRef]),
2474 StaWpsFailed,
2475 StaWpsTimeout,
2476 StaWpsPin(Option<u32>),
2477 StaWpsPbcOverlap,
2478
2479 ApStarted,
2480 ApStopped,
2481 ApStaConnected(&'a ApStaConnectedRef),
2482 ApStaDisconnected(&'a ApStaDisconnectedRef),
2483 ApProbeRequestReceived,
2484
2485 FtmReport,
2486 ActionTxStatus,
2487 RocDone,
2488
2489 #[cfg(not(any(
2490 esp_idf_version_major = "4",
2491 all(
2492 esp_idf_version_major = "5",
2493 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
2494 ),
2495 )))]
2496 HomeChannelChange(HomeChannelChange),
2497
2498 #[cfg(esp_idf_version_at_least_5_3_0)]
2501 StaNeighborRep(&'a StaNeighborRepRef),
2502
2503 #[cfg(any(
2505 esp_idf_version_patch_at_least_5_3_3,
2506 esp_idf_version_patch_at_least_5_4_1,
2507 esp_idf_version_at_least_5_5_0,
2508 ))]
2509 ApWrongPassword(&'a ApWrongPasswordRef),
2510
2511 #[cfg(esp_idf_version_at_least_5_5_0)]
2513 StaBeaconOffsetUnstable(&'a StaBeaconOffsetUnstableRef),
2514
2515 #[cfg(esp_idf_version_at_least_5_5_0)]
2517 DppUriReady(&'a DppUriReadyRef),
2518
2519 #[cfg(esp_idf_version_at_least_5_5_0)]
2521 DppCfgRecvd(&'a DppCfgRecvdRef),
2522
2523 #[cfg(esp_idf_version_at_least_5_5_0)]
2525 DppFailed(&'a DppFailedRef),
2526
2527 Other(i32),
2533}
2534
2535unsafe impl EspEventSource for WifiEvent<'_> {
2536 fn source() -> Option<&'static ffi::CStr> {
2537 Some(unsafe { ffi::CStr::from_ptr(WIFI_EVENT) })
2538 }
2539}
2540
2541impl EspEventDeserializer for WifiEvent<'_> {
2542 type Data<'d> = WifiEvent<'d>;
2543
2544 #[allow(non_upper_case_globals, non_snake_case)]
2545 fn deserialize<'d>(data: &crate::eventloop::EspEvent<'d>) -> WifiEvent<'d> {
2546 let event_id = data.event_id as u32;
2547
2548 match event_id {
2549 wifi_event_t_WIFI_EVENT_WIFI_READY => WifiEvent::Ready,
2550 wifi_event_t_WIFI_EVENT_SCAN_DONE => WifiEvent::ScanDone(unsafe { data.as_payload() }),
2551 wifi_event_t_WIFI_EVENT_STA_START => WifiEvent::StaStarted,
2552 wifi_event_t_WIFI_EVENT_STA_STOP => WifiEvent::StaStopped,
2553 wifi_event_t_WIFI_EVENT_STA_CONNECTED => {
2554 WifiEvent::StaConnected(unsafe { data.as_payload() })
2555 }
2556 wifi_event_t_WIFI_EVENT_STA_DISCONNECTED => {
2557 WifiEvent::StaDisconnected(unsafe { data.as_payload() })
2558 }
2559 wifi_event_t_WIFI_EVENT_STA_AUTHMODE_CHANGE => WifiEvent::StaAuthmodeChanged,
2560 wifi_event_t_WIFI_EVENT_STA_WPS_ER_SUCCESS => {
2561 let credentials: &[wifi_event_sta_wps_er_success_t__bindgen_ty_1] = data
2562 .payload
2563 .map(|x| x as *const _ as *const wifi_event_sta_wps_er_success_t)
2564 .and_then(|x| unsafe { x.as_ref() })
2565 .map(|x| &x.ap_cred[0..x.ap_cred_cnt as usize])
2566 .unwrap_or(&[]);
2567 let credentials: &[WpsCredentialsRef] =
2569 unsafe { core::mem::transmute(credentials) };
2570
2571 WifiEvent::StaWpsSuccess(credentials)
2572 }
2573 wifi_event_t_WIFI_EVENT_STA_WPS_ER_FAILED => WifiEvent::StaWpsFailed,
2574 wifi_event_t_WIFI_EVENT_STA_WPS_ER_TIMEOUT => WifiEvent::StaWpsTimeout,
2575 wifi_event_t_WIFI_EVENT_STA_WPS_ER_PIN => {
2576 let pin = data
2577 .payload
2578 .map(|x| x as *const _ as *const wifi_event_sta_wps_er_pin_t)
2579 .and_then(|x| unsafe { x.as_ref() })
2580 .and_then(|x| core::str::from_utf8(&x.pin_code).ok())
2581 .and_then(|x| x.parse().ok());
2582 WifiEvent::StaWpsPin(pin)
2583 }
2584 wifi_event_t_WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP => WifiEvent::StaWpsPbcOverlap,
2585 wifi_event_t_WIFI_EVENT_AP_START => WifiEvent::ApStarted,
2586 wifi_event_t_WIFI_EVENT_AP_STOP => WifiEvent::ApStopped,
2587 wifi_event_t_WIFI_EVENT_AP_STACONNECTED => {
2588 WifiEvent::ApStaConnected(unsafe { data.as_payload() })
2589 }
2590 wifi_event_t_WIFI_EVENT_AP_STADISCONNECTED => {
2591 WifiEvent::ApStaDisconnected(unsafe { data.as_payload() })
2592 }
2593 wifi_event_t_WIFI_EVENT_AP_PROBEREQRECVED => WifiEvent::ApProbeRequestReceived,
2594 wifi_event_t_WIFI_EVENT_FTM_REPORT => WifiEvent::FtmReport,
2595 wifi_event_t_WIFI_EVENT_STA_BSS_RSSI_LOW => WifiEvent::StaBssRssiLow,
2596 wifi_event_t_WIFI_EVENT_ACTION_TX_STATUS => WifiEvent::ActionTxStatus,
2597 wifi_event_t_WIFI_EVENT_STA_BEACON_TIMEOUT => WifiEvent::StaBeaconTimeout,
2598 wifi_event_t_WIFI_EVENT_ROC_DONE => WifiEvent::RocDone,
2599 #[cfg(not(any(
2600 esp_idf_version_major = "4",
2601 all(
2602 esp_idf_version_major = "5",
2603 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
2604 ),
2605 )))]
2606 wifi_event_t_WIFI_EVENT_HOME_CHANNEL_CHANGE => {
2607 #[cfg(esp_idf_soc_wifi_supported)]
2608 {
2609 let payload = unsafe {
2610 (data.payload.unwrap() as *const _
2611 as *const wifi_event_home_channel_change_t)
2612 .as_ref()
2613 }
2614 .unwrap();
2615
2616 WifiEvent::HomeChannelChange(HomeChannelChange {
2617 old_chan: payload.old_chan,
2618 old_snd: payload.old_snd.try_into().ok(),
2619 new_chan: payload.new_chan,
2620 new_snd: payload.new_snd.try_into().ok(),
2621 })
2622 }
2623
2624 #[cfg(not(esp_idf_soc_wifi_supported))]
2627 {
2628 let payload = data
2629 .payload
2630 .map(|p| p as *const _ as *const wifi_event_home_channel_change_t)
2631 .and_then(|p| unsafe { p.as_ref() });
2632
2633 WifiEvent::HomeChannelChange(HomeChannelChange {
2634 old_chan: payload.map_or(0, |p| p.old_chan),
2635 old_snd: payload.and_then(|p| p.old_snd.try_into().ok()),
2636 new_chan: payload.map_or(0, |p| p.new_chan),
2637 new_snd: payload.and_then(|p| p.new_snd.try_into().ok()),
2638 })
2639 }
2640 }
2641 #[cfg(esp_idf_version_at_least_5_3_0)]
2642 wifi_event_t_WIFI_EVENT_STA_NEIGHBOR_REP => {
2643 WifiEvent::StaNeighborRep(unsafe {
2648 (data.payload.unwrap() as *const _ as *const StaNeighborRepRef)
2649 .as_ref()
2650 .unwrap()
2651 })
2652 }
2653 #[cfg(any(
2654 esp_idf_version_patch_at_least_5_3_3,
2655 esp_idf_version_patch_at_least_5_4_1,
2656 esp_idf_version_at_least_5_5_0,
2657 ))]
2658 wifi_event_t_WIFI_EVENT_AP_WRONG_PASSWORD => {
2659 WifiEvent::ApWrongPassword(unsafe { data.as_payload() })
2660 }
2661 #[cfg(esp_idf_version_at_least_5_5_0)]
2662 wifi_event_t_WIFI_EVENT_STA_BEACON_OFFSET_UNSTABLE => {
2663 WifiEvent::StaBeaconOffsetUnstable(unsafe { data.as_payload() })
2664 }
2665 #[cfg(esp_idf_version_at_least_5_5_0)]
2666 wifi_event_t_WIFI_EVENT_DPP_URI_READY => {
2667 WifiEvent::DppUriReady(unsafe {
2670 (data.payload.unwrap() as *const _ as *const DppUriReadyRef)
2671 .as_ref()
2672 .unwrap()
2673 })
2674 }
2675 #[cfg(esp_idf_version_at_least_5_5_0)]
2676 wifi_event_t_WIFI_EVENT_DPP_CFG_RECVD => {
2677 WifiEvent::DppCfgRecvd(unsafe {
2681 (data.payload.unwrap() as *const _ as *const DppCfgRecvdRef)
2682 .as_ref()
2683 .unwrap()
2684 })
2685 }
2686 #[cfg(esp_idf_version_at_least_5_5_0)]
2687 wifi_event_t_WIFI_EVENT_DPP_FAILED => {
2688 WifiEvent::DppFailed(unsafe { data.as_payload() })
2689 }
2690 _ => {
2691 ::log::warn!("WifiEvent: unknown event ID {event_id}, ignoring");
2692 WifiEvent::Other(event_id as i32)
2693 }
2694 }
2695 }
2696}
2697
2698const CONNECT_TIMEOUT: Duration = Duration::from_secs(15);
2699const WPS_TIMEOUT: Duration = Duration::from_secs(120);
2700
2701pub struct BlockingWifi<T> {
2706 wifi: T,
2707 event_loop: crate::eventloop::EspSystemEventLoop,
2708}
2709
2710impl<T> BlockingWifi<T>
2711where
2712 T: Wifi<Error = EspError> + NonBlocking,
2713{
2714 pub fn wrap(wifi: T, event_loop: EspSystemEventLoop) -> Result<Self, EspError> {
2715 Ok(Self { wifi, event_loop })
2716 }
2717
2718 pub fn wifi(&self) -> &T {
2720 &self.wifi
2721 }
2722
2723 pub fn wifi_mut(&mut self) -> &mut T {
2725 &mut self.wifi
2726 }
2727
2728 pub fn get_capabilities(&self) -> Result<EnumSet<Capability>, EspError> {
2730 self.wifi.get_capabilities()
2731 }
2732
2733 pub fn get_configuration(&self) -> Result<Configuration, EspError> {
2735 self.wifi.get_configuration()
2736 }
2737
2738 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), EspError> {
2740 self.wifi.set_configuration(conf)
2741 }
2742
2743 pub fn is_started(&self) -> Result<bool, EspError> {
2745 self.wifi.is_started()
2746 }
2747
2748 pub fn is_connected(&self) -> Result<bool, EspError> {
2750 self.wifi.is_connected()
2751 }
2752
2753 pub fn start(&mut self) -> Result<(), EspError> {
2756 self.wifi.start()?;
2757 self.wifi_wait_while(|| self.wifi.is_started().map(|s| !s), None)
2758 }
2759
2760 pub fn stop(&mut self) -> Result<(), EspError> {
2763 self.wifi.stop()?;
2764 self.wifi_wait_while(|| self.wifi.is_started(), None)
2765 }
2766
2767 pub fn connect(&mut self) -> Result<(), EspError> {
2770 self.wifi.connect()?;
2771 self.wifi_wait_while(
2772 || self.wifi.is_connected().map(|s| !s),
2773 Some(CONNECT_TIMEOUT),
2774 )
2775 }
2776
2777 pub fn disconnect(&mut self) -> Result<(), EspError> {
2780 self.wifi.disconnect()?;
2781 self.wifi_wait_while(|| self.wifi.is_connected(), None)
2782 }
2783
2784 pub fn scan_n<const N: usize>(
2786 &mut self,
2787 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
2788 self.wifi.scan_n()
2789 }
2790
2791 #[cfg(feature = "alloc")]
2793 pub fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
2794 self.wifi.scan()
2795 }
2796
2797 pub fn wifi_wait_while<F: Fn() -> Result<bool, EspError>>(
2810 &self,
2811 matcher: F,
2812 timeout: Option<Duration>,
2813 ) -> Result<(), EspError> {
2814 let wait = Wait::new::<WifiEvent>(&self.event_loop)?;
2815
2816 wait.wait_while(matcher, timeout)
2817 }
2818
2819 pub fn start_wps(&mut self, config: &WpsConfig) -> Result<WpsStatus, EspError> {
2828 self.wifi.start_wps(config)?;
2829 self.wifi_wait_while(
2830 || self.wifi.is_wps_finished().map(|x| !x),
2831 Some(WPS_TIMEOUT),
2832 )?;
2833 Ok(self.wifi.stop_wps().unwrap_or(WpsStatus::Timeout))
2834 }
2835}
2836
2837#[cfg(esp_idf_comp_esp_netif_enabled)]
2838impl<T> BlockingWifi<T>
2839where
2840 T: NetifStatus,
2841{
2842 pub fn is_up(&self) -> Result<bool, EspError> {
2844 self.wifi.is_up()
2845 }
2846
2847 pub fn wait_netif_up(&self) -> Result<(), EspError> {
2849 self.ip_wait_while(|| self.wifi.is_up().map(|s| !s), Some(CONNECT_TIMEOUT))
2850 }
2851
2852 pub fn ip_wait_while<F: Fn() -> Result<bool, EspError>>(
2855 &self,
2856 matcher: F,
2857 timeout: Option<core::time::Duration>,
2858 ) -> Result<(), EspError> {
2859 let wait = crate::eventloop::Wait::new::<IpEvent>(&self.event_loop)?;
2860
2861 wait.wait_while(matcher, timeout)
2862 }
2863}
2864
2865impl<T> Wifi for BlockingWifi<T>
2866where
2867 T: Wifi<Error = EspError> + NonBlocking,
2868{
2869 type Error = EspError;
2870
2871 fn get_capabilities(&self) -> Result<EnumSet<Capability>, Self::Error> {
2872 BlockingWifi::get_capabilities(self)
2873 }
2874
2875 fn get_configuration(&self) -> Result<Configuration, Self::Error> {
2876 BlockingWifi::get_configuration(self)
2877 }
2878
2879 fn set_configuration(&mut self, conf: &Configuration) -> Result<(), Self::Error> {
2880 BlockingWifi::set_configuration(self, conf)
2881 }
2882
2883 fn is_started(&self) -> Result<bool, Self::Error> {
2884 BlockingWifi::is_started(self)
2885 }
2886
2887 fn is_connected(&self) -> Result<bool, Self::Error> {
2888 BlockingWifi::is_connected(self)
2889 }
2890
2891 fn start(&mut self) -> Result<(), Self::Error> {
2892 BlockingWifi::start(self)
2893 }
2894
2895 fn stop(&mut self) -> Result<(), Self::Error> {
2896 BlockingWifi::stop(self)
2897 }
2898
2899 fn connect(&mut self) -> Result<(), Self::Error> {
2900 BlockingWifi::connect(self)
2901 }
2902
2903 fn disconnect(&mut self) -> Result<(), Self::Error> {
2904 BlockingWifi::disconnect(self)
2905 }
2906
2907 fn scan_n<const N: usize>(
2908 &mut self,
2909 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), Self::Error> {
2910 BlockingWifi::scan_n(self)
2911 }
2912
2913 #[cfg(feature = "alloc")]
2914 fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, Self::Error> {
2915 BlockingWifi::scan(self)
2916 }
2917}
2918
2919#[cfg(esp_idf_comp_esp_netif_enabled)]
2920impl<T> NetifStatus for BlockingWifi<T>
2921where
2922 T: NetifStatus,
2923{
2924 fn is_up(&self) -> Result<bool, EspError> {
2925 BlockingWifi::is_up(self)
2926 }
2927}
2928
2929#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
2932pub struct AsyncWifi<T> {
2933 wifi: T,
2934 event_loop: crate::eventloop::EspSystemEventLoop,
2935 timer_service: crate::timer::EspTaskTimerService,
2936}
2937
2938#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
2939impl<T> AsyncWifi<T>
2940where
2941 T: Wifi<Error = EspError> + NonBlocking,
2942{
2943 pub fn wrap(
2944 wifi: T,
2945 event_loop: EspSystemEventLoop,
2946 timer_service: EspTaskTimerService,
2947 ) -> Result<Self, EspError> {
2948 Ok(Self {
2949 wifi,
2950 event_loop,
2951 timer_service,
2952 })
2953 }
2954
2955 pub fn wifi(&self) -> &T {
2957 &self.wifi
2958 }
2959
2960 pub fn wifi_mut(&mut self) -> &mut T {
2962 &mut self.wifi
2963 }
2964
2965 pub fn get_capabilities(&self) -> Result<EnumSet<Capability>, EspError> {
2967 self.wifi.get_capabilities()
2968 }
2969
2970 pub fn get_configuration(&self) -> Result<Configuration, EspError> {
2972 self.wifi.get_configuration()
2973 }
2974
2975 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), EspError> {
2977 self.wifi.set_configuration(conf)
2978 }
2979
2980 pub fn is_started(&self) -> Result<bool, EspError> {
2982 self.wifi.is_started()
2983 }
2984
2985 pub fn is_connected(&self) -> Result<bool, EspError> {
2987 self.wifi.is_connected()
2988 }
2989
2990 pub async fn start(&mut self) -> Result<(), EspError> {
2993 self.wifi.start()?;
2994 self.wifi_wait(|this| this.wifi.is_started().map(|s| !s), None)
2995 .await
2996 }
2997
2998 pub async fn stop(&mut self) -> Result<(), EspError> {
3001 self.wifi.stop()?;
3002 self.wifi_wait(|this| this.wifi.is_started(), None).await
3003 }
3004
3005 pub async fn connect(&mut self) -> Result<(), EspError> {
3008 self.wifi.connect()?;
3009 self.wifi_wait(
3010 |this| this.wifi.is_connected().map(|s| !s),
3011 Some(CONNECT_TIMEOUT),
3012 )
3013 .await
3014 }
3015
3016 pub async fn disconnect(&mut self) -> Result<(), EspError> {
3019 self.wifi.disconnect()?;
3020 self.wifi_wait(|this| this.wifi.is_connected(), None).await
3021 }
3022
3023 pub async fn scan_n<const N: usize>(
3026 &mut self,
3027 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), EspError> {
3028 self.wifi.start_scan(&Default::default(), false)?;
3029
3030 self.wifi_wait(|this| this.wifi.is_scan_done().map(|s| !s), None)
3031 .await?;
3032
3033 self.wifi.get_scan_result_n()
3034 }
3035
3036 #[cfg(feature = "alloc")]
3039 pub async fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, EspError> {
3040 self.wifi.start_scan(&Default::default(), false)?;
3041
3042 self.wifi_wait(|this| this.wifi.is_scan_done().map(|s| !s), None)
3043 .await?;
3044
3045 self.wifi.get_scan_result()
3046 }
3047
3048 pub async fn wifi_wait<F: FnMut(&mut Self) -> Result<bool, EspError>>(
3060 &mut self,
3061 mut matcher: F,
3062 timeout: Option<Duration>,
3063 ) -> Result<(), EspError> {
3064 let mut wait = crate::eventloop::AsyncWait::<WifiEvent, _>::new(
3065 &self.event_loop,
3066 &self.timer_service,
3067 )?;
3068
3069 wait.wait_while(|| matcher(self), timeout).await
3070 }
3071
3072 pub async fn start_wps(&mut self, config: &WpsConfig<'_>) -> Result<WpsStatus, EspError> {
3081 self.wifi.start_wps(config)?;
3082 self.wifi_wait(
3083 |this| this.wifi.is_wps_finished().map(|x| !x),
3084 Some(WPS_TIMEOUT),
3085 )
3086 .await?;
3087 Ok(self.wifi.stop_wps().unwrap_or(WpsStatus::Timeout))
3088 }
3089}
3090
3091#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
3092#[cfg(esp_idf_comp_esp_netif_enabled)]
3093impl<T> AsyncWifi<T>
3094where
3095 T: NetifStatus,
3096{
3097 pub fn is_up(&self) -> Result<bool, EspError> {
3099 self.wifi.is_up()
3100 }
3101
3102 pub async fn wait_netif_up(&mut self) -> Result<(), EspError> {
3104 self.ip_wait_while(|this| this.wifi.is_up().map(|s| !s), Some(CONNECT_TIMEOUT))
3105 .await
3106 }
3107
3108 pub async fn ip_wait_while<F: FnMut(&mut Self) -> Result<bool, EspError>>(
3111 &mut self,
3112 mut matcher: F,
3113 timeout: Option<core::time::Duration>,
3114 ) -> Result<(), EspError> {
3115 let mut wait =
3116 crate::eventloop::AsyncWait::<IpEvent, _>::new(&self.event_loop, &self.timer_service)?;
3117
3118 wait.wait_while(|| matcher(self), timeout).await
3119 }
3120}
3121
3122#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
3123impl<T> embedded_svc::wifi::asynch::Wifi for AsyncWifi<T>
3124where
3125 T: Wifi<Error = EspError> + NonBlocking,
3126{
3127 type Error = T::Error;
3128
3129 async fn get_capabilities(&self) -> Result<EnumSet<Capability>, Self::Error> {
3130 AsyncWifi::get_capabilities(self)
3131 }
3132
3133 async fn get_configuration(&self) -> Result<Configuration, Self::Error> {
3134 AsyncWifi::get_configuration(self)
3135 }
3136
3137 async fn set_configuration(&mut self, conf: &Configuration) -> Result<(), Self::Error> {
3138 AsyncWifi::set_configuration(self, conf)
3139 }
3140
3141 async fn start(&mut self) -> Result<(), Self::Error> {
3142 AsyncWifi::start(self).await
3143 }
3144
3145 async fn stop(&mut self) -> Result<(), Self::Error> {
3146 AsyncWifi::stop(self).await
3147 }
3148
3149 async fn connect(&mut self) -> Result<(), Self::Error> {
3150 AsyncWifi::connect(self).await
3151 }
3152
3153 async fn disconnect(&mut self) -> Result<(), Self::Error> {
3154 AsyncWifi::disconnect(self).await
3155 }
3156
3157 async fn is_started(&self) -> Result<bool, Self::Error> {
3158 AsyncWifi::is_started(self)
3159 }
3160
3161 async fn is_connected(&self) -> Result<bool, Self::Error> {
3162 AsyncWifi::is_connected(self)
3163 }
3164
3165 async fn scan_n<const N: usize>(
3166 &mut self,
3167 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), Self::Error> {
3168 AsyncWifi::scan_n(self).await
3169 }
3170
3171 #[cfg(feature = "alloc")]
3172 async fn scan(&mut self) -> Result<alloc::vec::Vec<AccessPointInfo>, Self::Error> {
3173 AsyncWifi::scan(self).await
3174 }
3175}
3176
3177#[cfg(esp_idf_comp_esp_netif_enabled)]
3178impl crate::netif::asynch::NetifStatus for EspWifi<'_> {
3179 async fn is_up(&self) -> Result<bool, EspError> {
3180 EspWifi::is_up(self)
3181 }
3182}
3183
3184#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
3185#[cfg(esp_idf_comp_esp_netif_enabled)]
3186impl<T> crate::netif::asynch::NetifStatus for AsyncWifi<T>
3187where
3188 T: NetifStatus,
3189{
3190 async fn is_up(&self) -> Result<bool, EspError> {
3191 AsyncWifi::is_up(self)
3192 }
3193}
3194
3195#[derive(Copy, Clone, Debug, Eq, PartialEq)]
3196enum WifiStaStatus {
3197 Stopped,
3198 Started,
3199 Connected,
3200}
3201
3202#[derive(Copy, Clone, Debug, Eq, PartialEq)]
3203enum WifiApStatus {
3204 Stopped,
3205 Started,
3206}
3207
3208#[derive(Copy, Clone, Debug, Eq, PartialEq)]
3209enum WifiScanStatus {
3210 Idle,
3211 Started,
3212 Done,
3213}
3214
3215#[derive(Debug)]
3216pub struct WpsConfig<'a> {
3217 pub wps_type: WpsType,
3218 pub factory_info: WpsFactoryInfo<'a>,
3219}
3220
3221impl TryFrom<&WpsConfig<'_>> for Newtype<esp_wps_config_t> {
3222 type Error = EspError;
3223
3224 fn try_from(config: &WpsConfig<'_>) -> Result<Self, Self::Error> {
3225 let factory_info = Newtype::<wps_factory_information_t>::try_from(&config.factory_info)?.0;
3226 Ok(Newtype(esp_wps_config_t {
3227 wps_type: config.wps_type.as_raw_type(),
3228 factory_info,
3229 #[cfg(not(esp_idf_version_major = "4"))]
3230 pin: config.wps_type.as_pin(),
3231 }))
3232 }
3233}
3234
3235#[derive(Clone, Debug)]
3236pub struct WpsFactoryInfo<'a> {
3237 pub manufacturer: &'a str,
3238 pub model_number: &'a str,
3239 pub model_name: &'a str,
3240 pub device_name: &'a str,
3241}
3242
3243impl TryFrom<&WpsFactoryInfo<'_>> for Newtype<wps_factory_information_t> {
3244 type Error = EspError;
3245
3246 fn try_from(info: &WpsFactoryInfo<'_>) -> Result<Self, Self::Error> {
3247 let mut result = Newtype(wps_factory_information_t {
3248 manufacturer: [0; 65],
3249 model_number: [0; 33],
3250 model_name: [0; 33],
3251 device_name: [0; 33],
3252 });
3253
3254 set_str(
3255 c_char_to_u8_slice_mut(&mut result.0.manufacturer),
3256 info.manufacturer,
3257 )?;
3258 set_str(
3259 c_char_to_u8_slice_mut(&mut result.0.model_number),
3260 info.model_number,
3261 )?;
3262 set_str(
3263 c_char_to_u8_slice_mut(&mut result.0.model_name),
3264 info.model_name,
3265 )?;
3266 set_str(
3267 c_char_to_u8_slice_mut(&mut result.0.device_name),
3268 info.device_name,
3269 )?;
3270
3271 Ok(result)
3272 }
3273}
3274
3275#[derive(Copy, Clone, Debug, Eq, PartialEq)]
3276pub enum WpsType {
3277 Pbc,
3278 Pin(u32),
3279}
3280
3281impl WpsType {
3282 fn as_raw_type(&self) -> wps_type_t {
3283 match self {
3284 WpsType::Pbc => wps_type_WPS_TYPE_PBC,
3285 WpsType::Pin(_) => wps_type_WPS_TYPE_PIN,
3286 }
3287 }
3288
3289 #[cfg(not(esp_idf_version_major = "4"))]
3290 fn as_pin(&self) -> [ffi::c_char; 9] {
3291 match self {
3292 WpsType::Pbc => [0; 9],
3293 WpsType::Pin(pin) => {
3294 let mut result = [0; 9];
3295 let mut pin = *pin;
3296 let mut rem: u32;
3297 for i in 0..8 {
3298 rem = pin % 10;
3299 pin /= 10;
3300 result[7 - i] = (rem as ffi::c_char) + 48;
3301 }
3302 result
3303 }
3304 }
3305 }
3306}
3307
3308#[derive(Clone, Debug)]
3309pub enum WpsStatus {
3310 SuccessConnected,
3311 SuccessMultipleAccessPoints(alloc::vec::Vec<WpsCredentials>),
3312 Failure,
3313 Timeout,
3314 Pin(Option<u32>),
3315 PbcOverlap,
3316}
3317
3318impl TryFrom<&WifiEvent<'_>> for WpsStatus {
3319 type Error = EspError;
3320
3321 fn try_from(event: &WifiEvent) -> Result<Self, Self::Error> {
3322 match event {
3323 WifiEvent::StaWpsSuccess(credentials) => {
3324 if credentials.is_empty() {
3325 Ok(WpsStatus::SuccessConnected)
3326 } else {
3327 Ok(WpsStatus::SuccessMultipleAccessPoints(
3328 credentials
3329 .iter()
3330 .filter_map(|c| c.try_into().ok())
3331 .collect::<alloc::vec::Vec<WpsCredentials>>(),
3332 ))
3333 }
3334 }
3335 WifiEvent::StaWpsFailed => Ok(WpsStatus::Failure),
3336 WifiEvent::StaWpsTimeout => Ok(WpsStatus::Timeout),
3337 WifiEvent::StaWpsPin(pin) => Ok(WpsStatus::Pin(*pin)),
3338 WifiEvent::StaWpsPbcOverlap => Ok(WpsStatus::PbcOverlap),
3339 _ => Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>()),
3340 }
3341 }
3342}