esp32_nimble/client/
ble_advertised_data.rs

1use alloc::vec::Vec;
2use bstr::BStr;
3use esp_idf_svc::sys;
4
5use crate::{enums::AdvFlag, utilities::BleUuid};
6
7pub struct BLEAdvertisedData<T>
8where
9    T: AsRef<[u8]>,
10{
11    payload: T,
12}
13
14impl<T: AsRef<[u8]>> BLEAdvertisedData<T> {
15    pub(crate) fn new(payload: T) -> Self {
16        Self { payload }
17    }
18
19    pub fn payload(&self) -> &[u8] {
20        self.payload.as_ref()
21    }
22
23    #[allow(clippy::should_implement_trait)]
24    pub fn clone(&self) -> BLEAdvertisedData<Vec<u8>> {
25        BLEAdvertisedData::<Vec<u8>>::new(self.payload().to_vec())
26    }
27
28    /// Get the advertisement flags.
29    pub fn adv_flags(&self) -> Option<AdvFlag> {
30        let data = self
31            .decode()
32            .find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_FLAGS as _))?;
33
34        let ad_flag = data.data.first()?;
35        AdvFlag::from_bits(*ad_flag)
36    }
37
38    pub fn is_advertising_service(&self, uuid: &BleUuid) -> bool {
39        self.service_uuids().any(|x| &x == uuid)
40    }
41
42    pub fn service_uuids(&self) -> impl Iterator<Item = BleUuid> + '_ {
43        ServiceUuidsIter {
44            iter: self.decode(),
45            current: None,
46        }
47    }
48
49    pub fn name(&self) -> Option<&BStr> {
50        let data = self.decode().find(|x| {
51            x.ty == (sys::BLE_HS_ADV_TYPE_COMP_NAME as _)
52                || x.ty == (sys::BLE_HS_ADV_TYPE_INCOMP_NAME as _)
53        })?;
54        Some(BStr::new(data.data))
55    }
56
57    pub fn tx_power(&self) -> Option<u8> {
58        let data = self
59            .decode()
60            .find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_TX_PWR_LVL as _))?;
61
62        data.data.first().copied()
63    }
64
65    pub fn service_data(&self) -> Option<BLEServiceData<'_>> {
66        for x in self.decode() {
67            match x.ty as u32 {
68                sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID16 => {
69                    if let Some((uuid, service_data)) = x.data.split_at_checked(2) {
70                        let uuid =
71                            BleUuid::from_uuid16(u16::from_le_bytes(uuid.try_into().unwrap()));
72                        return Some(BLEServiceData { uuid, service_data });
73                    } else {
74                        ::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID16");
75                    }
76                }
77                sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID32 => {
78                    if let Some((uuid, service_data)) = x.data.split_at_checked(4) {
79                        let uuid =
80                            BleUuid::from_uuid32(u32::from_le_bytes(uuid.try_into().unwrap()));
81                        return Some(BLEServiceData { uuid, service_data });
82                    } else {
83                        ::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID32");
84                    }
85                }
86                sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID128 => {
87                    if let Some((uuid, service_data)) = x.data.split_at_checked(16) {
88                        let uuid = BleUuid::from_uuid128(uuid.try_into().unwrap());
89                        return Some(BLEServiceData { uuid, service_data });
90                    } else {
91                        ::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID128");
92                    }
93                }
94                _ => {}
95            }
96        }
97        None
98    }
99
100    pub fn manufacture_data(&self) -> Option<ManufactureData<'_>> {
101        let data = self
102            .decode()
103            .find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_MFG_DATA as _))?;
104
105        let (id, payload) = data.data.split_at_checked(2)?;
106        Some(ManufactureData {
107            company_identifier: u16::from_le_bytes(id.try_into().unwrap()),
108            payload,
109        })
110    }
111
112    fn decode(&self) -> AdStructureIter<'_> {
113        AdStructureIter {
114            payload: self.payload(),
115        }
116    }
117}
118
119impl<T: AsRef<[u8]>> core::fmt::Debug for BLEAdvertisedData<T> {
120    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121        let mut f = f.debug_struct("BLEAdvertisedData");
122
123        #[cfg(feature = "debug")]
124        f.field("types", &self.decode().map(|x| x.ty).collect::<Vec<_>>());
125
126        if let Some(adv_flags) = self.adv_flags() {
127            f.field("adv_flags", &adv_flags);
128        }
129        if let Some(name) = self.name() {
130            f.field("name", &name);
131        }
132        if let Some(tx_power) = self.tx_power() {
133            f.field("tx_power", &tx_power);
134        }
135        if let Some(manufacture_data) = self.manufacture_data() {
136            f.field("manufacture_data", &manufacture_data);
137        }
138        let service_uuids: Vec<BleUuid> = self.service_uuids().collect();
139        if !service_uuids.is_empty() {
140            f.field("service_uuids", &service_uuids);
141        }
142        if let Some(service_data) = self.service_data() {
143            f.field("service_data", &service_data);
144        }
145        f.finish()
146    }
147}
148
149#[derive(Debug, Copy, Clone)]
150pub struct BLEServiceData<'a> {
151    pub uuid: BleUuid,
152    pub service_data: &'a [u8],
153}
154
155#[derive(Debug, Copy, Clone)]
156pub struct ManufactureData<'a> {
157    pub company_identifier: u16,
158    pub payload: &'a [u8],
159}
160
161struct AdData<'d> {
162    ty: u8,
163    data: &'d [u8],
164}
165
166struct AdStructureIter<'d> {
167    payload: &'d [u8],
168}
169
170impl<'d> Iterator for AdStructureIter<'d> {
171    type Item = AdData<'d>;
172
173    fn next(&mut self) -> Option<Self::Item> {
174        let length = (*self.payload.first()?) as usize;
175        let (data, next_payload) = self.payload.split_at_checked(1 + length)?;
176        self.payload = next_payload;
177        if length == 0 {
178            return None;
179        }
180        Some(AdData {
181            ty: unsafe { *data.get_unchecked(1) },
182            data: data.get(2..(length + 1)).unwrap(),
183        })
184    }
185}
186
187struct ServiceUuidsIter<'d> {
188    iter: AdStructureIter<'d>,
189    current: Option<AdData<'d>>,
190}
191
192impl Iterator for ServiceUuidsIter<'_> {
193    type Item = BleUuid;
194
195    fn next(&mut self) -> Option<Self::Item> {
196        if self.current.is_none() {
197            loop {
198                self.current = self.iter.next();
199                if let Some(current) = &self.current {
200                    if matches!(
201                        current.ty as u32,
202                        sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS16
203                            | sys::BLE_HS_ADV_TYPE_COMP_UUIDS16
204                            | sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS32
205                            | sys::BLE_HS_ADV_TYPE_COMP_UUIDS32
206                            | sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS128
207                            | sys::BLE_HS_ADV_TYPE_COMP_UUIDS128
208                    ) {
209                        break;
210                    }
211                } else {
212                    return None;
213                }
214            }
215        }
216
217        let current = self.current.as_mut().unwrap();
218        match current.ty as u32 {
219            sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS16 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS16 => {
220                if let Some((uuid, next)) = current.data.split_at_checked(2) {
221                    current.data = next;
222                    Some(BleUuid::from_uuid16(u16::from_le_bytes(
223                        uuid.try_into().unwrap(),
224                    )))
225                } else {
226                    self.current = None;
227                    self.next()
228                }
229            }
230            sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS32 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS32 => {
231                if let Some((uuid, next)) = current.data.split_at_checked(4) {
232                    current.data = next;
233                    Some(BleUuid::from_uuid32(u32::from_le_bytes(
234                        uuid.try_into().unwrap(),
235                    )))
236                } else {
237                    self.current = None;
238                    self.next()
239                }
240            }
241            sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS128 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS128 => {
242                if let Ok(data) = current.data.try_into() {
243                    self.current = None;
244                    Some(BleUuid::Uuid128(data))
245                } else {
246                    self.current = None;
247                    self.next()
248                }
249            }
250            _ => unreachable!(),
251        }
252    }
253}