diff --git a/src/ble.rs b/src/ble.rs index bdb32be..9546519 100644 --- a/src/ble.rs +++ b/src/ble.rs @@ -39,11 +39,69 @@ pub enum BleSendable { SpatemRaw(io::msg::RawMsgRx), } -#[gatt_server] -struct Server { +// GAP + 4 GATT attributes + our service(s) +const _ATTRIBUTE_TABLE_SIZE: usize = GAP_SERVICE_ATTRIBUTE_COUNT + 4 + CITSEvents::ATTRIBUTE_COUNT; + +pub struct Server<'values> { + server: AttributeServer< + 'values, + embassy_sync::blocking_mutex::raw::NoopRawMutex, + DefaultPacketPool, + _ATTRIBUTE_TABLE_SIZE, + 1, + CONNECTIONS_MAX, + >, c_its_events: CITSEvents, } +impl<'values> Server<'values> { + // we implement this ourselves to add the "SERVICE_CHANGED" GATT characteristic, + // otherwise client devices won't update the service's characteristics when they change. + fn new_with_config(config: PeripheralConfig<'values>) -> Result { + let mut table: AttributeTable< + '_, + embassy_sync::blocking_mutex::raw::NoopRawMutex, + _ATTRIBUTE_TABLE_SIZE, + > = AttributeTable::new(); + + // manual implementation of: gap.build(&mut table)?; + if !config.name.is_ascii() || config.name.len() > 22 { + return Err("Device name is too long. Max length is 22 bytes"); + } + + let mut gap_builder = table.add_service(Service::new(service::GAP)); + gap_builder.add_characteristic_ro(characteristic::DEVICE_NAME, config.name.as_bytes()); + gap_builder.add_characteristic_ro(characteristic::APPEARANCE, config.appearance); + gap_builder.build(); + + // add custom GATT service + let mut gatt_builder = table.add_service(Service::new(service::GATT)); + gatt_builder.add_characteristic_ro(characteristic::SERVICE_CHANGED, &[1, 0, 255, 255]); // [0x0001, 0xFFFF] in little-endian + gatt_builder.build(); + + let c_its_events = CITSEvents::new(&mut table); + + Ok(Self { + server: AttributeServer::new(table), + c_its_events, + }) + } +} + +impl<'values> core::ops::Deref for Server<'values> { + type Target = AttributeServer< + 'values, + embassy_sync::blocking_mutex::raw::NoopRawMutex, + DefaultPacketPool, + _ATTRIBUTE_TABLE_SIZE, + 1, + CONNECTIONS_MAX, + >; + fn deref(&self) -> &Self::Target { + &self.server + } +} + #[gatt_service(uuid = "c0b70000-d4f4-4000-ada8-f99a02ee315c")] struct CITSEvents { #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "Position", read, value = "PositionState proto msg", type = &'static str)] @@ -135,10 +193,10 @@ pub async fn run( "Starting advertising and GATT service for {}", features.join(",") ); - let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig { + let server = Server::new_with_config(PeripheralConfig { name: &name, appearance: &appearance::UNKNOWN, - })) + }) .unwrap(); let _ = embassy_futures::join::join(ble_task(runner), async {