bme280_rs/
configuration.rs

1// Copyright Claudio Mattera 2022-2024.
2//
3// Distributed under the MIT License or the Apache 2.0 License at your option.
4// See the accompanying files License-MIT.txt and License-Apache-2.0.txt, or
5// online at
6// https://opensource.org/licenses/MIT
7// https://opensource.org/licenses/Apache-2.0
8
9//! Data types and functions for BME280 sensor configuration
10
11/// Chip configuration
12#[derive(Clone, Debug, Default, Eq, PartialEq)]
13pub struct Configuration {
14    /// Standby time settings
15    standby_time: StandbyTime,
16
17    /// Filter settings
18    filter: Filter,
19
20    /// SPI3w option
21    spi3w: bool,
22
23    /// Temperature oversampling settings
24    temperature_oversampling: Oversampling,
25
26    /// Pressure oversampling settings
27    pressure_oversampling: Oversampling,
28
29    /// Humidity oversampling settings
30    humidity_oversampling: Oversampling,
31
32    /// Sensor mode
33    sensor_mode: SensorMode,
34}
35
36impl From<&Configuration> for (Config, ControlMeasurement, ControlHumidity) {
37    fn from(configuration: &Configuration) -> Self {
38        let config = (
39            configuration.standby_time,
40            configuration.filter,
41            configuration.spi3w,
42        )
43            .into();
44        let control_measurement = (
45            configuration.temperature_oversampling,
46            configuration.pressure_oversampling,
47            configuration.sensor_mode,
48        )
49            .into();
50        let control_humidity = configuration.humidity_oversampling.into();
51        (config, control_measurement, control_humidity)
52    }
53}
54
55impl Configuration {
56    /// Convert to low-level configuration items
57    #[doc(hidden)]
58    #[must_use]
59    pub(crate) fn to_lowlevel_configuration(
60        &self,
61    ) -> (Config, ControlMeasurement, ControlHumidity) {
62        self.into()
63    }
64
65    /// Set the standby time
66    #[must_use]
67    pub fn with_standby_time(mut self, standby_time: StandbyTime) -> Self {
68        self.standby_time = standby_time;
69        self
70    }
71
72    /// Set the filter
73    #[must_use]
74    pub fn with_filter(mut self, filter: Filter) -> Self {
75        self.filter = filter;
76        self
77    }
78
79    /// Set the SPI3w option
80    #[doc(hidden)]
81    #[allow(unused)]
82    pub(crate) fn with_spi3w(mut self, spi3w: bool) -> Self {
83        self.spi3w = spi3w;
84        self
85    }
86
87    /// Set the oversampling factor for temperature
88    #[must_use]
89    pub fn with_temperature_oversampling(mut self, temperature_oversampling: Oversampling) -> Self {
90        self.temperature_oversampling = temperature_oversampling;
91        self
92    }
93
94    /// Set the oversampling factor for pressure
95    #[must_use]
96    pub fn with_pressure_oversampling(mut self, pressure_oversampling: Oversampling) -> Self {
97        self.pressure_oversampling = pressure_oversampling;
98        self
99    }
100
101    /// Set the oversampling factor for humidity
102    #[must_use]
103    pub fn with_humidity_oversampling(mut self, humidity_oversampling: Oversampling) -> Self {
104        self.humidity_oversampling = humidity_oversampling;
105        self
106    }
107
108    /// Set the sensor mode
109    #[must_use]
110    pub fn with_sensor_mode(mut self, sensor_mode: SensorMode) -> Self {
111        self.sensor_mode = sensor_mode;
112        self
113    }
114
115    /// Check if chip is in forced mode
116    #[doc(hidden)]
117    pub(crate) fn is_forced(&self) -> bool {
118        self.sensor_mode == SensorMode::Forced
119    }
120}
121
122/// Chip status
123#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
124pub struct Status {
125    /// True if the sensor is performing a measurement
126    measuring: bool,
127
128    /// True if the sensor is performing calibration
129    calibrating: bool,
130}
131
132impl Status {
133    /// Return `true` if the chip is measuring data
134    #[must_use]
135    pub fn is_measuring(&self) -> bool {
136        self.measuring
137    }
138
139    /// Return `true` if the chip is computing calibration data
140    #[must_use]
141    pub fn is_calibrating(&self) -> bool {
142        self.calibrating
143    }
144}
145
146impl core::fmt::Display for Status {
147    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148        if self.calibrating && self.measuring {
149            write!(f, "calibrating and measuring")?;
150        } else if self.calibrating {
151            write!(f, "calibrating")?;
152        } else if self.measuring {
153            write!(f, "measuring")?;
154        } else {
155            write!(f, "ready")?;
156        }
157        Ok(())
158    }
159}
160
161impl From<u8> for Status {
162    fn from(arg: u8) -> Self {
163        Self {
164            measuring: (arg & 0b0000_0100) != 0,
165            calibrating: (arg & 0b0000_0001) != 0,
166        }
167    }
168}
169
170/// Oversampling setting
171#[derive(Copy, Clone, Debug, Eq, PartialEq)]
172pub enum Oversampling {
173    /// Skip the measurement altogether
174    Skip,
175
176    /// Take a single sample
177    Oversample1,
178
179    /// Take two samples
180    Oversample2,
181
182    /// Take four samples
183    Oversample4,
184
185    /// Take eight samples
186    Oversample8,
187
188    /// Take sixteen samples
189    Oversample16,
190}
191
192impl Oversampling {
193    /// Convert to binary value
194    #[doc(hidden)]
195    pub(crate) fn to_value(self) -> u8 {
196        match self {
197            Self::Skip => 0b000,
198            Self::Oversample1 => 0b001,
199            Self::Oversample2 => 0b010,
200            Self::Oversample4 => 0b011,
201            Self::Oversample8 => 0b100,
202            Self::Oversample16 => 0b101,
203        }
204    }
205}
206
207impl Default for Oversampling {
208    fn default() -> Self {
209        Self::Skip
210    }
211}
212
213/// Sensor working mode
214#[derive(Copy, Clone, Debug, Eq, PartialEq)]
215pub enum SensorMode {
216    /// Sleep mode
217    ///
218    /// The sensor is not active.
219    Sleep,
220
221    /// Forced mode
222    ///
223    /// The sensor takes a single sample and then enters sleep mode.
224    Forced,
225
226    /// Normal mode
227    ///
228    /// The sensor takes samples at regular times, and returns the latest one
229    /// when queried.
230    /// The time between measurements can be specified as [`StandbyTime`]
231    Normal,
232}
233
234impl SensorMode {
235    /// Convert to binary value
236    #[doc(hidden)]
237    pub(crate) fn to_value(self) -> u8 {
238        match self {
239            Self::Sleep => 0b00,
240            Self::Forced => 0b01,
241            Self::Normal => 0b11,
242        }
243    }
244}
245
246impl Default for SensorMode {
247    fn default() -> Self {
248        Self::Sleep
249    }
250}
251
252/// Standby time between readings in normal mode
253#[derive(Copy, Clone, Debug, Eq, PartialEq)]
254pub enum StandbyTime {
255    /// 0.5 ms
256    Millis0_5,
257
258    /// 10 ms
259    Millis10,
260
261    /// 20 ms
262    Millis20,
263
264    /// 62.5 ms
265    Millis62_5,
266
267    /// 125 ms
268    Millis125,
269
270    /// 250 ms
271    Millis250,
272
273    /// 500 ms
274    Millis500,
275
276    /// 1000 ms
277    Millis1000,
278}
279
280impl StandbyTime {
281    /// Convert to binary value
282    #[doc(hidden)]
283    pub(crate) fn to_value(self) -> u8 {
284        match self {
285            Self::Millis0_5 => 0b000,
286            Self::Millis10 => 0b110,
287            Self::Millis20 => 0b111,
288            Self::Millis62_5 => 0b001,
289            Self::Millis125 => 0b010,
290            Self::Millis250 => 0b011,
291            Self::Millis500 => 0b100,
292            Self::Millis1000 => 0b101,
293        }
294    }
295}
296
297impl Default for StandbyTime {
298    fn default() -> Self {
299        Self::Millis0_5
300    }
301}
302
303/// Filter coefficient
304#[derive(Copy, Clone, Debug, Eq, PartialEq)]
305pub enum Filter {
306    /// No filtering
307    Off,
308
309    /// Filter ×2
310    Filter2,
311
312    /// Filter ×4
313    Filter4,
314
315    /// Filter ×8
316    Filter8,
317
318    /// Filter ×16
319    Filter16,
320}
321
322impl Filter {
323    /// Convert to binary value
324    #[doc(hidden)]
325    pub(crate) fn to_value(self) -> u8 {
326        match self {
327            Self::Off => 0b000,
328            Self::Filter2 => 0b001,
329            Self::Filter4 => 0b010,
330            Self::Filter8 => 0b011,
331            Self::Filter16 => 0b100,
332        }
333    }
334}
335
336impl Default for Filter {
337    fn default() -> Self {
338        Self::Off
339    }
340}
341
342/// Low-level config item
343#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
344pub(crate) struct Config(u8);
345
346impl From<(StandbyTime, Filter, bool)> for Config {
347    fn from((standby_time, filter, spi3w): (StandbyTime, Filter, bool)) -> Self {
348        let standby_time = standby_time.to_value() & 0b111;
349        let filter = filter.to_value() & 0b111;
350        let spi3w = u8::from(spi3w) & 0b1;
351        Self(standby_time << 5 | filter << 2 | spi3w)
352    }
353}
354
355impl From<Config> for u8 {
356    fn from(config: Config) -> Self {
357        config.0
358    }
359}
360
361/// Low-level control humidity item
362#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
363pub(crate) struct ControlHumidity(u8);
364
365impl From<Oversampling> for ControlHumidity {
366    fn from(humidity_oversampling: Oversampling) -> Self {
367        Self(humidity_oversampling.to_value() & 0b111)
368    }
369}
370
371impl From<ControlHumidity> for u8 {
372    fn from(ctrl_hum: ControlHumidity) -> Self {
373        ctrl_hum.0
374    }
375}
376
377/// Low-level control measurement item
378#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
379pub(crate) struct ControlMeasurement(u8);
380
381impl From<(Oversampling, Oversampling, SensorMode)> for ControlMeasurement {
382    fn from(
383        (oversampling_temperature, oversampling_pressure, sensor_mode): (
384            Oversampling,
385            Oversampling,
386            SensorMode,
387        ),
388    ) -> Self {
389        let oversampling_temperature = oversampling_temperature.to_value() & 0b111;
390        let oversampling_pressure = oversampling_pressure.to_value() & 0b111;
391        let sensor_mode = sensor_mode.to_value() & 0b11;
392        Self(oversampling_temperature << 5 | oversampling_pressure << 2 | sensor_mode)
393    }
394}
395
396impl From<ControlMeasurement> for u8 {
397    fn from(ctrl_meas: ControlMeasurement) -> Self {
398        ctrl_meas.0
399    }
400}
401
402#[cfg(test)]
403mod tests {
404    use super::*;
405
406    #[test]
407    fn test_status() {
408        let raw_status = 0b0000_0000;
409        let status: Status = raw_status.into();
410
411        let expected = Status {
412            measuring: false,
413            calibrating: false,
414        };
415
416        assert_eq!(status, expected);
417    }
418
419    #[test]
420    fn test_status_calibrating() {
421        let raw_status = 0b0000_0001;
422        let status: Status = raw_status.into();
423
424        let expected = Status {
425            measuring: false,
426            calibrating: true,
427        };
428
429        assert_eq!(status, expected);
430    }
431
432    #[test]
433    fn test_standby() {
434        let standby = StandbyTime::Millis125;
435
436        let expected = 0b010;
437        let actual = standby.to_value();
438
439        assert_eq!(actual, expected, "0b{actual:03b} == 0b{expected:03b}");
440    }
441
442    #[test]
443    fn test_config() {
444        let configuration = Configuration::default()
445            .with_standby_time(StandbyTime::Millis125)
446            .with_filter(Filter::Filter2)
447            .with_spi3w(true);
448        let (config, _ctrl_meas, _ctrl_hum) = configuration.to_lowlevel_configuration();
449        let actual: u8 = config.into();
450
451        let expected = 0b0100_0101;
452
453        assert_eq!(actual, expected, "0b{actual:08b} == 0b{expected:08b}");
454    }
455
456    #[test]
457    fn test_control_measurement() {
458        let configuration = Configuration::default()
459            .with_temperature_oversampling(Oversampling::Oversample8)
460            .with_pressure_oversampling(Oversampling::Oversample4)
461            .with_sensor_mode(SensorMode::Normal);
462        let (_config, ctrl_meas, _ctrl_hum) = configuration.to_lowlevel_configuration();
463        let actual: u8 = ctrl_meas.into();
464
465        let expected = 0b1000_1111;
466
467        assert_eq!(actual, expected, "0b{actual:08b} == 0b{expected:08b}");
468    }
469
470    #[test]
471    fn test_control_humidity() {
472        let configuration =
473            Configuration::default().with_humidity_oversampling(Oversampling::Oversample8);
474        let (_config, _ctrl_meas, ctrl_hum) = configuration.to_lowlevel_configuration();
475        let actual: u8 = ctrl_hum.into();
476
477        let expected = 0b100;
478
479        assert_eq!(actual, expected, "0b{actual:03b} == 0b{expected:03b}");
480    }
481}