BLE: Add "service change attribute", advertise service
This commit is contained in:
parent
059f47df56
commit
8e6bea372a
1 changed files with 75 additions and 12 deletions
87
src/ble.rs
87
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<Self, &'static str> {
|
||||
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)]
|
||||
|
|
@ -56,7 +114,7 @@ struct CITSEvents {
|
|||
raw_notify: [u8; io::msg::RawNotify::PROTO_SIZE],
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "CAM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "CAM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4003-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
cam_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
#[cfg(feature = "cam")]
|
||||
|
|
@ -65,7 +123,7 @@ struct CITSEvents {
|
|||
cam_event: [u8; io::msg::CamEvent::PROTO_SIZE],
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "DENM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "DENM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4005-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
denm_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
#[cfg(feature = "denm")]
|
||||
|
|
@ -74,11 +132,11 @@ struct CITSEvents {
|
|||
denm_event: [u8; io::msg::DenmEvent::PROTO_SIZE],
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "MAPEM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "MAPEM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4007-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
mapem_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
#[cfg(feature = "spat")]
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "SPATEM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "SPATEM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4008-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
spatem_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
}
|
||||
|
|
@ -135,16 +193,18 @@ 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 {
|
||||
loop {
|
||||
match advertise("ESP32 C-ITS", &mut peripheral, &server).await {
|
||||
match advertise(&mut peripheral, &server).await {
|
||||
Ok(conn) => {
|
||||
info!("BLE: New client connected");
|
||||
|
||||
// set up tasks when the connection is established to a central, so they don't run when no one is connected.
|
||||
let a = gatt_events_task(&server, &conn);
|
||||
let b = publishing_task(&server, &conn);
|
||||
|
|
@ -158,7 +218,7 @@ pub async fn run(
|
|||
});
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("BLE ADV: fatal error {e:?}");
|
||||
panic!("BLE ADV: fatal error: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -183,15 +243,18 @@ pub fn update(payload: BleSendable) {
|
|||
|
||||
/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect.
|
||||
async fn advertise<'values, 'server, C: Controller>(
|
||||
name: &'values str,
|
||||
peripheral: &mut Peripheral<'values, C, DefaultPacketPool>,
|
||||
server: &'server Server<'values>,
|
||||
) -> Result<GattConnection<'values, 'server, DefaultPacketPool>, BleHostError<C::Error>> {
|
||||
let mut advertiser_data = [0; 35];
|
||||
let service_uuid = Uuid::from(0xc0b7_0000_d4f4_4000_ada8_f99a_02ee_315c_u128)
|
||||
.as_raw()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let len = AdStructure::encode_slice(
|
||||
&[
|
||||
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
|
||||
AdStructure::CompleteLocalName(name.as_bytes()),
|
||||
AdStructure::ServiceUuids128(&[service_uuid]),
|
||||
],
|
||||
&mut advertiser_data[..],
|
||||
)?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue