Skip to main content

bme280_rs/
configuration.rs

1// Copyright Claudio Mattera 2022-2026.
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, Default, Eq, PartialEq)]
172pub enum Oversampling {
173    /// Skip the measurement altogether
174    #[default]
175    Skip,
176
177    /// Take a single sample
178    Oversample1,
179
180    /// Take two samples
181    Oversample2,
182
183    /// Take four samples
184    Oversample4,
185
186    /// Take eight samples
187    Oversample8,
188
189    /// Take sixteen samples
190    Oversample16,
191}
192
193impl Oversampling {
194    /// Convert to binary value
195    #[doc(hidden)]
196    pub(crate) fn to_value(self) -> u8 {
197        match self {
198            Self::Skip => 0b000,
199            Self::Oversample1 => 0b001,
200            Self::Oversample2 => 0b010,
201            Self::Oversample4 => 0b011,
202            Self::Oversample8 => 0b100,
203            Self::Oversample16 => 0b101,
204        }
205    }
206}
207
208/// Sensor working mode
209#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
210pub enum SensorMode {
211    /// Sleep mode
212    ///
213    /// The sensor is not active.
214    #[default]
215    Sleep,
216
217    /// Forced mode
218    ///
219    /// The sensor takes a single sample and then enters sleep mode.
220    Forced,
221
222    /// Normal mode
223    ///
224    /// The sensor takes samples at regular times, and returns the latest one
225    /// when queried.
226    /// The time between measurements can be specified as [`StandbyTime`]
227    Normal,
228}
229
230impl SensorMode {
231    /// Convert to binary value
232    #[doc(hidden)]
233    pub(crate) fn to_value(self) -> u8 {
234        match self {
235            Self::Sleep => 0b00,
236            Self::Forced => 0b01,
237            Self::Normal => 0b11,
238        }
239    }
240}
241
242/// Standby time between readings in normal mode
243#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
244pub enum StandbyTime {
245    /// 0.5 ms
246    #[default]
247    Millis0_5,
248
249    /// 10 ms
250    Millis10,
251
252    /// 20 ms
253    Millis20,
254
255    /// 62.5 ms
256    Millis62_5,
257
258    /// 125 ms
259    Millis125,
260
261    /// 250 ms
262    Millis250,
263
264    /// 500 ms
265    Millis500,
266
267    /// 1000 ms
268    Millis1000,
269}
270
271impl StandbyTime {
272    /// Convert to binary value
273    #[doc(hidden)]
274    pub(crate) fn to_value(self) -> u8 {
275        match self {
276            Self::Millis0_5 => 0b000,
277            Self::Millis10 => 0b110,
278            Self::Millis20 => 0b111,
279            Self::Millis62_5 => 0b001,
280            Self::Millis125 => 0b010,
281            Self::Millis250 => 0b011,
282            Self::Millis500 => 0b100,
283            Self::Millis1000 => 0b101,
284        }
285    }
286}
287
288/// Filter coefficient
289#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
290pub enum Filter {
291    /// No filtering
292    #[default]
293    Off,
294
295    /// Filter ×2
296    Filter2,
297
298    /// Filter ×4
299    Filter4,
300
301    /// Filter ×8
302    Filter8,
303
304    /// Filter ×16
305    Filter16,
306}
307
308impl Filter {
309    /// Convert to binary value
310    #[doc(hidden)]
311    pub(crate) fn to_value(self) -> u8 {
312        match self {
313            Self::Off => 0b000,
314            Self::Filter2 => 0b001,
315            Self::Filter4 => 0b010,
316            Self::Filter8 => 0b011,
317            Self::Filter16 => 0b100,
318        }
319    }
320}
321
322/// Low-level config item
323#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
324pub(crate) struct Config(u8);
325
326impl From<(StandbyTime, Filter, bool)> for Config {
327    fn from((standby_time, filter, spi3w): (StandbyTime, Filter, bool)) -> Self {
328        let standby_time = standby_time.to_value() & 0b111;
329        let filter = filter.to_value() & 0b111;
330        let spi3w = u8::from(spi3w) & 0b1;
331        Self((standby_time << 5) | (filter << 2) | spi3w)
332    }
333}
334
335impl From<Config> for u8 {
336    fn from(config: Config) -> Self {
337        config.0
338    }
339}
340
341/// Low-level control humidity item
342#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
343pub(crate) struct ControlHumidity(u8);
344
345impl From<Oversampling> for ControlHumidity {
346    fn from(humidity_oversampling: Oversampling) -> Self {
347        Self(humidity_oversampling.to_value() & 0b111)
348    }
349}
350
351impl From<ControlHumidity> for u8 {
352    fn from(ctrl_hum: ControlHumidity) -> Self {
353        ctrl_hum.0
354    }
355}
356
357/// Low-level control measurement item
358#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
359pub(crate) struct ControlMeasurement(u8);
360
361impl From<(Oversampling, Oversampling, SensorMode)> for ControlMeasurement {
362    fn from(
363        (oversampling_temperature, oversampling_pressure, sensor_mode): (
364            Oversampling,
365            Oversampling,
366            SensorMode,
367        ),
368    ) -> Self {
369        let oversampling_temperature = oversampling_temperature.to_value() & 0b111;
370        let oversampling_pressure = oversampling_pressure.to_value() & 0b111;
371        let sensor_mode = sensor_mode.to_value() & 0b11;
372        Self((oversampling_temperature << 5) | (oversampling_pressure << 2) | sensor_mode)
373    }
374}
375
376impl From<ControlMeasurement> for u8 {
377    fn from(ctrl_meas: ControlMeasurement) -> Self {
378        ctrl_meas.0
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385
386    #[test]
387    fn test_status() {
388        let raw_status = 0b0000_0000;
389        let status: Status = raw_status.into();
390
391        let expected = Status {
392            measuring: false,
393            calibrating: false,
394        };
395
396        assert_eq!(status, expected);
397    }
398
399    #[test]
400    fn test_status_calibrating() {
401        let raw_status = 0b0000_0001;
402        let status: Status = raw_status.into();
403
404        let expected = Status {
405            measuring: false,
406            calibrating: true,
407        };
408
409        assert_eq!(status, expected);
410    }
411
412    #[test]
413    fn test_standby() {
414        let standby = StandbyTime::Millis125;
415
416        let expected = 0b010;
417        let actual = standby.to_value();
418
419        assert_eq!(actual, expected, "0b{actual:03b} == 0b{expected:03b}");
420    }
421
422    #[test]
423    fn test_config() {
424        let configuration = Configuration::default()
425            .with_standby_time(StandbyTime::Millis125)
426            .with_filter(Filter::Filter2)
427            .with_spi3w(true);
428        let (config, _ctrl_meas, _ctrl_hum) = configuration.to_lowlevel_configuration();
429        let actual: u8 = config.into();
430
431        let expected = 0b0100_0101;
432
433        assert_eq!(actual, expected, "0b{actual:08b} == 0b{expected:08b}");
434    }
435
436    #[test]
437    fn test_control_measurement() {
438        let configuration = Configuration::default()
439            .with_temperature_oversampling(Oversampling::Oversample8)
440            .with_pressure_oversampling(Oversampling::Oversample4)
441            .with_sensor_mode(SensorMode::Normal);
442        let (_config, ctrl_meas, _ctrl_hum) = configuration.to_lowlevel_configuration();
443        let actual: u8 = ctrl_meas.into();
444
445        let expected = 0b1000_1111;
446
447        assert_eq!(actual, expected, "0b{actual:08b} == 0b{expected:08b}");
448    }
449
450    #[test]
451    fn test_control_humidity() {
452        let configuration =
453            Configuration::default().with_humidity_oversampling(Oversampling::Oversample8);
454        let (_config, _ctrl_meas, ctrl_hum) = configuration.to_lowlevel_configuration();
455        let actual: u8 = ctrl_hum.into();
456
457        let expected = 0b100;
458
459        assert_eq!(actual, expected, "0b{actual:03b} == 0b{expected:03b}");
460    }
461}