nrf_softdevice/ble/
advertisement_builder.rs1#[cfg(feature = "defmt")]
2use defmt::Format;
3
4const LEGACY_PAYLOAD_LEN: usize = 31;
5const EXTENDED_PAYLOAD_LEN: usize = 254;
6
7#[derive(Debug, Clone, Copy)]
8#[cfg_attr(feature = "defmt", derive(Format))]
9pub enum Error {
10 Oversize { expected: usize },
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[cfg_attr(feature = "defmt", derive(Format))]
15pub struct AdvertisementDataType(u8);
16
17impl AdvertisementDataType {
18 pub const FLAGS: AdvertisementDataType = AdvertisementDataType(0x01);
19 pub const INCOMPLETE_16_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x02);
20 pub const COMPLETE_16_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x03);
21 pub const INCOMPLETE_32_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x04);
22 pub const COMPLETE_32_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x05);
23 pub const INCOMPLETE_128_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x06);
24 pub const COMPLETE_128_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x07);
25 pub const SHORT_NAME: AdvertisementDataType = AdvertisementDataType(0x08);
26 pub const FULL_NAME: AdvertisementDataType = AdvertisementDataType(0x09);
27 pub const TXPOWER_LEVEL: AdvertisementDataType = AdvertisementDataType(0x0a);
28 pub const PERIPHERAL_CONNECTION_INTERVAL_RANGE: AdvertisementDataType = AdvertisementDataType(0x12);
29 pub const SERVICE_SOLICITATION_16: AdvertisementDataType = AdvertisementDataType(0x14);
30 pub const SERVICE_SOLICITATION_128: AdvertisementDataType = AdvertisementDataType(0x15);
31 pub const SERVICE_SOLICITATION_32: AdvertisementDataType = AdvertisementDataType(0x1f);
32 pub const SERVICE_DATA_16: AdvertisementDataType = AdvertisementDataType(0x16);
33 pub const SERVICE_DATA_32: AdvertisementDataType = AdvertisementDataType(0x20);
34 pub const SERVICE_DATA_128: AdvertisementDataType = AdvertisementDataType(0x21);
35 pub const APPEARANCE: AdvertisementDataType = AdvertisementDataType(0x19);
36 pub const PUBLIC_TARGET_ADDRESS: AdvertisementDataType = AdvertisementDataType(0x17);
37 pub const RANDOM_TARGET_ADDRESS: AdvertisementDataType = AdvertisementDataType(0x18);
38 pub const ADVERTISING_INTERVAL: AdvertisementDataType = AdvertisementDataType(0x1a);
39 pub const URI: AdvertisementDataType = AdvertisementDataType(0x24);
40 pub const LE_SUPPORTED_FEATURES: AdvertisementDataType = AdvertisementDataType(0x27);
41 pub const MANUFACTURER_SPECIFIC_DATA: AdvertisementDataType = AdvertisementDataType(0xff);
42
43 pub const fn from_u8(value: u8) -> Self {
44 AdvertisementDataType(value)
45 }
46
47 pub const fn to_u8(self) -> u8 {
48 self.0
49 }
50}
51
52impl From<u8> for AdvertisementDataType {
53 fn from(value: u8) -> Self {
54 AdvertisementDataType(value)
55 }
56}
57
58impl From<AdvertisementDataType> for u8 {
59 fn from(value: AdvertisementDataType) -> Self {
60 value.0
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65#[cfg_attr(feature = "defmt", derive(Format))]
66#[repr(u8)]
67pub enum Flag {
68 LimitedDiscovery = 0b1,
69 GeneralDiscovery = 0b10,
70 #[allow(non_camel_case_types)]
71 LE_Only = 0b100,
72
73 Bit3 = 0b1000,
75 Bit4 = 0b10000,
76 }
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "defmt", derive(Format))]
81pub enum ServiceList {
82 Incomplete,
83 Complete,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87#[cfg_attr(feature = "defmt", derive(Format))]
88pub struct ServiceUuid16(u16);
89
90impl ServiceUuid16 {
91 pub const GENERIC_ACCESS: ServiceUuid16 = ServiceUuid16(0x1800);
92 pub const GENERIC_ATTRIBUTE: ServiceUuid16 = ServiceUuid16(0x1801);
93 pub const IMMEDIATE_ALERT: ServiceUuid16 = ServiceUuid16(0x1802);
94 pub const LINK_LOSS: ServiceUuid16 = ServiceUuid16(0x1803);
95 pub const TX_POWER: ServiceUuid16 = ServiceUuid16(0x1804);
96 pub const CURRENT_TIME: ServiceUuid16 = ServiceUuid16(0x1805);
97 pub const REFERENCE_TIME_UPDATE: ServiceUuid16 = ServiceUuid16(0x1806);
98 pub const NEXT_DST_CHANGE: ServiceUuid16 = ServiceUuid16(0x1807);
99 pub const GLUCOSE: ServiceUuid16 = ServiceUuid16(0x1808);
100 pub const HEALTH_THERMOMETER: ServiceUuid16 = ServiceUuid16(0x1809);
101 pub const DEVICE_INFORMATION: ServiceUuid16 = ServiceUuid16(0x180A);
102 pub const HEART_RATE: ServiceUuid16 = ServiceUuid16(0x180D);
103 pub const PHONE_ALERT_STATUS: ServiceUuid16 = ServiceUuid16(0x180E);
104 pub const BATTERY: ServiceUuid16 = ServiceUuid16(0x180F);
105 pub const BLOOD_PRESSURE: ServiceUuid16 = ServiceUuid16(0x1810);
106 pub const ALERT_NOTIFICATION: ServiceUuid16 = ServiceUuid16(0x1811);
107 pub const HUMAN_INTERFACE_DEVICE: ServiceUuid16 = ServiceUuid16(0x1812);
108 pub const SCAN_PARAMETERS: ServiceUuid16 = ServiceUuid16(0x1813);
109 pub const RUNNNIG_SPEED_AND_CADENCE: ServiceUuid16 = ServiceUuid16(0x1814);
110 pub const AUTOMATION_IO: ServiceUuid16 = ServiceUuid16(0x1815);
111 pub const CYCLING_SPEED_AND_CADENCE: ServiceUuid16 = ServiceUuid16(0x1816);
112 pub const CYCLING_POWER: ServiceUuid16 = ServiceUuid16(0x1818);
113 pub const LOCATION_AND_NAVIGATION: ServiceUuid16 = ServiceUuid16(0x1819);
114 pub const ENVIRONMENTAL_SENSING: ServiceUuid16 = ServiceUuid16(0x181A);
115 pub const BODY_COMPOSITION: ServiceUuid16 = ServiceUuid16(0x181B);
116 pub const USER_DATA: ServiceUuid16 = ServiceUuid16(0x181C);
117 pub const WEIGHT_SCALE: ServiceUuid16 = ServiceUuid16(0x181D);
118 pub const BOND_MANAGEMENT: ServiceUuid16 = ServiceUuid16(0x181E);
119 pub const CONTINOUS_GLUCOSE_MONITORING: ServiceUuid16 = ServiceUuid16(0x181F);
120 pub const INTERNET_PROTOCOL_SUPPORT: ServiceUuid16 = ServiceUuid16(0x1820);
121 pub const INDOOR_POSITIONING: ServiceUuid16 = ServiceUuid16(0x1821);
122 pub const PULSE_OXIMETER: ServiceUuid16 = ServiceUuid16(0x1822);
123 pub const HTTP_PROXY: ServiceUuid16 = ServiceUuid16(0x1823);
124 pub const TRANSPORT_DISCOVERY: ServiceUuid16 = ServiceUuid16(0x1824);
125 pub const OBJECT_TRANSFER: ServiceUuid16 = ServiceUuid16(0x1825);
126 pub const FITNESS_MACHINE: ServiceUuid16 = ServiceUuid16(0x1826);
127 pub const MESH_PROVISIONING: ServiceUuid16 = ServiceUuid16(0x1827);
128 pub const MESH_PROXY: ServiceUuid16 = ServiceUuid16(0x1828);
129 pub const RECONNECTION_CONFIGURATION: ServiceUuid16 = ServiceUuid16(0x1829);
130 pub const INSULIN_DELIVERY: ServiceUuid16 = ServiceUuid16(0x183A);
131 pub const BINARY_SENSOR: ServiceUuid16 = ServiceUuid16(0x183B);
132 pub const EMERGENCY_CONFIGURATION: ServiceUuid16 = ServiceUuid16(0x183C);
133 pub const AUTHORIZATION_CONTROL: ServiceUuid16 = ServiceUuid16(0x183D);
134 pub const PHYSICAL_ACTIVITY_MONITOR: ServiceUuid16 = ServiceUuid16(0x183E);
135 pub const ELAPSED_TIME: ServiceUuid16 = ServiceUuid16(0x183F);
136 pub const GENERIC_HEALTH_SENSOR: ServiceUuid16 = ServiceUuid16(0x1840);
137 pub const AUDIO_INPUT_CONTROL: ServiceUuid16 = ServiceUuid16(0x1843);
138 pub const VOLUME_CONTROL: ServiceUuid16 = ServiceUuid16(0x1844);
139 pub const VOLUME_OFFSET_CONTROL: ServiceUuid16 = ServiceUuid16(0x1845);
140 pub const COORDINATED_SET_IDENTIFICATION: ServiceUuid16 = ServiceUuid16(0x1846);
141 pub const DEVICE_TIME: ServiceUuid16 = ServiceUuid16(0x1847);
142 pub const MEDIA_CONTROL: ServiceUuid16 = ServiceUuid16(0x1848);
143 pub const GENERIC_MEDIA_CONTROL: ServiceUuid16 = ServiceUuid16(0x1849);
144 pub const CONSTANT_TONE_EXTENSION: ServiceUuid16 = ServiceUuid16(0x184A);
145 pub const TELEPHONE_BEARER: ServiceUuid16 = ServiceUuid16(0x184B);
146 pub const GENERIC_TELEPHONE_BEARER: ServiceUuid16 = ServiceUuid16(0x184C);
147 pub const MICROPHONE_CONTROL: ServiceUuid16 = ServiceUuid16(0x184D);
148 pub const AUDIO_STREAM_CONTROL: ServiceUuid16 = ServiceUuid16(0x184E);
149 pub const BROADCAST_AUDIO_SCAN: ServiceUuid16 = ServiceUuid16(0x184F);
150 pub const PUBLISHED_AUDIO_SCAN: ServiceUuid16 = ServiceUuid16(0x1850);
151 pub const BASIC_AUDIO_CAPABILITIES: ServiceUuid16 = ServiceUuid16(0x1851);
152 pub const BROADCAST_AUDIO_ANNOUNCEMENT: ServiceUuid16 = ServiceUuid16(0x1852);
153 pub const COMMON_AUDIO: ServiceUuid16 = ServiceUuid16(0x1853);
154 pub const HEARING_ACCESS: ServiceUuid16 = ServiceUuid16(0x1854);
155 pub const TELEPHONY_AND_MEDIA_AUDIO: ServiceUuid16 = ServiceUuid16(0x1855);
156 pub const PUBLIC_BROADCAST_ANNOUNCEMENT: ServiceUuid16 = ServiceUuid16(0x1856);
157 pub const ELECTRONIC_SHELF_LABEL: ServiceUuid16 = ServiceUuid16(0x1847);
158 pub const GAMING_AUDIO: ServiceUuid16 = ServiceUuid16(0x1858);
159 pub const MESH_PROXY_SOLICITATION: ServiceUuid16 = ServiceUuid16(0x1859);
160
161 pub const fn from_u16(value: u16) -> Self {
162 ServiceUuid16(value)
163 }
164
165 pub const fn to_u16(self) -> u16 {
166 self.0
167 }
168}
169
170impl From<u16> for ServiceUuid16 {
171 fn from(value: u16) -> Self {
172 ServiceUuid16(value)
173 }
174}
175
176impl From<ServiceUuid16> for u16 {
177 fn from(value: ServiceUuid16) -> Self {
178 value.0
179 }
180}
181
182pub struct AdvertisementBuilder<const N: usize> {
183 buf: [u8; N],
184 ptr: usize,
185}
186
187pub struct AdvertisementPayload<const N: usize> {
188 buf: [u8; N],
189 len: usize,
190}
191
192impl<const N: usize> AsRef<[u8]> for AdvertisementPayload<N> {
193 fn as_ref(&self) -> &[u8] {
194 &self.buf[..self.len]
195 }
196}
197
198impl<const N: usize> core::ops::Deref for AdvertisementPayload<N> {
199 type Target = [u8];
200
201 fn deref(&self) -> &Self::Target {
202 &self.buf[..self.len]
203 }
204}
205
206impl<const K: usize> AdvertisementBuilder<K> {
207 pub const fn new() -> Self {
208 Self { buf: [0; K], ptr: 0 }
209 }
210
211 const fn write(mut self, data: &[u8]) -> Self {
212 if self.ptr + data.len() <= K {
213 let mut i = 0;
214 while i < data.len() {
215 self.buf[self.ptr] = data[i];
216 i += 1;
217 self.ptr += 1;
218 }
219 } else {
220 self.ptr += data.len();
222 }
223
224 self
225 }
226
227 pub const fn capacity() -> usize {
228 K
229 }
230
231 pub const fn len(&self) -> usize {
232 self.ptr
233 }
234
235 pub const fn raw(self, ad: AdvertisementDataType, data: &[u8]) -> Self {
239 self.write(&[data.len() as u8 + 1, ad.to_u8()]).write(data)
240 }
241
242 pub const fn try_build(self) -> Result<AdvertisementPayload<K>, Error> {
246 if self.ptr <= K {
247 Ok(AdvertisementPayload {
248 buf: self.buf,
249 len: self.ptr,
250 })
251 } else {
252 Err(Error::Oversize { expected: self.ptr })
253 }
254 }
255
256 pub const fn build(self) -> AdvertisementPayload<K> {
260 core::assert!(self.ptr <= K, "advertisement exceeded buffer length");
262
263 AdvertisementPayload {
264 buf: self.buf,
265 len: self.ptr,
266 }
267 }
268
269 pub const fn flags(self, flags: &[Flag]) -> Self {
271 let mut i = 0;
272 let mut bits = 0;
273 while i < flags.len() {
274 bits |= flags[i] as u8;
275 i += 1;
276 }
277
278 self.raw(AdvertisementDataType::FLAGS, &[bits])
279 }
280
281 pub const fn services_16(self, complete: ServiceList, services: &[ServiceUuid16]) -> Self {
283 let ad_type = match complete {
284 ServiceList::Incomplete => AdvertisementDataType::INCOMPLETE_16_SERVICE_LIST,
285 ServiceList::Complete => AdvertisementDataType::COMPLETE_16_SERVICE_LIST,
286 };
287
288 let mut res = self.write(&[(services.len() * 2) as u8 + 1, ad_type.to_u8()]);
289 let mut i = 0;
290 while i < services.len() {
291 res = res.write(&(services[i].to_u16()).to_le_bytes());
292 i += 1;
293 }
294 res
295 }
296
297 pub const fn services_128(self, complete: ServiceList, services: &[[u8; 16]]) -> Self {
302 let ad_type = match complete {
303 ServiceList::Incomplete => AdvertisementDataType::INCOMPLETE_128_SERVICE_LIST,
304 ServiceList::Complete => AdvertisementDataType::COMPLETE_128_SERVICE_LIST,
305 };
306
307 let mut res = self.write(&[(services.len() * 16) as u8 + 1, ad_type.to_u8()]);
308 let mut i = 0;
309 while i < services.len() {
310 res = res.write(&services[i]);
311 i += 1;
312 }
313 res
314 }
315
316 pub const fn short_name(self, name: &str) -> Self {
318 self.raw(AdvertisementDataType::SHORT_NAME, name.as_bytes())
319 }
320
321 pub const fn full_name(self, name: &str) -> Self {
323 self.raw(AdvertisementDataType::FULL_NAME, name.as_bytes())
324 }
325
326 pub const fn adapt_name(self, name: &str) -> Self {
330 let p = self.ptr;
331 if p + 2 + name.len() <= K {
332 self.full_name(name)
333 } else {
334 let mut res = self.write(&[(K - p) as u8, AdvertisementDataType::SHORT_NAME.to_u8()]);
335 let mut i: usize = 0;
336 let bytes = name.as_bytes();
337 while res.ptr < K {
338 res.buf[res.ptr] = bytes[i];
339 res.ptr += 1;
340 i += 1;
341 }
342 res
343 }
344 }
345}
346
347pub type LegacyAdvertisementBuilder = AdvertisementBuilder<LEGACY_PAYLOAD_LEN>;
348pub type ExtendedAdvertisementBuilder = AdvertisementBuilder<EXTENDED_PAYLOAD_LEN>;
349
350pub type LegacyAdvertisementPayload = AdvertisementPayload<LEGACY_PAYLOAD_LEN>;
351pub type ExtendedAdvertisementPayload = AdvertisementPayload<EXTENDED_PAYLOAD_LEN>;