esp32-nimble 0.12.0

A wrapper for the ESP32 NimBLE Bluetooth stack.
use alloc::{sync::Arc, vec::Vec};
use esp_idf_svc::sys as esp_idf_sys;
use esp_idf_sys::ble_uuid_any_t;

use crate::{
    BLECharacteristic, BLEError, ble,
    utilities::{BleUuid, mutex::Mutex},
};

use super::ble_characteristic::NimbleProperties;

const NULL_HANDLE: u16 = 0xFFFF;

pub struct BLEService {
    pub(crate) uuid: ble_uuid_any_t,
    pub(crate) handle: u16,
    pub(crate) characteristics: Vec<Arc<Mutex<BLECharacteristic>>>,
    svc_def: Option<[esp_idf_sys::ble_gatt_svc_def; 2]>,
    svc_def_characteristics: Vec<esp_idf_sys::ble_gatt_chr_def>,
}

impl BLEService {
    pub(crate) fn new(uuid: BleUuid) -> Self {
        Self {
            uuid: ble_uuid_any_t::from(uuid),
            handle: NULL_HANDLE,
            characteristics: Vec::new(),
            svc_def: None,
            svc_def_characteristics: Vec::new(),
        }
    }

    pub fn uuid(&self) -> BleUuid {
        BleUuid::from(self.uuid)
    }

    pub(crate) fn start(&mut self) -> Result<(), BLEError> {
        let svc_def = self.svc_def.get_or_insert_with(|| {
            let mut svc = [esp_idf_sys::ble_gatt_svc_def::default(); 2];
            svc[0].type_ = esp_idf_sys::BLE_GATT_SVC_TYPE_PRIMARY as _;
            svc[0].uuid = unsafe { &self.uuid.u };
            svc[0].includes = core::ptr::null_mut();

            if self.characteristics.is_empty() {
            } else {
                for chr in &mut self.characteristics {
                    let arg =
                        unsafe { Arc::get_mut_unchecked(chr) } as *mut Mutex<BLECharacteristic>;
                    let mut chr = chr.lock();
                    self.svc_def_characteristics
                        .push(esp_idf_sys::ble_gatt_chr_def {
                            uuid: unsafe { &chr.uuid.u },
                            access_cb: Some(BLECharacteristic::handle_gap_event),
                            arg: arg as _,
                            descriptors: chr.construct_svc_def_descriptors(),
                            flags: chr.properties.bits(),
                            min_key_size: 0,
                            val_handle: &mut chr.handle,
                            #[cfg(cpfd)]
                            cpfd: chr.cpfd.as_mut_ptr(),
                        });
                }
                self.svc_def_characteristics
                    .push(esp_idf_sys::ble_gatt_chr_def::default());
                svc[0].characteristics = self.svc_def_characteristics.as_ptr();
            }

            svc[1].type_ = 0;
            svc
        });

        unsafe {
            ble!(esp_idf_sys::ble_gatts_count_cfg(svc_def.as_ptr()))?;
            ble!(esp_idf_sys::ble_gatts_add_svcs(svc_def.as_ptr()))?;
        }
        Ok(())
    }

    pub fn create_characteristic(
        &mut self,
        uuid: BleUuid,
        properties: NimbleProperties,
    ) -> Arc<Mutex<BLECharacteristic>> {
        let characteristic = Arc::new(Mutex::new(BLECharacteristic::new(uuid, properties)));
        self.characteristics.push(characteristic.clone());
        characteristic
    }

    /// Get the characteristic object for the UUID.
    pub async fn get_characteristic(
        &self,
        uuid: BleUuid,
    ) -> Option<&Arc<Mutex<BLECharacteristic>>> {
        self.characteristics
            .iter()
            .find(|x| unsafe { x.raw() }.uuid() == uuid)
    }
}

unsafe impl Send for BLEService {}