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