esp32_nimble/client/
ble_advertised_data.rs1use 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 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}