stm32wb_hci/types/scan_window.rs
1//! Types related to the LE scanning window.
2
3use byteorder::{ByteOrder, LittleEndian};
4use core::time::Duration;
5
6/// Define a scanning window.
7///
8/// The controller runs LE scans every [`interval`](ScanWindow::interval), with scanning active
9/// during the [`window`](ScanWindow::window) in every interval.
10///
11/// The minimum time range is 2.5 ms, and the maximum is 10.24 s. The window must be shorter than or
12/// equal to the interval.
13#[derive(Clone, Debug, PartialEq)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub struct ScanWindow {
16 interval_width: Duration,
17 window_width: Duration,
18}
19
20impl ScanWindow {
21 /// Returns the interval for the scanning window. The controller starts an LE scan every
22 /// interval.
23 pub fn interval(&self) -> Duration {
24 self.interval_width
25 }
26
27 /// Returns the amount of time the controller is scanning every interval.
28 pub fn window(&self) -> Duration {
29 self.window_width
30 }
31
32 /// Serializes the window into the given byte buffer.
33 ///
34 /// # Panics
35 ///
36 /// The buffer must be at least 4 bytes long.
37 pub fn copy_into_slice(&self, bytes: &mut [u8]) {
38 assert!(bytes.len() >= 4);
39
40 LittleEndian::write_u16(&mut bytes[0..2], Self::duration_as_u16(self.interval_width));
41 LittleEndian::write_u16(&mut bytes[2..4], Self::duration_as_u16(self.window_width));
42 }
43
44 /// Begins building a [ScanWindow]. The scan window has the given interval. Returns a
45 /// [builder](ScanWindowBuilder) that can be used to set the window duration.
46 ///
47 /// # Errors
48 ///
49 /// - [ScanWindowError::TooShort] if the provided interval is too short. It must be at least 2.5
50 /// ms.
51 /// - [ScanWindowError::TooLong] if the provided interval is too long. It must be 10.24 seconds
52 /// or less.
53 pub fn start_every(interval: Duration) -> Result<ScanWindowBuilder, ScanWindowError> {
54 Ok(ScanWindowBuilder {
55 interval: ScanWindow::validate(interval)?,
56 })
57 }
58
59 fn validate(d: Duration) -> Result<Duration, ScanWindowError> {
60 const MIN: Duration = Duration::from_micros(2500);
61 if d < MIN {
62 return Err(ScanWindowError::TooShort(d));
63 }
64
65 const MAX: Duration = Duration::from_millis(10240);
66 if d > MAX {
67 return Err(ScanWindowError::TooLong(d));
68 }
69
70 Ok(d)
71 }
72
73 fn duration_as_u16(d: Duration) -> u16 {
74 // T = 0.625 ms * N
75 // so N = T / 0.625 ms
76 // = T / 625 us
77 //
78 // Note: 1600 = 1_000_000 / 625
79 (1600 * d.as_secs() as u32 + (d.subsec_micros() / 625)) as u16
80 }
81}
82
83/// Intermediate builder for the [`ScanWindow`].
84#[cfg_attr(feature = "defmt", derive(defmt::Format))]
85pub struct ScanWindowBuilder {
86 interval: Duration,
87}
88
89impl ScanWindowBuilder {
90 /// Completes building a [ScanWindow]. The scan window has the given window.
91 ///
92 /// # Errors
93 ///
94 /// - [ScanWindowError::TooShort] if the provided interval is too short. It must be at least 2.5
95 /// ms.
96 /// - [ScanWindowError::TooLong] if the provided interval is too long. It must be 10.24 seconds
97 /// or less.
98 /// - [ScanWindowError::Inverted] if the window is longer than the interval.
99 pub fn open_for(&self, window: Duration) -> Result<ScanWindow, ScanWindowError> {
100 if window > self.interval {
101 return Err(ScanWindowError::Inverted {
102 interval: self.interval,
103 window,
104 });
105 }
106
107 Ok(ScanWindow {
108 interval_width: self.interval,
109 window_width: ScanWindow::validate(window)?,
110 })
111 }
112}
113
114/// Types of errors that can occure when creating a [`ScanWindow`].
115#[derive(Copy, Clone, Debug, PartialEq)]
116#[cfg_attr(feature = "defmt", derive(defmt::Format))]
117pub enum ScanWindowError {
118 /// The duration is too short. Both the interval and duration must be at least 2.5 ms. Includes
119 /// the invalid duration.
120 TooShort(Duration),
121 /// The duration is too long. Both the interval and duration must be no more than 10.24
122 /// seconds. Includes the invalid duration.
123 TooLong(Duration),
124 /// The interval and window are inverted. That is, the interval is shorter than the window.
125 Inverted {
126 /// The provided interval, which is shorter than the window.
127 interval: Duration,
128 /// The provided window, which is longer than the interval.
129 window: Duration,
130 },
131}