esp32_nimble/client/
ble_scan.rs1use crate::{BLEAdvertisedData, BLEDevice};
2use crate::{BLEAdvertisedDevice, BLEError, Signal, ble, enums::*, utilities::voidp_to_ref};
3use core::ffi::c_void;
4use esp_idf_svc::sys;
5
6pub struct BLEScan {
27 scan_params: sys::ble_gap_disc_params,
28 signal: Signal<()>,
29}
30
31type CbArgType<'a> = (
32 &'a mut BLEScan,
33 &'a mut dyn FnMut(&mut BLEScan, &BLEAdvertisedDevice, BLEAdvertisedData<&[u8]>),
34);
35
36impl BLEScan {
37 pub fn new() -> Self {
38 let mut ret = Self {
39 scan_params: sys::ble_gap_disc_params {
40 itvl: 0,
41 window: 0,
42 filter_policy: sys::BLE_HCI_SCAN_FILT_NO_WL as _,
43 ..Default::default()
44 },
45 signal: Signal::new(),
46 };
47 ret.limited(false);
48 ret.filter_duplicates(true);
49 ret.active_scan(false).interval(100).window(100);
50 ret
51 }
52
53 pub fn active_scan(&mut self, active: bool) -> &mut Self {
54 self.scan_params.set_passive((!active) as _);
55 self
56 }
57
58 pub fn filter_duplicates(&mut self, val: bool) -> &mut Self {
59 self.scan_params.set_filter_duplicates(val as _);
60 self
61 }
62
63 pub fn limited(&mut self, val: bool) -> &mut Self {
66 self.scan_params.set_limited(val as _);
67 self
68 }
69
70 pub fn filter_policy(&mut self, val: ScanFilterPolicy) -> &mut Self {
72 self.scan_params.filter_policy = val.into();
73 self
74 }
75
76 pub fn interval(&mut self, interval_msecs: u16) -> &mut Self {
78 self.scan_params.itvl = ((interval_msecs as f32) / 0.625) as u16;
79 self
80 }
81
82 pub fn window(&mut self, window_msecs: u16) -> &mut Self {
84 self.scan_params.window = ((window_msecs as f32) / 0.625) as u16;
85 self
86 }
87
88 pub async fn start<F, R>(
92 &mut self,
93 _ble_device: &BLEDevice,
94 duration_ms: i32,
95 mut callback: F,
96 ) -> Result<Option<R>, BLEError>
97 where
98 F: FnMut(&BLEAdvertisedDevice, BLEAdvertisedData<&[u8]>) -> Option<R>,
99 {
100 let mut result: Option<R> = None;
101
102 let mut on_result =
103 |scan: &mut Self, device: &BLEAdvertisedDevice, data: BLEAdvertisedData<&[u8]>| {
104 if let Some(res) = callback(device, data) {
105 result = Some(res);
106
107 if let Err(err) = Self::stop() {
108 ::log::warn!("scan stop err: {err:?}");
109 }
110 scan.signal.signal(());
111 }
112 };
113
114 let cb_arg: CbArgType = (self, &mut on_result);
115
116 #[cfg(esp_idf_bt_nimble_ext_adv)]
117 {
118 let mut scan_params = sys::ble_gap_ext_disc_params {
119 itvl: cb_arg.0.scan_params.itvl,
120 window: cb_arg.0.scan_params.window,
121 ..Default::default()
122 };
123 scan_params.set_passive(cb_arg.0.scan_params.passive());
124 unsafe {
125 ble!(sys::ble_gap_ext_disc(
126 crate::ble_device::OWN_ADDR_TYPE as _,
127 (duration_ms / 10) as _,
128 0,
129 cb_arg.0.scan_params.filter_duplicates(),
130 cb_arg.0.scan_params.filter_policy,
131 cb_arg.0.scan_params.limited(),
132 &scan_params,
133 &scan_params,
134 Some(Self::handle_gap_event),
135 core::ptr::addr_of!(cb_arg) as _,
136 ))?;
137 }
138 }
139
140 #[cfg(not(esp_idf_bt_nimble_ext_adv))]
141 unsafe {
142 ble!(sys::ble_gap_disc(
143 crate::ble_device::OWN_ADDR_TYPE as _,
144 duration_ms,
145 &cb_arg.0.scan_params,
146 Some(Self::handle_gap_event),
147 core::ptr::addr_of!(cb_arg) as _,
148 ))?;
149 }
150
151 cb_arg.0.signal.wait().await;
152
153 Ok(result)
154 }
155
156 fn stop() -> Result<(), BLEError> {
157 let rc = unsafe { sys::ble_gap_disc_cancel() };
158 if rc != 0 && rc != (sys::BLE_HS_EALREADY as _) {
159 return BLEError::convert(rc as _);
160 }
161
162 Ok(())
163 }
164
165 extern "C" fn handle_gap_event(event: *mut sys::ble_gap_event, arg: *mut c_void) -> i32 {
166 let event = unsafe { &*event };
167 let (scan, on_result) = unsafe { voidp_to_ref::<CbArgType>(arg) };
168
169 match event.type_ as u32 {
170 sys::BLE_GAP_EVENT_EXT_DISC | sys::BLE_GAP_EVENT_DISC => {
171 #[cfg(esp_idf_bt_nimble_ext_adv)]
172 let disc = unsafe { &event.__bindgen_anon_1.ext_disc };
173
174 #[cfg(not(esp_idf_bt_nimble_ext_adv))]
175 let disc = unsafe { &event.__bindgen_anon_1.disc };
176
177 let data = BLEAdvertisedData::new(unsafe {
178 core::slice::from_raw_parts(disc.data, disc.length_data as _)
179 });
180
181 let advertised_device: &BLEAdvertisedDevice = unsafe { core::mem::transmute(disc) };
182
183 on_result(scan, advertised_device, data);
184 }
185 sys::BLE_GAP_EVENT_DISC_COMPLETE => {
186 scan.signal.signal(());
187 }
188 _ => {}
189 }
190 0
191 }
192}