1use crate::{
2 data_manipulation::{crc24_ble, reverse_bits, whiten},
3 services::BlePayload,
4};
5use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice};
6use rf24::{
7 radio::{
8 prelude::{EsbChannel, EsbPaLevel, EsbRadio},
9 Nrf24Error, RadioConfig, RF24,
10 },
11 CrcLength, PaLevel,
12};
13
14pub const BLE_CHANNEL: [u8; 3] = [2, 26, 80];
16
17pub struct BleChannels;
19
20impl BleChannels {
21 pub fn index_of(channel: u8) -> Option<usize> {
25 for (index, ch) in BLE_CHANNEL.iter().enumerate() {
26 if *ch == channel {
27 return Some(index);
28 }
29 }
30 None
31 }
32
33 pub fn increment(current: u8) -> Option<u8> {
37 if let Some(index) = Self::index_of(current) {
38 if index < (BLE_CHANNEL.len() - 1) {
39 return Some(BLE_CHANNEL[index + 1]);
40 } else {
41 return Some(BLE_CHANNEL[0]);
42 }
43 }
44 None
45 }
46}
47
48const BLE_ADDRESS: [u8; 4] = [0x71, 0x91, 0x7d, 0x6b];
50
51pub fn ble_config() -> RadioConfig {
56 RadioConfig::default()
57 .with_channel(BLE_CHANNEL[0])
58 .with_crc_length(CrcLength::Disabled)
59 .with_auto_ack(0)
60 .with_auto_retries(0, 0)
61 .with_address_length(4)
62 .with_rx_address(1, &BLE_ADDRESS)
63 .with_tx_address(&BLE_ADDRESS)
64}
65
66pub struct FakeBle {
84 pub(crate) name: [u8; 12],
85 pub show_pa_level: bool,
90 pub mac_address: [u8; 6],
95}
96
97impl Default for FakeBle {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103impl FakeBle {
104 const DEVICE_FLAGS: u8 = 0x42;
105 const PROFILE_FLAGS: [u8; 3] = [2, 1, 5];
106
107 pub fn new() -> Self {
112 let mut mac_address = [0u8; 6];
113
114 mac_address.copy_from_slice(b"nRF24L");
116
117 Self {
118 name: [0u8; 12],
119 show_pa_level: false,
120 mac_address,
121 }
122 }
123
124 pub fn set_name(&mut self, name: &str) {
133 let len = name.len().min(10);
134 self.name[2..len + 2].copy_from_slice(&name.as_bytes()[0..len]);
135 self.name[0] = len as u8 + 1;
136 self.name[1] = 0x08;
137 }
138
139 pub fn get_name(&self, name: &mut [u8]) -> u8 {
148 let len = self.name[0];
149 if len > 1 {
150 let len = (len as usize - 1).min(name.len());
151 name[0..len].copy_from_slice(&self.name[2..len + 2]);
152 return len as u8;
153 }
154 0
155 }
156
157 pub fn len_available(&self, hypothetical: &[u8]) -> i8 {
168 let mut result = 18 - hypothetical.len() as i8;
169 let name_len = self.name[0];
170 if name_len > 1 {
171 result -= name_len as i8 + 1;
172 }
173 if self.show_pa_level {
174 result -= 3;
175 }
176 result
177 }
178
179 pub fn hop_channel<SPI, DO, DELAY>(
184 &self,
185 radio: &mut RF24<SPI, DO, DELAY>,
186 ) -> Result<(), Nrf24Error<SPI::Error, DO::Error>>
187 where
188 SPI: SpiDevice,
189 DO: OutputPin,
190 DELAY: DelayNs,
191 {
192 let channel = radio.get_channel()?;
193 if let Some(channel) = BleChannels::increment(channel) {
194 radio.set_channel(channel)?;
195 }
196 Ok(())
198 }
199
200 pub fn make_payload(
207 &self,
208 buf: &[u8],
209 pa_level: Option<PaLevel>,
210 channel: u8,
211 ) -> Option<[u8; 32]> {
212 let mut payload_length = buf.len() + 9;
213
214 let mut tx_queue = [0; 32];
215 tx_queue[0] = Self::DEVICE_FLAGS;
217 tx_queue[2..8].copy_from_slice(&self.mac_address);
222 tx_queue[8..11].copy_from_slice(&Self::PROFILE_FLAGS);
224 let mut offset = 11;
225
226 if let Some(pa_level) = pa_level {
227 let pa_level: i8 = match pa_level {
228 rf24::PaLevel::Min => -18,
229 rf24::PaLevel::Low => -12,
230 rf24::PaLevel::High => -6,
231 rf24::PaLevel::Max => 0,
232 };
233 payload_length += 3;
234 offset += 3;
235 tx_queue[11..offset].copy_from_slice(&[2, 0x0A, pa_level as u8]);
236 }
237
238 if self.name[0] > 1 {
239 let len = self.name[0] as usize + 1;
240 payload_length += len;
241 tx_queue[offset..offset + len].copy_from_slice(&self.name[0..len]);
242 offset += len;
243 }
244
245 if payload_length > BlePayload::MAX_BLE_PAYLOAD_SIZE as usize {
246 return None;
247 }
248
249 tx_queue[1] = payload_length as u8;
250 for byte in buf {
251 tx_queue[offset] = *byte;
252 offset += 1;
253 }
254 let crc = crc24_ble(&tx_queue[0..offset]);
255 tx_queue[offset..offset + 3].copy_from_slice(&crc);
256 offset += 3;
257
258 let coefficient = (BleChannels::index_of(channel).unwrap_or_default() + 37) | 0x40;
259 whiten(&mut tx_queue[0..offset], coefficient as u8);
260
261 reverse_bits(&mut tx_queue[0..offset]);
262 Some(tx_queue)
263 }
264
265 pub fn send<SPI, DO, DELAY>(
284 &self,
285 radio: &mut RF24<SPI, DO, DELAY>,
286 buf: &[u8],
287 ) -> Result<bool, Nrf24Error<SPI::Error, DO::Error>>
288 where
289 SPI: SpiDevice,
290 DO: OutputPin,
291 DELAY: DelayNs,
292 {
293 if let Some(tx_queue) = self.make_payload(
294 buf,
295 if self.show_pa_level {
296 Some(radio.get_pa_level()?)
297 } else {
298 None
299 },
300 radio.get_channel()?,
301 ) {
302 return radio.send(&tx_queue, false);
305 }
306 Ok(false)
307 }
308
309 pub fn read<SPI, DO, DELAY>(
326 &self,
327 radio: &mut RF24<SPI, DO, DELAY>,
328 ) -> Result<Option<BlePayload>, Nrf24Error<SPI::Error, DO::Error>>
329 where
330 SPI: SpiDevice,
331 DO: OutputPin,
332 DELAY: DelayNs,
333 {
334 let mut buf = [0u8; 32];
335 radio.read(&mut buf, Some(32))?;
336 let channel = radio.get_channel()?;
337 Ok(BlePayload::from_bytes(&mut buf, channel))
338 }
339}
340
341#[cfg(test)]
344mod test {
345 extern crate std;
346 use super::{ble_config, FakeBle, BLE_ADDRESS, BLE_CHANNEL};
347 use crate::{spi_test_expects, test::mk_radio};
348 use embedded_hal_mock::eh1::{
349 digital::{State, Transaction as PinTransaction},
350 spi::Transaction as SpiTransaction,
351 };
352 use rf24::{CrcLength, PaLevel};
353 use std::vec;
354
355 #[test]
356 fn name() {
357 let mut ble = FakeBle::default();
358 let mut expected = [0u8; 10];
359 assert_eq!(0, ble.get_name(&mut expected));
360 ble.set_name("nRF24L");
361 assert_eq!(6, ble.get_name(&mut expected));
362 assert!(expected.starts_with(b"nRF24L"));
363 assert_eq!(ble.len_available(b""), 10);
364 }
365
366 #[test]
367 fn mac() {
368 let mut mac = [0u8; 6];
369 mac.copy_from_slice(b"nRF24L");
370 let mut ble = FakeBle::default();
371 ble.mac_address.copy_from_slice(&mac);
372 assert_eq!(ble.len_available(b""), 18);
373 }
374
375 #[test]
376 fn pa_level() {
377 let mut ble = FakeBle::default();
378 assert_eq!(ble.len_available(b""), 18);
379 ble.show_pa_level = true;
380 assert_eq!(ble.len_available(b""), 15);
381 }
382
383 #[test]
384 fn config() {
385 let config = ble_config();
386 assert_eq!(config.channel(), BLE_CHANNEL[0]);
387 assert_eq!(config.crc_length(), CrcLength::Disabled);
388 assert_eq!(config.auto_ack(), 0);
389 assert_eq!(config.auto_retry_count(), 0);
390 assert_eq!(config.auto_retry_delay(), 0);
391 assert_eq!(config.address_length(), 4);
392 let mut address = [0u8; 4];
393 config.tx_address(&mut address);
394 assert_eq!(address, BLE_ADDRESS);
395 config.rx_address(1, &mut address);
396 assert_eq!(address, BLE_ADDRESS);
397 for pipe in 0..5 {
398 let enabled = config.is_rx_pipe_enabled(pipe);
399 assert_eq!(enabled, pipe == 1);
400 }
401 }
402
403 const RF_CH: u8 = 5;
405 const W_REGISTER: u8 = 0x20;
407
408 #[test]
409 fn channel_hop() {
410 let expectations = spi_test_expects![
411 (vec![RF_CH, 0], vec![0xEu8, BLE_CHANNEL[0]]),
412 (vec![RF_CH | W_REGISTER, BLE_CHANNEL[1]], vec![0xEu8, 0]),
413 (vec![RF_CH, 0], vec![0xEu8, BLE_CHANNEL[1]]),
414 (vec![RF_CH | W_REGISTER, BLE_CHANNEL[2]], vec![0xEu8, 0]),
415 (vec![RF_CH, 0], vec![0xEu8, BLE_CHANNEL[2]]),
416 (vec![RF_CH | W_REGISTER, BLE_CHANNEL[0]], vec![0xEu8, 0]),
417 (vec![RF_CH, 0], vec![0xEu8, 0]),
418 ];
419 let mocks = mk_radio(&[], &expectations);
420 let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2);
421 let ble = FakeBle::default();
422 for _ in 0..4 {
423 ble.hop_channel(&mut radio).unwrap();
424 }
425 spi.done();
426 ce_pin.done();
427 }
428
429 const R_RX_PAYLOAD: u8 = 0x61;
430 const STATUS: u8 = 7;
431 const MASK_RX_DR: u8 = 1 << 6;
432
433 #[test]
434 fn read() {
435 let ble = FakeBle::default();
436 let channel = BLE_CHANNEL[0];
437 let payload = ble.make_payload(&[], None, channel).unwrap();
438 let mut buf = [0; 33];
439 buf[1..].copy_from_slice(&payload);
440 buf[0] = 0xE;
441 let mut expected = [0; 33];
442 expected[0] = R_RX_PAYLOAD;
443
444 let spi_expectations = spi_test_expects![
445 (expected.to_vec(), buf.to_vec()),
446 (vec![STATUS | W_REGISTER, MASK_RX_DR], vec![0xEu8, 0]),
447 (vec![RF_CH, 0], vec![0xEu8, BLE_CHANNEL[0]]),
448 ];
449 let mocks = mk_radio(&[], &spi_expectations);
450 let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2);
451
452 let ble_payload = ble.read(&mut radio).unwrap().unwrap();
453 assert_eq!(&ble.mac_address, &ble_payload.mac_address);
454 spi.done();
455 ce_pin.done();
456 }
457
458 const MASK_TX_DS: u8 = 1 << 5;
459 const MASK_MAX_RT: u8 = 1 << 4;
460 const W_TX_PAYLOAD: u8 = 0xA0;
461 const FLUSH_TX: u8 = 0xE1;
462 const NOP: u8 = 0xFF;
463 const RF_SETUP: u8 = 0x06;
464
465 fn send_ce_expects() -> vec::Vec<PinTransaction> {
466 vec![
467 PinTransaction::set(State::Low),
468 PinTransaction::set(State::High),
469 ]
470 }
471
472 fn send_spi_expects(
473 ble: &FakeBle,
474 pa_level: Option<PaLevel>,
475 big_buf: bool,
476 ) -> vec::Vec<SpiTransaction<u8>> {
477 let channel = BLE_CHANNEL[0];
478 let payload = ble.make_payload(&[], pa_level, channel).unwrap();
479 let mut buf = [0; 33];
480 buf[0] = 0xE;
481 let mut expected = [0; 33];
482 expected[0] = W_TX_PAYLOAD;
483 expected[1..].copy_from_slice(&payload);
484
485 let mut expectations = vec![];
486 let bin_pa_level = pa_level.map(|lvl| match lvl {
487 PaLevel::High => 4,
491 _ => 6,
492 });
493 if let Some(lvl) = bin_pa_level {
494 expectations
495 .append(&mut spi_test_expects![(vec![RF_SETUP, 0], vec![0xEu8, lvl]),].to_vec());
496 }
497 expectations.append(
498 &mut spi_test_expects![(
499 vec![RF_CH, bin_pa_level.unwrap_or_default()],
500 vec![0xEu8, BLE_CHANNEL[0]]
501 ),]
502 .to_vec(),
503 );
504 if !big_buf {
505 expectations.append(
506 &mut spi_test_expects![
507 (vec![FLUSH_TX], vec![0xEu8]),
508 (
509 vec![STATUS | W_REGISTER, MASK_TX_DS | MASK_MAX_RT],
510 vec![0xEu8, 0]
511 ),
512 (expected.to_vec(), buf.to_vec()),
513 (vec![NOP], vec![0xE | MASK_TX_DS]),
514 ]
515 .to_vec(),
516 );
517 }
518 expectations
519 }
520
521 #[test]
522 fn send() {
523 let ble = FakeBle::default();
524
525 let spi_expectations = send_spi_expects(&ble, None, false);
526 let ce_expectations = send_ce_expects();
527 let mocks = mk_radio(&ce_expectations, &spi_expectations);
528 let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2);
529
530 assert!(ble.send(&mut radio, &[]).unwrap());
531 spi.done();
532 ce_pin.done();
533 }
534
535 #[test]
536 fn send_pa_level() {
537 let mut ble = FakeBle::new();
538 ble.show_pa_level = true;
539
540 let spi_expectations = send_spi_expects(&ble, Some(PaLevel::Max), false);
541 let ce_expectations = send_ce_expects();
542 let mocks = mk_radio(&ce_expectations, &spi_expectations);
543 let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2);
544
545 assert!(ble.send(&mut radio, &[]).unwrap());
546 spi.done();
547 ce_pin.done();
548 }
549
550 #[test]
551 fn send_big_buf() {
552 let mut ble = FakeBle::new();
553 ble.show_pa_level = true;
554
555 let spi_expectations = send_spi_expects(&ble, Some(PaLevel::High), true);
556 let ce_expectations = [];
557 let mocks = mk_radio(&ce_expectations, &spi_expectations);
558 let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2);
559
560 assert!(!ble.send(&mut radio, &[0u8; 20]).unwrap());
561 spi.done();
562 ce_pin.done();
563 }
564}