esp32_nimble/server/
ble_advertising.rs1use core::ffi::{c_int, c_void};
2use esp_idf_svc::sys as esp_idf_sys;
3
4use crate::{
5 BLEAdvertisementData, BLEError, BLEServer, ble,
6 enums::*,
7 utilities::{as_void_ptr, voidp_to_ref},
8};
9use alloc::boxed::Box;
10use once_cell::sync::Lazy;
11
12const BLE_HS_ADV_MAX_SZ: usize = esp_idf_sys::BLE_HS_ADV_MAX_SZ as usize;
13
14const BLE_HS_FOREVER: i32 = i32::MAX;
16
17pub struct BLEAdvertising {
18 adv_params: esp_idf_sys::ble_gap_adv_params,
19 scan_response: bool,
20 on_complete: Option<Box<dyn FnMut(c_int) + Send + Sync>>,
21}
22
23impl BLEAdvertising {
24 #[allow(dead_code)]
25 pub(crate) fn new() -> Self {
26 let mut ret = Self {
27 adv_params: esp_idf_sys::ble_gap_adv_params::default(),
28 scan_response: true,
29 on_complete: None,
30 };
31
32 ret.reset().unwrap();
33 ret
34 }
35
36 pub fn reset(&mut self) -> Result<(), BLEError> {
37 if self.is_advertising() {
38 self.stop()?;
39 }
40
41 self.adv_params.conn_mode = esp_idf_sys::BLE_GAP_CONN_MODE_UND as _;
42 self.adv_params.disc_mode = esp_idf_sys::BLE_GAP_DISC_MODE_GEN as _;
43 self.scan_response = true;
44
45 Ok(())
46 }
47
48 pub fn set_data(&mut self, data: &mut BLEAdvertisementData) -> Result<(), BLEError> {
49 if self.adv_params.conn_mode == (ConnMode::Non as _) && !self.scan_response {
50 data.flags = 0;
51 } else {
52 data.flags =
53 (esp_idf_sys::BLE_HS_ADV_F_DISC_GEN | esp_idf_sys::BLE_HS_ADV_F_BREDR_UNSUP) as _;
54 }
55
56 let mut adv_data = data.as_ble_hs_adv_fields();
57 let mut scan_data = esp_idf_sys::ble_hs_adv_fields::default();
58
59 let mut payload_len = data.payload_len();
60
61 if payload_len > BLE_HS_ADV_MAX_SZ {
62 if self.scan_response {
63 scan_data.name = adv_data.name;
64 scan_data.name_len = adv_data.name_len;
65 if scan_data.name_len > (BLE_HS_ADV_MAX_SZ - 2) as _ {
66 scan_data.name_len = (BLE_HS_ADV_MAX_SZ - 2) as _;
67 scan_data.set_name_is_complete(0);
68 } else {
69 scan_data.set_name_is_complete(1);
70 }
71
72 adv_data.name = core::ptr::null();
73 adv_data.name_len = 0;
74 adv_data.set_name_is_complete(0);
75 } else {
76 if adv_data.tx_pwr_lvl_is_present() > 0 {
77 adv_data.set_tx_pwr_lvl_is_present(0);
78 payload_len -= 2 + 1;
79 }
80
81 if payload_len > BLE_HS_ADV_MAX_SZ {
82 adv_data.name_len -= (payload_len - BLE_HS_ADV_MAX_SZ) as u8;
83 adv_data.set_name_is_complete(0);
84 }
85 }
86 }
87
88 unsafe {
89 if self.scan_response {
90 ble!(esp_idf_sys::ble_gap_adv_rsp_set_fields(&scan_data))?;
91 }
92
93 ble!(esp_idf_sys::ble_gap_adv_set_fields(&adv_data))
94 }
95 }
96
97 pub fn set_raw_data(&mut self, data: &[u8]) -> Result<(), BLEError> {
98 let rc =
99 unsafe { esp_idf_sys::ble_gap_adv_set_data(data.as_ptr(), data.len() as i32) } as u32;
100
101 ble!(match rc {
105 esp_idf_sys::ble_error_codes_BLE_ERR_INV_HCI_CMD_PARMS => esp_idf_sys::BLE_HS_EINVAL,
106 rc => rc,
107 })
108 }
109
110 pub fn set_raw_scan_response_data(&mut self, data: &[u8]) -> Result<(), BLEError> {
111 unsafe {
112 ble!(esp_idf_sys::ble_gap_adv_rsp_set_data(
113 data.as_ptr(),
114 data.len() as i32
115 ))
116 }
117 }
118
119 pub fn advertisement_type(&mut self, adv_type: ConnMode) -> &mut Self {
121 self.adv_params.conn_mode = adv_type as _;
122 self
123 }
124
125 pub fn disc_mode(&mut self, mode: DiscMode) -> &mut Self {
127 self.adv_params.disc_mode = mode as _;
128 self
129 }
130
131 pub fn high_duty_cycle(&mut self, val: bool) -> &mut Self {
135 self.adv_params.set_high_duty_cycle(val as _);
136 self
137 }
138
139 pub fn min_interval(&mut self, interval: u16) -> &mut Self {
143 self.adv_params.itvl_min = interval;
144 self
145 }
146
147 pub fn max_interval(&mut self, interval: u16) -> &mut Self {
151 self.adv_params.itvl_max = interval;
152 self
153 }
154
155 pub fn scan_response(&mut self, value: bool) -> &mut Self {
157 self.scan_response = value;
158 self
159 }
160
161 pub fn filter_policy(&mut self, value: AdvFilterPolicy) -> &mut Self {
163 self.adv_params.filter_policy = value.into();
164 self
165 }
166
167 pub fn start(&mut self) -> Result<(), BLEError> {
170 self.start_with_duration(BLE_HS_FOREVER)
171 }
172
173 pub fn start_with_duration(&mut self, duration_ms: i32) -> Result<(), BLEError> {
175 let mut server = unsafe { Lazy::get_mut(&mut crate::ble_device::BLE_SERVER) };
176 if let Some(server) = server.as_mut()
177 && !server.started
178 {
179 server.start()?;
180 }
181
182 if self.adv_params.conn_mode == (ConnMode::Non as _) && !self.scan_response {
183 self.adv_params.disc_mode = esp_idf_sys::BLE_GAP_DISC_MODE_NON as _;
184 } else {
185 self.adv_params.disc_mode = esp_idf_sys::BLE_GAP_DISC_MODE_GEN as _;
186 }
187
188 let handle_gap_event = if server.is_some() {
189 BLEServer::handle_gap_event
190 } else {
191 Self::handle_gap_event
192 };
193 unsafe {
194 ble!(esp_idf_sys::ble_gap_adv_start(
195 crate::ble_device::OWN_ADDR_TYPE as _,
196 core::ptr::null(),
197 duration_ms,
198 &self.adv_params,
199 Some(handle_gap_event),
200 as_void_ptr(self),
201 ))?;
202 }
203
204 Ok(())
205 }
206
207 pub fn stop(&self) -> Result<(), BLEError> {
208 unsafe { ble!(esp_idf_sys::ble_gap_adv_stop()) }
209 }
210
211 pub fn is_advertising(&self) -> bool {
212 unsafe { esp_idf_sys::ble_gap_adv_active() != 0 }
213 }
214
215 pub fn on_complete(
216 &mut self,
217 callback: impl FnMut(c_int) + Send + Sync + 'static,
218 ) -> &mut Self {
219 self.on_complete = Some(Box::new(callback));
220 self
221 }
222
223 pub(crate) extern "C" fn handle_gap_event(
224 event: *mut esp_idf_sys::ble_gap_event,
225 arg: *mut c_void,
226 ) -> i32 {
227 let event = unsafe { &*event };
228 let adv = unsafe { voidp_to_ref::<Self>(arg) };
229
230 if event.type_ == esp_idf_sys::BLE_GAP_EVENT_ADV_COMPLETE as _
231 && let Some(callback) = adv.on_complete.as_mut()
232 {
233 callback(unsafe { event.__bindgen_anon_1.adv_complete.reason });
234 }
235
236 0
237 }
238}
239
240unsafe impl Send for BLEAdvertising {}