1use crate::{
2 BLECharacteristic, BLEConnDesc, BLEDevice, BLEError, BLEService, NimbleProperties, NotifyTx,
3 ble,
4 utilities::{BleUuid, ble_gap_conn_find, extend_lifetime_mut, mutex::Mutex},
5};
6use alloc::{boxed::Box, sync::Arc, vec::Vec};
7use core::{cell::UnsafeCell, ffi::c_void};
8use esp_idf_svc::sys as esp_idf_sys;
9
10const BLE_HS_CONN_HANDLE_NONE: u16 = esp_idf_sys::BLE_HS_CONN_HANDLE_NONE as _;
11const MAX_CONNECTIONS: usize = esp_idf_sys::CONFIG_BT_NIMBLE_MAX_CONNECTIONS as _;
12
13#[allow(clippy::type_complexity)]
14pub struct BLEServer {
15 pub(crate) started: bool,
16 advertise_on_disconnect: bool,
17 services: Vec<Arc<Mutex<BLEService>>>,
18 notify_characteristic: Vec<&'static mut BLECharacteristic>,
19 connections: heapless::Vec<u16, MAX_CONNECTIONS>,
20 indicate_wait: [u16; MAX_CONNECTIONS],
21
22 on_connect: Option<Box<dyn FnMut(&mut Self, &BLEConnDesc) + Send + Sync>>,
23 on_disconnect: Option<Box<dyn FnMut(&BLEConnDesc, Result<(), BLEError>) + Send + Sync>>,
24 on_passkey_request: Option<Box<dyn Fn() -> u32 + Send + Sync>>,
25 on_confirm_pin: Option<Box<dyn Fn(u32) -> bool + Send + Sync>>,
26 on_authentication_complete:
27 Option<Box<dyn Fn(&mut Self, &BLEConnDesc, Result<(), BLEError>) + Send + Sync>>,
28}
29
30impl BLEServer {
31 pub(crate) fn new() -> Self {
32 Self {
33 started: false,
34 advertise_on_disconnect: true,
35 services: Vec::new(),
36 notify_characteristic: Vec::new(),
37 connections: heapless::Vec::new(),
38 indicate_wait: [BLE_HS_CONN_HANDLE_NONE; MAX_CONNECTIONS],
39 on_connect: None,
40 on_disconnect: None,
41 on_passkey_request: None,
42 on_confirm_pin: None,
43 on_authentication_complete: None,
44 }
45 }
46
47 pub fn on_connect(
48 &mut self,
49 callback: impl FnMut(&mut Self, &BLEConnDesc) + Send + Sync + 'static,
50 ) -> &mut Self {
51 self.on_connect = Some(Box::new(callback));
52 self
53 }
54
55 pub fn on_disconnect(
59 &mut self,
60 callback: impl FnMut(&BLEConnDesc, Result<(), BLEError>) + Send + Sync + 'static,
61 ) -> &mut Self {
62 self.on_disconnect = Some(Box::new(callback));
63 self
64 }
65
66 pub fn on_passkey_request(
71 &mut self,
72 callback: impl Fn() -> u32 + Send + Sync + 'static,
73 ) -> &mut Self {
74 if cfg!(debug_assertions) {
75 self.on_passkey_request = Some(Box::new(move || {
76 let passkey = callback();
77 debug_assert!(
78 passkey <= 999999,
79 "passkey must be between 000000..=999999 inclusive"
80 );
81 passkey
82 }));
83 } else {
84 self.on_passkey_request = Some(Box::new(callback));
85 }
86
87 self
88 }
89
90 pub fn on_confirm_pin(
91 &mut self,
92 callback: impl Fn(u32) -> bool + Send + Sync + 'static,
93 ) -> &mut Self {
94 self.on_confirm_pin = Some(Box::new(callback));
95 self
96 }
97
98 pub fn on_authentication_complete(
104 &mut self,
105 callback: impl Fn(&mut Self, &BLEConnDesc, Result<(), BLEError>) + Send + Sync + 'static,
106 ) -> &mut Self {
107 self.on_authentication_complete = Some(Box::new(callback));
108 self
109 }
110
111 pub fn start(&mut self) -> Result<(), BLEError> {
112 if self.started {
113 return Ok(());
114 }
115
116 unsafe {
117 esp_idf_sys::ble_gatts_reset();
118 esp_idf_sys::ble_svc_gap_init();
119 esp_idf_sys::ble_svc_gatt_init();
120 }
121
122 for svc in &mut self.services {
123 svc.lock().start()?;
124 }
125
126 unsafe {
127 ble!(esp_idf_sys::ble_gatts_start())?;
128
129 for svc in &self.services {
130 let mut svc = svc.lock();
131 ble!(esp_idf_sys::ble_gatts_find_svc(
132 &svc.uuid.u,
133 &mut svc.handle
134 ))?;
135
136 for chr in &svc.characteristics {
137 let mut chr = chr.lock();
138 if chr
139 .properties
140 .intersects(NimbleProperties::INDICATE | NimbleProperties::NOTIFY)
141 {
142 let chr = &mut *chr;
143 self.notify_characteristic.push(extend_lifetime_mut(chr));
144 }
145 }
146 }
147 }
148
149 self.started = true;
150
151 Ok(())
152 }
153
154 pub fn disconnect(&mut self, conn_id: u16) -> Result<(), BLEError> {
156 self.disconnect_with_reason(
157 conn_id,
158 esp_idf_sys::ble_error_codes_BLE_ERR_REM_USER_CONN_TERM as _,
159 )
160 }
161
162 pub fn disconnect_with_reason(&mut self, conn_id: u16, reason: u8) -> Result<(), BLEError> {
164 unsafe { ble!(esp_idf_sys::ble_gap_terminate(conn_id, reason)) }
165 }
166
167 pub fn ble_gatts_show_local(&self) {
170 unsafe {
171 esp_idf_sys::ble_gatts_show_local();
172 }
173 }
174
175 pub fn connected_count(&self) -> usize {
176 self.connections.len()
177 }
178
179 pub fn connections(&self) -> impl Iterator<Item = BLEConnDesc> + '_ {
180 self.connections
181 .iter()
182 .filter_map(|x| ble_gap_conn_find(*x).ok())
183 }
184
185 pub fn create_service(&mut self, uuid: BleUuid) -> Arc<Mutex<BLEService>> {
186 let service = Arc::new(Mutex::new(BLEService::new(uuid)));
187 self.services.push(service.clone());
188 service
189 }
190
191 pub async fn get_service(&self, uuid: BleUuid) -> Option<&Arc<Mutex<BLEService>>> {
193 self.services
194 .iter()
195 .find(|x| unsafe { x.raw() }.uuid() == uuid)
196 }
197
198 pub fn advertise_on_disconnect(&mut self, value: bool) -> &mut Self {
200 self.advertise_on_disconnect = value;
201 self
202 }
203
204 pub fn update_conn_params(
213 &mut self,
214 conn_handle: u16,
215 min_interval: u16,
216 max_interval: u16,
217 latency: u16,
218 timeout: u16,
219 ) -> Result<(), BLEError> {
220 let params = esp_idf_sys::ble_gap_upd_params {
221 itvl_min: min_interval,
222 itvl_max: max_interval,
223 latency,
224 supervision_timeout: timeout,
225 min_ce_len: esp_idf_sys::BLE_GAP_INITIAL_CONN_MIN_CE_LEN as _,
226 max_ce_len: esp_idf_sys::BLE_GAP_INITIAL_CONN_MAX_CE_LEN as _,
227 };
228
229 unsafe { ble!(esp_idf_sys::ble_gap_update_params(conn_handle, ¶ms)) }
230 }
231
232 pub(crate) fn reset(&mut self) {
233 self.advertise_on_disconnect = true;
234 self.services.clear();
235 self.notify_characteristic.clear();
236 self.connections.clear();
237 self.on_connect = None;
238 self.on_disconnect = None;
239 self.on_passkey_request = None;
240 self.on_confirm_pin = None;
241 self.on_authentication_complete = None;
242 }
243
244 pub(crate) extern "C" fn handle_gap_event(
245 _event: *mut esp_idf_sys::ble_gap_event,
246 _arg: *mut c_void,
247 ) -> i32 {
248 let event = unsafe { &*_event };
249 let server = BLEDevice::take().get_server();
250
251 match event.type_ as _ {
252 esp_idf_sys::BLE_GAP_EVENT_CONNECT => {
253 let connect = unsafe { &event.__bindgen_anon_1.connect };
254 if connect.status == 0 {
255 if !server.connections.contains(&connect.conn_handle)
256 && server.connections.push(connect.conn_handle).is_err()
257 {
258 ::log::warn!(
259 "BLE connection table full; drop conn_handle={}",
260 connect.conn_handle
261 );
262 return esp_idf_sys::BLE_ATT_ERR_INSUFFICIENT_RES as _;
263 }
264
265 if let Ok(desc) = ble_gap_conn_find(connect.conn_handle) {
266 let server = UnsafeCell::new(server);
267 unsafe {
268 if let Some(callback) = (*server.get()).on_connect.as_mut() {
269 callback(*server.get(), &desc);
270 }
271 }
272 }
273 }
274 }
275 esp_idf_sys::BLE_GAP_EVENT_DISCONNECT => {
276 let disconnect = unsafe { &event.__bindgen_anon_1.disconnect };
277 if let Some(idx) = server
278 .connections
279 .iter()
280 .position(|x| *x == disconnect.conn.conn_handle)
281 {
282 server.connections.swap_remove(idx);
283 }
284
285 if let Some(callback) = server.on_disconnect.as_mut() {
286 callback(
287 &BLEConnDesc(disconnect.conn),
288 BLEError::convert(disconnect.reason as _),
289 );
290 }
291
292 #[cfg(not(esp_idf_bt_nimble_ext_adv))]
293 if server.advertise_on_disconnect
294 && let Err(err) = BLEDevice::take().get_advertising().lock().start()
295 {
296 ::log::warn!("can't start advertising: {err:?}");
297 }
298 }
299 esp_idf_sys::BLE_GAP_EVENT_SUBSCRIBE => {
300 let subscribe = unsafe { &event.__bindgen_anon_1.subscribe };
301 if let Some(chr) = server
302 .notify_characteristic
303 .iter_mut()
304 .find(|x| x.handle == subscribe.attr_handle)
305 {
306 if chr.properties.intersects(
307 NimbleProperties::READ_AUTHEN
308 | NimbleProperties::READ_AUTHOR
309 | NimbleProperties::READ_ENC,
310 ) && let Ok(desc) = ble_gap_conn_find(subscribe.conn_handle)
311 && !desc.encrypted()
312 {
313 let rc = unsafe {
314 esp_idf_sys::ble_gap_security_initiate(subscribe.conn_handle)
315 };
316 if rc != 0 {
317 ::log::error!("ble_gap_security_initiate: rc={rc}");
318 }
319 }
320
321 chr.subscribe(subscribe);
322 }
323 }
324 esp_idf_sys::BLE_GAP_EVENT_MTU => {
325 let mtu = unsafe { &event.__bindgen_anon_1.mtu };
326 ::log::info!(
327 "mtu update event; conn_handle={} mtu={}",
328 mtu.conn_handle,
329 mtu.value
330 );
331 }
332 esp_idf_sys::BLE_GAP_EVENT_NOTIFY_TX => {
333 let notify_tx = unsafe { &event.__bindgen_anon_1.notify_tx };
334 if let Some(chr) = server
335 .notify_characteristic
336 .iter_mut()
337 .find(|x| x.handle == notify_tx.attr_handle)
338 {
339 if notify_tx.indication() > 0 {
340 if notify_tx.status == 0 {
341 return 0;
342 }
343
344 BLEDevice::take()
345 .get_server()
346 .clear_indicate_wait(notify_tx.conn_handle);
347 }
348
349 if let Some(callback) = &mut chr.on_notify_tx {
350 callback(NotifyTx { notify_tx });
351 }
352 }
353 }
354 #[cfg(not(esp_idf_bt_nimble_ext_adv))]
355 esp_idf_sys::BLE_GAP_EVENT_ADV_COMPLETE => {
356 return crate::BLEAdvertising::handle_gap_event(_event, _arg);
357 }
358 #[cfg(esp_idf_bt_nimble_ext_adv)]
359 esp_idf_sys::BLE_GAP_EVENT_ADV_COMPLETE | esp_idf_sys::BLE_GAP_EVENT_SCAN_REQ_RCVD => {
360 return crate::BLEExtAdvertising::handle_gap_event(_event, _arg);
361 }
362 esp_idf_sys::BLE_GAP_EVENT_CONN_UPDATE => {
363 ::log::debug!("Connection parameters updated.");
364 }
365 esp_idf_sys::BLE_GAP_EVENT_CONN_UPDATE_REQ => {}
366 esp_idf_sys::BLE_GAP_EVENT_REPEAT_PAIRING => {
367 let repeat_pairing = unsafe { &event.__bindgen_anon_1.repeat_pairing };
368
369 let Ok(desc) = crate::utilities::ble_gap_conn_find(repeat_pairing.conn_handle)
371 else {
372 return esp_idf_sys::BLE_GAP_REPEAT_PAIRING_IGNORE as _;
373 };
374 unsafe {
375 esp_idf_sys::ble_store_util_delete_peer(&desc.0.peer_id_addr);
376 }
377
378 return esp_idf_sys::BLE_GAP_REPEAT_PAIRING_RETRY as _;
381 }
382 esp_idf_sys::BLE_GAP_EVENT_ENC_CHANGE => {
383 let enc_change = unsafe { &event.__bindgen_anon_1.enc_change };
384 let Ok(desk) = ble_gap_conn_find(enc_change.conn_handle) else {
385 return esp_idf_sys::BLE_ATT_ERR_INVALID_HANDLE as _;
386 };
387
388 let server = UnsafeCell::new(server);
389 unsafe {
390 if let Some(callback) = &(*server.get()).on_authentication_complete {
391 callback(
392 *server.get(),
393 &desk,
394 BLEError::convert(enc_change.status as _),
395 );
396 }
397 }
398 }
399 esp_idf_sys::BLE_GAP_EVENT_PASSKEY_ACTION => {
400 let passkey = unsafe { &event.__bindgen_anon_1.passkey };
401 let mut pkey = esp_idf_sys::ble_sm_io {
402 action: passkey.params.action,
403 ..Default::default()
404 };
405 match passkey.params.action as _ {
406 esp_idf_sys::BLE_SM_IOACT_DISP => {
407 pkey.__bindgen_anon_1.passkey =
408 if let Some(callback) = &server.on_passkey_request {
409 callback()
410 } else {
411 BLEDevice::take().security().get_passkey()
412 };
413
414 let rc = unsafe {
415 esp_idf_sys::ble_sm_inject_io(passkey.conn_handle, &mut pkey)
416 };
417 ::log::debug!("BLE_SM_IOACT_DISP; ble_sm_inject_io result: {rc}");
418 }
419 esp_idf_sys::BLE_SM_IOACT_NUMCMP => {
420 if let Some(callback) = &server.on_confirm_pin {
421 pkey.__bindgen_anon_1.numcmp_accept =
422 callback(passkey.params.numcmp) as _;
423 } else {
424 ::log::warn!("on_passkey_request is not setted");
425 }
426 let rc = unsafe {
427 esp_idf_sys::ble_sm_inject_io(passkey.conn_handle, &mut pkey)
428 };
429 ::log::debug!("BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: {rc}");
430 }
431 esp_idf_sys::BLE_SM_IOACT_INPUT => {
432 if let Some(callback) = &server.on_passkey_request {
433 pkey.__bindgen_anon_1.passkey = callback();
434 } else {
435 ::log::warn!("on_passkey_request is not setted");
436 }
437 let rc = unsafe {
438 esp_idf_sys::ble_sm_inject_io(passkey.conn_handle, &mut pkey)
439 };
440 ::log::debug!("BLE_SM_IOACT_INPUT; ble_sm_inject_io result: {rc}");
441 }
442 esp_idf_sys::BLE_SM_IOACT_NONE => {
443 ::log::debug!("BLE_SM_IOACT_NONE; No passkey action required");
444 }
445 action => {
446 todo!("implementation required: {}", action);
447 }
448 }
449 }
450 #[cfg(not(all(
451 esp_idf_version_major = "5",
452 any(esp_idf_version_minor = "1", esp_idf_version_minor = "2"),
453 )))]
454 esp_idf_sys::BLE_GAP_EVENT_DATA_LEN_CHG => {
455 let data_len = unsafe { &event.__bindgen_anon_1.data_len_chg };
456 ::log::debug!(
457 "==========
458data length changed:
459conn_handle={},
460tx_octets={},
461tx_time={},
462rx_octets={},
463rx_time={}
464==========",
465 data_len.conn_handle,
466 data_len.max_tx_octets,
467 data_len.max_tx_time,
468 data_len.max_rx_octets,
469 data_len.max_rx_time
470 );
471 }
472 esp_idf_sys::BLE_GAP_EVENT_IDENTITY_RESOLVED
473 | esp_idf_sys::BLE_GAP_EVENT_PHY_UPDATE_COMPLETE => {}
474 _ => {
475 ::log::warn!("unhandled event: {}", event.type_);
476 }
477 }
478
479 0
480 }
481
482 pub(super) fn set_indicate_wait(&self, conn_handle: u16) -> bool {
483 !self.indicate_wait.contains(&conn_handle)
484 }
485
486 pub(super) fn clear_indicate_wait(&mut self, conn_handle: u16) {
487 if let Some(it) = self.indicate_wait.iter_mut().find(|x| **x == conn_handle) {
488 *it = BLE_HS_CONN_HANDLE_NONE;
489 }
490 }
491}