1use crate::{
4 data_manipulation::{crc24_ble, reverse_bits, whiten},
5 BleChannels,
6};
7
8#[cfg(feature = "std")]
9extern crate std;
10
11const TEMPERATURE_UUID: u16 = 0x1809;
13const BATTERY_UUID: u16 = 0x180F;
15const EDDYSTONE_UUID: u16 = 0xFEAA;
17
18pub mod prelude {
20 pub(super) trait FromBuffer {
22 fn from_buffer(buf: &[u8]) -> Self;
23 }
24
25 pub trait AsBuffer {
27 fn buffer(&self) -> &[u8];
28 }
29
30 pub trait ServiceData<T> {
32 fn set_data(&mut self, value: T);
33 fn data(&self) -> T;
34 }
35}
36
37#[derive(Debug, Clone, Copy)]
42pub struct TemperatureService {
43 buf: [u8; 8],
44}
45
46impl Default for TemperatureService {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl TemperatureService {
53 pub fn new() -> Self {
55 let mut data = [0u8; 8];
56 data[0] = 7; data[1] = 0x16; data[2..4].copy_from_slice(&TEMPERATURE_UUID.to_le_bytes());
59 data[7] = 0xFE;
60 Self { buf: data }
61 }
62}
63
64impl prelude::ServiceData<f32> for TemperatureService {
65 fn set_data(&mut self, value: f32) {
67 let buf = ((value * 100.0) as u32 & 0xFFFFFF).to_le_bytes();
68 self.buf[4..7].copy_from_slice(&buf[0..3]);
69 }
70
71 fn data(&self) -> f32 {
73 let mut buf = [0u8; 4];
74 buf[0..3].copy_from_slice(&self.buf[4..7]);
75 let value = u32::from_le_bytes(buf);
76 value as f32 / 100.0
77 }
78}
79
80impl prelude::AsBuffer for TemperatureService {
81 fn buffer(&self) -> &[u8] {
83 &self.buf
84 }
85}
86
87impl prelude::FromBuffer for TemperatureService {
88 fn from_buffer(buf: &[u8]) -> Self {
89 let mut self_buf = [0u8; 8];
90 self_buf.copy_from_slice(&buf[0..buf.len().min(8)]);
91 Self { buf: self_buf }
92 }
93}
94
95#[derive(Debug, Clone, Copy)]
100pub struct BatteryService {
101 buf: [u8; 5],
102}
103
104impl Default for BatteryService {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl BatteryService {
111 pub fn new() -> Self {
113 let mut data = [0u8; 5];
114 data[0] = 4; data[1] = 0x16; data[2..4].copy_from_slice(&BATTERY_UUID.to_le_bytes());
117 Self { buf: data }
118 }
119}
120
121impl prelude::ServiceData<u8> for BatteryService {
122 fn set_data(&mut self, value: u8) {
124 self.buf[4] = value;
125 }
126
127 fn data(&self) -> u8 {
129 self.buf[4]
130 }
131}
132
133impl prelude::AsBuffer for BatteryService {
134 fn buffer(&self) -> &[u8] {
136 &self.buf
137 }
138}
139
140impl prelude::FromBuffer for BatteryService {
141 fn from_buffer(buf: &[u8]) -> Self {
142 let mut self_buf = [0u8; 5];
143 self_buf.copy_from_slice(&buf[0..buf.len().min(5)]);
144 Self { buf: self_buf }
145 }
146}
147
148#[derive(Debug, Clone, Copy)]
154pub struct UrlService {
155 buf: [u8; 18],
156}
157
158impl Default for UrlService {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl UrlService {
165 const CODEX_PREFIX: [&str; 4] = ["http://www.", "https://www.", "http://", "https://"];
166 const CODEX_SUFFIX: [&str; 14] = [
167 ".com/", ".org/", ".edu/", ".net/", ".info/", ".biz/", ".gov/", ".com", ".org", ".edu",
168 ".net", ".info", ".biz", ".gov",
169 ];
170
171 pub fn new() -> Self {
173 let mut data = [0u8; 18];
174 data[1] = 0x16; data[2..4].copy_from_slice(&EDDYSTONE_UUID.to_le_bytes());
176 data[4] = 0x10; data[5] = -25i8 as u8;
178 Self { buf: data }
179 }
180
181 pub fn set_pa_level(&mut self, level: i8) {
183 self.buf[5] = level as u8;
184 }
185
186 pub fn pa_level(&self) -> i8 {
188 self.buf[5] as i8
189 }
190
191 pub fn set_data(&mut self, value: &str) {
193 let mut index = 6; let max_len = self.buf.len();
195 let mut pos = 0; let len = value.len();
197 for (j, pre) in Self::CODEX_PREFIX.iter().enumerate() {
198 if value[0..len].starts_with(*pre) {
199 self.buf[index] = j as u8;
200 pos += pre.len();
201 index += 1;
202 break;
203 }
204 }
205 for (i, ch) in value.char_indices() {
206 if index >= max_len {
207 break;
208 }
209 if i < pos {
210 continue;
211 }
212 for (j, post) in Self::CODEX_SUFFIX.iter().enumerate() {
213 if value[i..len].starts_with(*post) {
214 self.buf[index] = j as u8;
215 pos += post.len();
216 index += 1;
217 break;
218 }
219 }
220 if i < pos {
221 continue;
222 }
223 self.buf[index] = ch as u8;
224 index += 1;
225 pos += 1;
226 }
227 self.buf[0] = index as u8 - 1;
228 }
229
230 #[cfg(feature = "std")]
232 pub fn data(&self) -> std::string::String {
233 let mut result = std::string::String::new();
234 let mut index = 0; let max_len = self.buf[0] - 5;
236 for (j, pre) in Self::CODEX_PREFIX.iter().enumerate() {
237 if j as u8 == self.buf[6] {
238 result.push_str(pre);
239 index += 1;
240 break;
241 }
242 }
243 for (i, byte) in self.buf[6..6 + max_len as usize].iter().enumerate() {
244 if index > i {
245 continue;
246 }
247 for (j, post) in Self::CODEX_SUFFIX.iter().enumerate() {
248 if j as u8 == *byte {
249 result.push_str(post);
250 index += 1;
251 break;
252 }
253 }
254 if index > i {
255 continue;
256 }
257 result.push(*byte as char);
258 index += 1;
259 }
260 result
261 }
262}
263
264impl prelude::AsBuffer for UrlService {
265 fn buffer(&self) -> &[u8] {
267 let len = self.buf[0] + 1;
268 &self.buf[0..len as usize]
269 }
270}
271
272impl prelude::FromBuffer for UrlService {
273 fn from_buffer(buf: &[u8]) -> Self {
274 let max_len = buf.len().min(18);
275 let mut self_buf = [0u8; 18];
276 self_buf[0..max_len].copy_from_slice(&buf[0..max_len]);
277 Self { buf: self_buf }
278 }
279}
280
281pub struct BlePayload {
283 pub mac_address: [u8; 6],
284 pub short_name: Option<[u8; 10]>,
285 pub tx_power: Option<i8>,
286 pub battery_charge: Option<BatteryService>,
287 pub url: Option<UrlService>,
288 pub temperature: Option<TemperatureService>,
289}
290
291impl BlePayload {
292 pub(crate) const MAX_BLE_PAYLOAD_SIZE: u8 = 27;
293
294 pub fn from_bytes(buf: &mut [u8], channel: u8) -> Option<Self> {
295 use prelude::FromBuffer;
296
297 reverse_bits(buf);
298 let coefficient = (BleChannels::index_of(channel).unwrap_or_default() as u8 + 37) | 0x40;
299 whiten(buf, coefficient);
300
301 let len = buf[1];
302 if len > Self::MAX_BLE_PAYLOAD_SIZE {
303 return None;
304 }
305 let len = len as usize + 2;
306
307 let mut crc = [0u8; 3];
308 crc.copy_from_slice(&buf[len..len + 3]);
309 let expected = crc24_ble(&buf[0..len]);
310 if crc != expected {
311 return None;
312 }
313
314 let mut mac_address = [0u8; 6];
315 mac_address.copy_from_slice(&buf[2..8]);
316
317 let mut tx_power = None;
318 let mut short_name = None;
319 let mut battery_charge = None;
320 let mut temperature = None;
321 let mut url = None;
322
323 let mut index = 8_usize;
324 while index < len {
325 let chunk_len = (buf[index] - 1) as usize;
326 let chunk_type = buf[index + 1];
327 let start = index + 2;
328 let end = index + chunk_len + 2;
329 match chunk_type {
330 0x08 | 0x09 => {
331 let mut name = [0u8; 10];
332 let name_len = (end - start).min(10);
333 name[0..name_len].copy_from_slice(&buf[start..start + name_len]);
334 short_name = Some(name);
335 }
336 0x0A => {
337 tx_power = Some(buf[start] as i8);
338 }
339 0x16 => {
340 let mut tmp = [0u8; 2];
341 tmp.copy_from_slice(&buf[start..start + 2]);
342 let service_id = u16::from_le_bytes(tmp);
343 match service_id {
344 BATTERY_UUID => {
345 let batt = BatteryService::from_buffer(&buf[index..end]);
346 battery_charge = Some(batt);
347 }
348 TEMPERATURE_UUID => {
349 let temp = TemperatureService::from_buffer(&buf[index..end]);
350 temperature = Some(temp);
351 }
352 EDDYSTONE_UUID => {
353 let eddystone = UrlService::from_buffer(&buf[index..end]);
354 url = Some(eddystone);
355 }
356 _ => {}
357 }
358 }
359 _ => {
360 }
363 }
364 index = end;
365 }
366 Some(Self {
367 mac_address,
368 short_name,
369 tx_power,
370 battery_charge,
371 url,
372 temperature,
373 })
374 }
375}
376
377#[cfg(test)]
378mod test {
379 use rf24::PaLevel;
380
381 use super::{
382 prelude::{AsBuffer, ServiceData},
383 BatteryService, BlePayload, TemperatureService, UrlService,
384 };
385 use crate::data_manipulation::{reverse_bits, whiten};
386 use crate::{BleChannels, FakeBle, BLE_CHANNEL};
387
388 #[test]
389 fn battery_service() {
390 let mut battery = BatteryService::default();
391 battery.set_data(85);
392 assert_eq!(battery.data(), 85);
393 assert_eq!([0x04, 0x16, 0x0F, 0x18, 0x55], *battery.buffer());
394 }
395
396 #[test]
397 fn temperature_service() {
398 let mut temp = TemperatureService::default();
399 temp.set_data(45.5);
400 assert_eq!(temp.data(), 45.5);
401 assert_eq!(
402 [0x07, 0x16, 0x09, 0x18, 0xC6, 0x11, 0x00, 0xFE],
403 *temp.buffer()
404 );
405 }
406
407 #[test]
408 fn url_service() {
409 let mut url = UrlService::default();
410 url.set_data("https://www.foo.com/bar/bazz");
411 url.set_pa_level(-20);
412 assert_eq!(url.pa_level(), -20);
413 assert_eq!(
414 [
415 0x11, 0x16, 0xAA, 0xFE, 0x10, 0xEC, 0x01, 0x66, 0x6F, 0x6F, 0x00, 0x62, 0x61, 0x72,
416 0x2F, 0x62, 0x61, 0x7A
417 ],
418 *(url.buffer())
419 );
420 }
421
422 #[test]
423 fn rx_battery() {
424 let mut service = BatteryService::default();
425 service.set_data(85);
426
427 let mut ble = FakeBle::default();
428 ble.set_name("nRF24L01");
429 let channel = BLE_CHANNEL[0];
430 let mut payload = ble
431 .make_payload(service.buffer(), Some(PaLevel::Low), channel)
432 .unwrap();
433
434 let ble_payload = BlePayload::from_bytes(&mut payload, channel).unwrap();
435 assert_eq!(&ble.mac_address, &ble_payload.mac_address);
436 assert_eq!(&ble_payload.short_name.unwrap(), &ble.name[2..]);
437 assert_eq!(ble_payload.tx_power.unwrap(), -12);
438 assert_eq!(
439 ble_payload.battery_charge.unwrap().buffer(),
440 service.buffer()
441 );
442 }
443
444 #[test]
445 fn rx_temperature() {
446 let mut service = TemperatureService::default();
447 service.set_data(45.5);
448
449 let ble = FakeBle::default();
450 let channel = BLE_CHANNEL[0];
451 let mut payload = ble.make_payload(service.buffer(), None, channel).unwrap();
452
453 let ble_payload = BlePayload::from_bytes(&mut payload, channel).unwrap();
454 assert_eq!(&ble.mac_address, &ble_payload.mac_address);
455 assert_eq!(ble_payload.temperature.unwrap().buffer(), service.buffer());
456 }
457
458 #[test]
459 fn rx_url() {
460 let mut service = UrlService::default();
461 service.set_data("https://www.google.com");
462 let buffer = service.buffer();
463
464 let ble = FakeBle::default();
465 let channel = BLE_CHANNEL[0];
466 let mut payload = ble.make_payload(buffer, None, channel).unwrap();
467
468 let ble_payload = BlePayload::from_bytes(&mut payload, channel).unwrap();
469 assert_eq!(&ble.mac_address, &ble_payload.mac_address);
470 for (i, byte) in ble_payload.url.unwrap().buffer().iter().enumerate() {
471 assert_eq!(buffer[i], *byte);
472 }
473 }
474
475 #[test]
476 fn rx_too_big() {
477 let channel = BLE_CHANNEL[0];
478 let coefficient = (BleChannels::index_of(channel).unwrap() as u8 + 37) | 0x40;
479 let mut payload = [0u8; 32];
480 payload[1] = 29;
481 whiten(&mut payload, coefficient);
482 reverse_bits(&mut payload);
483 assert!(BlePayload::from_bytes(&mut payload, channel).is_none());
484 }
485
486 #[test]
487 fn rx_bad_crc() {
488 let ble = FakeBle::default();
489 let channel = BLE_CHANNEL[0];
490 let coefficient = (BleChannels::index_of(channel).unwrap() as u8 + 37) | 0x40;
491
492 let mut payload = ble.make_payload(&[17u8; 18], None, channel).unwrap();
494 reverse_bits(&mut payload[29..32]);
495 assert!(BlePayload::from_bytes(&mut payload, coefficient).is_none());
496 }
497
498 #[test]
499 fn rx_unsupported_service() {
500 let buffer = [4u8, 0x16, 0xFF, 0x0F, 0xFF];
501
502 let ble = FakeBle::default();
503 let channel = BLE_CHANNEL[0];
504 let mut payload = ble
505 .make_payload(&buffer, Some(PaLevel::Min), channel)
506 .unwrap();
507
508 let ble_payload = BlePayload::from_bytes(
509 &mut payload,
510 (BleChannels::index_of(channel).unwrap() as u8 + 37) | 0x40,
511 )
512 .unwrap();
513 assert_eq!(&ble.mac_address, &ble_payload.mac_address);
514 }
516}