esp32_nimble/
ble_device.rs1use alloc::{ffi::CString, vec::Vec};
2use core::{
3 ffi::c_void,
4 sync::atomic::{AtomicBool, Ordering},
5};
6use esp_idf_svc::sys as esp_idf_sys;
7use esp_idf_sys::{EspError, esp, esp_nofail};
8use once_cell::sync::Lazy;
9
10use crate::{
11 BLEAddress, BLEAddressType, BLEClient, BLEError, BLESecurity, BLEServer, ble, enums::*,
12 utilities::mutex::Mutex,
13};
14
15#[cfg(not(esp_idf_bt_nimble_ext_adv))]
16type BLEAdvertising = crate::BLEAdvertising;
17#[cfg(esp_idf_bt_nimble_ext_adv)]
18type BLEAdvertising = crate::BLEExtAdvertising;
19
20unsafe extern "C" {
21 fn ble_store_config_init();
22}
23
24#[cfg(esp32)]
25unsafe extern "C" {
26 fn ble_hs_pvcy_rpa_config(enable: u8) -> core::ffi::c_int;
27}
28#[cfg(esp32)]
29const NIMBLE_HOST_DISABLE_PRIVACY: u8 = 0x00;
30#[cfg(esp32)]
31const NIMBLE_HOST_ENABLE_RPA: u8 = 0x01;
32#[cfg(esp32)]
33const NIMBLE_HOST_ENABLE_NRPA: u8 = 0x02;
34
35static mut BLE_DEVICE: Lazy<BLEDevice> = Lazy::new(|| {
36 BLEDevice::init();
37 BLEDevice {
38 security: BLESecurity::new(),
39 }
40});
41pub static mut BLE_SERVER: Lazy<BLEServer> = Lazy::new(BLEServer::new);
42static BLE_ADVERTISING: Lazy<Mutex<BLEAdvertising>> =
43 Lazy::new(|| Mutex::new(BLEAdvertising::new()));
44
45pub static mut OWN_ADDR_TYPE: OwnAddrType = OwnAddrType::Public;
46static INITIALIZED: AtomicBool = AtomicBool::new(false);
47static SYNCED: AtomicBool = AtomicBool::new(false);
48
49pub struct BLEDevice {
50 security: BLESecurity,
51}
52
53impl BLEDevice {
54 pub fn init() {
55 unsafe {
57 let initialized = INITIALIZED.load(Ordering::Acquire);
58 if !initialized {
59 let result = esp_idf_sys::nvs_flash_init();
60 if result == esp_idf_sys::ESP_ERR_NVS_NO_FREE_PAGES
61 || result == esp_idf_sys::ESP_ERR_NVS_NEW_VERSION_FOUND
62 {
63 ::log::warn!("NVS initialisation failed. Erasing NVS.");
64 esp_nofail!(esp_idf_sys::nvs_flash_erase());
65 esp_nofail!(esp_idf_sys::nvs_flash_init());
66 }
67
68 esp_idf_sys::esp_bt_controller_mem_release(
69 esp_idf_sys::esp_bt_mode_t_ESP_BT_MODE_CLASSIC_BT,
70 );
71
72 #[cfg(esp_idf_version_major = "4")]
73 esp_nofail!(esp_idf_sys::esp_nimble_hci_and_controller_init());
74
75 esp_idf_sys::nimble_port_init();
76
77 esp_idf_sys::ble_hs_cfg.sync_cb = Some(Self::on_sync);
78 esp_idf_sys::ble_hs_cfg.reset_cb = Some(Self::on_reset);
79
80 esp_idf_sys::ble_hs_cfg.sm_io_cap = esp_idf_sys::BLE_HS_IO_NO_INPUT_OUTPUT as _;
82 esp_idf_sys::ble_hs_cfg.set_sm_bonding(0);
83 esp_idf_sys::ble_hs_cfg.set_sm_mitm(0);
84 esp_idf_sys::ble_hs_cfg.set_sm_sc(1);
85 esp_idf_sys::ble_hs_cfg.sm_our_key_dist = 1;
86 esp_idf_sys::ble_hs_cfg.sm_their_key_dist = 3;
87 esp_idf_sys::ble_hs_cfg.store_status_cb =
88 Some(esp_idf_sys::ble_store_util_status_rr);
89
90 ble_store_config_init();
91
92 esp_idf_sys::nimble_port_freertos_init(Some(Self::blecent_host_task));
93 }
94
95 loop {
96 let syncd = SYNCED.load(Ordering::Acquire);
97 if syncd {
98 break;
99 }
100 esp_idf_sys::vPortYield();
101 }
102
103 INITIALIZED.store(true, Ordering::Release);
104 }
105 }
106
107 pub fn take() -> &'static mut Self {
108 unsafe { Lazy::force_mut(&mut BLE_DEVICE) }
109 }
110
111 pub fn deinit() -> Result<(), EspError> {
115 if !INITIALIZED.load(Ordering::Acquire) {
116 ::log::info!("BLE stack is not initialized, skipping deinit.");
117 return Ok(());
118 }
119
120 unsafe {
121 esp!(esp_idf_sys::nimble_port_stop())?;
122
123 #[cfg(esp_idf_version_major = "4")]
124 {
125 esp_idf_sys::nimble_port_deinit();
126 esp!(esp_idf_sys::esp_nimble_hci_and_controller_deinit())?;
127 }
128
129 #[cfg(not(esp_idf_version_major = "4"))]
130 esp!(esp_idf_sys::nimble_port_deinit())?;
131
132 INITIALIZED.store(false, Ordering::Release);
133 SYNCED.store(false, Ordering::Release);
134
135 if let Some(server) = Lazy::get_mut(&mut BLE_SERVER) {
136 server.started = false;
137 }
138 };
139
140 Ok(())
141 }
142
143 pub fn deinit_full() -> Result<(), EspError> {
149 Self::deinit()?;
150 unsafe {
151 #[cfg(not(esp_idf_bt_nimble_ext_adv))]
152 if let Some(advertising) = Lazy::get(&BLE_ADVERTISING) {
153 advertising.lock().reset().unwrap();
154 }
155
156 if let Some(server) = Lazy::get_mut(&mut BLE_SERVER) {
157 server.reset();
158 }
159 }
160 Ok(())
161 }
162
163 pub fn new_client(&self) -> BLEClient {
164 BLEClient::new()
165 }
166
167 pub fn get_server(&self) -> &'static mut BLEServer {
168 unsafe { Lazy::force_mut(&mut BLE_SERVER) }
169 }
170
171 pub fn get_advertising(&self) -> &'static Mutex<BLEAdvertising> {
172 Lazy::force(&BLE_ADVERTISING)
173 }
174
175 pub fn set_power(
176 &mut self,
177 power_type: PowerType,
178 power_level: PowerLevel,
179 ) -> Result<(), BLEError> {
180 unsafe {
181 ble!(esp_idf_sys::esp_ble_tx_power_set(
182 power_type.into(),
183 power_level.into()
184 ))
185 }
186 }
187
188 pub fn get_power(&self, power_type: PowerType) -> PowerLevel {
189 PowerLevel::try_from(unsafe { esp_idf_sys::esp_ble_tx_power_get(power_type.into()) })
190 .unwrap()
191 }
192
193 pub fn set_preferred_mtu(&self, mtu: u16) -> Result<(), BLEError> {
201 unsafe { ble!(esp_idf_sys::ble_att_set_preferred_mtu(mtu)) }
202 }
203
204 pub fn get_preferred_mtu(&self) -> u16 {
207 unsafe { esp_idf_sys::ble_att_preferred_mtu() }
208 }
209
210 pub fn bonded_addresses(&self) -> Result<Vec<BLEAddress>, BLEError> {
212 let mut peer_id_addrs =
213 [esp_idf_sys::ble_addr_t::default(); esp_idf_sys::MYNEWT_VAL_BLE_STORE_MAX_BONDS as _];
214 let mut num_peers: core::ffi::c_int = 0;
215
216 unsafe {
217 ble!(esp_idf_sys::ble_store_util_bonded_peers(
218 peer_id_addrs.as_mut_ptr(),
219 &mut num_peers,
220 esp_idf_sys::MYNEWT_VAL_BLE_STORE_MAX_BONDS as _,
221 ))?
222 };
223
224 let mut result = Vec::with_capacity(esp_idf_sys::MYNEWT_VAL_BLE_STORE_MAX_BONDS as _);
225 for addr in peer_id_addrs.iter().take(num_peers as _) {
226 result.push(BLEAddress::from(*addr));
227 }
228
229 Ok(result)
230 }
231
232 pub fn delete_all_bonds(&self) -> Result<(), BLEError> {
234 unsafe { ble!(esp_idf_sys::ble_store_clear()) }
235 }
236
237 pub fn delete_bond(&self, address: &BLEAddress) -> Result<(), BLEError> {
241 unsafe { ble!(esp_idf_sys::ble_gap_unpair(&address.value)) }
242 }
243
244 pub fn set_white_list(&mut self, white_list: &[BLEAddress]) -> Result<(), BLEError> {
245 unsafe {
246 ble!(esp_idf_sys::ble_gap_wl_set(
247 white_list.as_ptr() as _,
248 white_list.len() as _
249 ))
250 }
251 }
252
253 pub fn security(&mut self) -> &mut BLESecurity {
254 &mut self.security
255 }
256
257 pub fn get_addr(&self) -> Result<BLEAddress, BLEError> {
258 let mut addr = [0; 6];
259
260 unsafe {
261 ble!(esp_idf_sys::ble_hs_id_copy_addr(
262 OWN_ADDR_TYPE.into(),
263 addr.as_mut_ptr(),
264 core::ptr::null_mut()
265 ))?;
266
267 let addr_type = match OWN_ADDR_TYPE {
268 OwnAddrType::Public => BLEAddressType::Public,
269 _ => BLEAddressType::Random,
270 };
271
272 Ok(BLEAddress::from_le_bytes(addr, addr_type))
273 }
274 }
275
276 pub fn set_own_addr_type(&mut self, own_addr_type: OwnAddrType) {
278 self._set_own_addr_type(own_addr_type, false);
279 }
280
281 #[cfg(esp32)]
283 pub fn set_own_addr_type_to_non_resolvable_random(&mut self) {
284 self._set_own_addr_type(OwnAddrType::Random, true);
285 }
286
287 #[allow(unused_variables)]
288 fn _set_own_addr_type(&mut self, own_addr_type: OwnAddrType, use_nrpa: bool) {
289 unsafe {
290 OWN_ADDR_TYPE = own_addr_type;
291 match own_addr_type {
292 OwnAddrType::Public => {
293 #[cfg(esp32)]
294 ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
295 }
296 OwnAddrType::Random => {
297 self.security().resolve_rpa();
298 #[cfg(esp32)]
299 ble_hs_pvcy_rpa_config(if use_nrpa {
300 NIMBLE_HOST_ENABLE_NRPA
301 } else {
302 NIMBLE_HOST_ENABLE_RPA
303 });
304 }
305 OwnAddrType::RpaPublicDefault | OwnAddrType::RpaRandomDefault => {
306 self.security().resolve_rpa();
307 #[cfg(esp32)]
308 ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
309 }
310 }
311 }
312 }
313
314 pub fn set_rnd_addr(&mut self, mut addr: [u8; 6]) -> Result<(), BLEError> {
316 addr.reverse();
317 unsafe { ble!(esp_idf_sys::ble_hs_id_set_rnd(addr.as_ptr())) }
318 }
319
320 #[allow(dangling_pointers_from_temporaries)]
321 pub fn set_device_name(device_name: &str) -> Result<(), BLEError> {
322 unsafe {
323 ble!(esp_idf_sys::ble_svc_gap_device_name_set(
324 CString::new(device_name).unwrap().as_ptr().cast()
325 ))
326 }
327 }
328
329 extern "C" fn on_sync() {
330 unsafe {
331 esp_idf_sys::ble_hs_util_ensure_addr(0);
332
333 esp_nofail!(esp_idf_sys::ble_hs_id_infer_auto(
334 0,
335 core::mem::transmute::<&mut OwnAddrType, &mut u8>(&mut OWN_ADDR_TYPE) as *mut _
336 ));
337
338 let mut addr = [0; 6];
339 esp_nofail!(esp_idf_sys::ble_hs_id_copy_addr(
340 OWN_ADDR_TYPE.into(),
341 addr.as_mut_ptr(),
342 core::ptr::null_mut()
343 ));
344 ::log::info!(
345 "Device Address: {:X}:{:X}:{:X}:{:X}:{:X}:{:X}",
346 addr[5],
347 addr[4],
348 addr[3],
349 addr[2],
350 addr[1],
351 addr[0]
352 );
353
354 SYNCED.store(true, Ordering::Release);
355 }
356 }
357
358 extern "C" fn on_reset(reason: i32) {
359 ::log::info!("Resetting state; reason={reason}");
360 }
361
362 extern "C" fn blecent_host_task(_: *mut c_void) {
363 unsafe {
364 ::log::info!("BLE Host Task Started");
365 esp_idf_sys::nimble_port_run();
366 esp_idf_sys::nimble_port_freertos_deinit();
367 }
368 }
369}