trouble_host/
gap.rs

1//! ## Generic Access Profile
2//!
3//! This profile defines the generic procedures related to discovery of
4//! Bluetooth devices (idle mode procedures) and link management aspects
5//! of connecting to Bluetooth devices (connecting mode procedures).
6//! It also defines procedures related to use of different security levels.
7//! In addition, this profile includes common format requirements for
8//! parameters accessible on the user interface level.
9
10use embassy_sync::blocking_mutex::raw::RawMutex;
11use heapless::String;
12use static_cell::StaticCell;
13
14use crate::prelude::*;
15
16/// Advertising packet is limited to 31 bytes. 9 of these are used by other GAP data, leaving 22 bytes for the Device Name characteristic
17const DEVICE_NAME_MAX_LENGTH: usize = 22;
18
19/// The number of attributes added by the GAP and GATT services
20/// GAP_SERVICE:       1
21/// ├── DEVICE_NAME:   2
22/// └── APPEARANCE:    2
23/// GATT_SERVICE:    + 1
24///                  ---
25///                  = 6
26pub const GAP_SERVICE_ATTRIBUTE_COUNT: usize = 6;
27
28/// Configuration for the GAP Service.
29pub enum GapConfig<'a> {
30    /// Peripheral device configuration.
31    Peripheral(PeripheralConfig<'a>),
32    /// Central device configuration.
33    Central(CentralConfig<'a>),
34}
35
36/// Configuration for a peripheral device GAP Service.
37pub struct PeripheralConfig<'a> {
38    /// The name of the peripheral device.
39    pub name: &'a str,
40    /// The representation of the external appearance of the device.
41    ///
42    /// Example: `&appearance::sensor::GENERIC_SENSOR.`
43    pub appearance: &'a BluetoothUuid16,
44    // TODO: Add more GAP parameters
45    // pub preferred_connection_parameters: Option<ConnectionParameters>,
46}
47
48/// Configuration for a central device GAP Service.
49pub struct CentralConfig<'a> {
50    /// The name of the central device.
51    pub name: &'a str,
52    /// The representation of the external appearance of the device.
53    ///
54    /// Example: `&appearance::sensor::GENERIC_SENSOR`
55    pub appearance: &'a BluetoothUuid16,
56    // TODO: Add more GAP parameters
57}
58
59impl<'a> GapConfig<'a> {
60    /// Create a default peripheral configuration.
61    ///
62    /// This configuration will use the `UNKNOWN` appearance.
63    pub fn default(name: &'a str) -> Self {
64        GapConfig::Peripheral(PeripheralConfig {
65            name,
66            appearance: &appearance::UNKNOWN,
67        })
68    }
69
70    /// Add the GAP config to the attribute table
71    pub fn build<M: RawMutex, const MAX: usize>(
72        self,
73        table: &mut AttributeTable<'a, M, MAX>,
74    ) -> Result<(), &'static str> {
75        match self {
76            GapConfig::Peripheral(config) => config.build(table),
77            GapConfig::Central(config) => config.build(table),
78        }
79    }
80}
81
82impl<'a> PeripheralConfig<'a> {
83    /// Add the peripheral GAP config to the attribute table
84    fn build<M: RawMutex, const MAX: usize>(self, table: &mut AttributeTable<'a, M, MAX>) -> Result<(), &'static str> {
85        static PERIPHERAL_NAME: StaticCell<String<DEVICE_NAME_MAX_LENGTH>> = StaticCell::new();
86        let peripheral_name = PERIPHERAL_NAME.init(String::new());
87        peripheral_name
88            .push_str(self.name)
89            .map_err(|_| "Device name is too long. Max length is 22 bytes")?;
90
91        let mut gap_builder = table.add_service(Service::new(service::GAP));
92        gap_builder.add_characteristic_ro(characteristic::DEVICE_NAME, peripheral_name);
93        gap_builder.add_characteristic_ro(characteristic::APPEARANCE, self.appearance);
94        gap_builder.build();
95
96        table.add_service(Service::new(service::GATT));
97
98        Ok(())
99    }
100}
101
102impl<'a> CentralConfig<'a> {
103    /// Add the peripheral GAP config to the attribute table
104    fn build<M: RawMutex, const MAX: usize>(self, table: &mut AttributeTable<'a, M, MAX>) -> Result<(), &'static str> {
105        static CENTRAL_NAME: StaticCell<String<DEVICE_NAME_MAX_LENGTH>> = StaticCell::new();
106        let central_name = CENTRAL_NAME.init(String::new());
107        central_name
108            .push_str(self.name)
109            .map_err(|_| "Device name is too long. Max length is 22 bytes")?;
110
111        let mut gap_builder = table.add_service(Service::new(service::GAP));
112        gap_builder.add_characteristic_ro(characteristic::DEVICE_NAME, central_name);
113        gap_builder.add_characteristic_ro(characteristic::APPEARANCE, self.appearance);
114        gap_builder.build();
115
116        table.add_service(Service::new(service::GATT));
117
118        Ok(())
119    }
120}