esp32_nimble/
ble_device.rs

1use 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        // NVS initialisation.
56        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                // Set initial security capabilities
81                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    /// Shutdown the NimBLE stack/controller
112    ///
113    /// To restart, call `BLEDevice::init()`.
114    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    /// Shutdown the NimBLE stack/controller.
144    ///
145    /// server/advertising/scan will be reset.
146    ///
147    /// To restart, call `BLEDevice::init()`.
148    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    /// Sets the preferred ATT MTU; the device will indicate this value in all subsequent ATT MTU exchanges.
194    /// The ATT MTU of a connection is equal to the lower of the two peers’preferred MTU values.
195    /// The ATT MTU is what dictates the maximum size of any message sent during a GATT procedure.
196    ///
197    /// The specified MTU must be within the following range: [23, BLE_ATT_MTU_MAX].
198    /// 23 is a minimum imposed by the Bluetooth specification;
199    /// BLE_ATT_MTU_MAX is a NimBLE compile-time setting.
200    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    /// Retrieves the preferred ATT MTU.
205    /// This is the value indicated by the device during an ATT MTU exchange.
206    pub fn get_preferred_mtu(&self) -> u16 {
207        unsafe { esp_idf_sys::ble_att_preferred_mtu() }
208    }
209
210    /// Get the addresses of all bonded peer device.
211    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    /// Deletes all bonding information.
233    pub fn delete_all_bonds(&self) -> Result<(), BLEError> {
234        unsafe { ble!(esp_idf_sys::ble_store_clear()) }
235    }
236
237    /// Deletes a peer bond.
238    ///
239    /// * `address`: The address of the peer with which to delete bond info.
240    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    /// Set the own address type.
277    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    /// Set the own address type to non-resolvable random address.
282    #[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    /// Set the own address to be used when the address type is random.
315    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}