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}