rainmaker-components 0.1.0

Component abstractions for rainmaker
Documentation
#![cfg(target_os = "espidf")]

use esp32_nimble::{
    utilities::BleUuid, BLEAdvertisementData, BLEDevice, DescriptorProperties, NimbleProperties,
};
use std::sync::{
    atomic::{AtomicBool, Ordering},
    LazyLock,
};

use crate::{ble::base::*, error::Error};

pub type ApplicationHandle = ApplicationHandleGeneric<()>;
pub type AdvertisementHandle = AdvertisementHandleGeneric<()>;

static BLE_DEVICE: LazyLock<&'static mut BLEDevice> = LazyLock::new(|| BLEDevice::take());

static ADVERTISING: AtomicBool = AtomicBool::new(false);
static SERVING: AtomicBool = AtomicBool::new(false);

impl Advertisement {
    pub fn advertise(self) -> Result<AdvertisementHandleGeneric<()>, Error> {
        if ADVERTISING.load(Ordering::SeqCst) {
            return Err(Error("Already advertising".to_string()));
        }

        let mut adv_data = BLEAdvertisementData::from(self);
        let mut adv = BLE_DEVICE.get_advertising().lock();
        if adv.set_data(&mut adv_data).is_err() {
            return Err(Error("Unable to set BLE advertising data".into()));
        }
        if adv.start().is_err() {
            return Err(Error("Failed to start BLE advertising".into()));
        }

        ADVERTISING.store(true, Ordering::SeqCst);

        Ok(AdvertisementHandleGeneric(()))
    }
}

impl From<Advertisement> for BLEAdvertisementData {
    fn from(value: Advertisement) -> Self {
        let mut adv = Self::new();
        adv.name(&value.device_name.unwrap_or_default());
        value.service_uuids.iter().for_each(|x| {
            adv.add_service_uuid(BleUuid::from(*x));
        });
        adv
    }
}

impl<T> Drop for AdvertisementHandleGeneric<T> {
    fn drop(&mut self) {
        BLEDevice::deinit_full().expect("Unable to deinit BLE");
        ADVERTISING.store(false, Ordering::SeqCst);
    }
}

impl GattApplication {
    pub fn serve(self) -> Result<ApplicationHandle, Error> {
        if SERVING.load(Ordering::SeqCst) {
            return Err(Error("Already serving.".to_string()));
        }

        let server = BLE_DEVICE.get_server();
        for srv in self.services {
            let current_service_mutex = server.create_service(srv.uuid.into());
            let mut current_service = current_service_mutex.lock();

            for chr in srv.characteristics {
                let mut chr_properties = NimbleProperties::empty();
                if chr.read.is_some() {
                    chr_properties |= NimbleProperties::READ;
                }
                if chr.write.is_some() {
                    chr_properties |= NimbleProperties::WRITE;
                }
                let curr_characteristic_mutex =
                    current_service.create_characteristic(chr.uuid.into(), chr_properties);
                let mut curr_characteristic = curr_characteristic_mutex.lock();

                if let Some(read_fn) = chr.read {
                    curr_characteristic.on_read(move |val, _desc| val.set_value(&read_fn()));
                }

                if let Some(write_fn) = chr.write {
                    curr_characteristic.on_write(move |arg| write_fn(arg.recv_data().to_vec()));
                }

                chr.descriptors.iter().for_each(|x| {
                    let descriptor_mutex = curr_characteristic
                        .create_descriptor(BleUuid::from(x.uuid), DescriptorProperties::READ);
                    let mut descriptor = descriptor_mutex.lock();
                    descriptor.set_value(&x.value);
                });
            }
        }

        SERVING.store(true, Ordering::SeqCst);
        Ok(ApplicationHandleGeneric(()))
    }
}