sen66_interface/configuration/
mod.rs

1//! Data types for configuring the SEN66's operations.
2
3mod temperature;
4mod tuning;
5
6use crate::{
7    error::DataError,
8    util::{check_deserialization, check_range},
9};
10pub use temperature::{TemperatureAcceleration, TemperatureOffset};
11pub use tuning::{NoxTuning, VocTuning};
12
13/// Target CO2 concentration after a forced CO2 recalibration in ppm.
14pub struct TargetCO2Concentration(u16);
15
16impl From<u16> for TargetCO2Concentration {
17    fn from(value: u16) -> Self {
18        TargetCO2Concentration(value)
19    }
20}
21
22impl From<TargetCO2Concentration> for u16 {
23    fn from(value: TargetCO2Concentration) -> Self {
24        value.0
25    }
26}
27
28/// CO2 correction value determined after forced CO2 recalibration (FRC).
29/// Is set to `0xFFFF` if recalibration has failed.
30pub struct Co2Correction(u16);
31
32impl Co2Correction {
33    /// Returns true if recalibration has failed.
34    pub fn is_valid(&self) -> bool {
35        self.0 != 0xFFFF
36    }
37}
38
39impl TryFrom<&[u8]> for Co2Correction {
40    type Error = DataError;
41
42    /// Computes the correction value from the received data. Does not perform the computation if
43    /// `0xFFFF` has been received, indicating a failed FRC.
44    ///
45    /// # Errors
46    ///
47    /// - [`CrcFailed`](crate::error::DataError::CrcFailed): If the received data CRC indicates
48    ///   corruption.
49    /// - [`ReceivedBufferWrongSize`](crate::error::DataError::ReceivedBufferWrongSize): If the
50    ///   received data buffer is not the expected size.
51    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
52        check_deserialization(data, 3)?;
53        let value = u16::from_be_bytes([data[0], data[1]]);
54        let value = if value != 0xFFFF {
55            value - 0x8000
56        } else {
57            value
58        };
59        Ok(Co2Correction(value))
60    }
61}
62
63impl From<Co2Correction> for u16 {
64    fn from(value: Co2Correction) -> Self {
65        value.0
66    }
67}
68
69/// Ambient pressure value used for CO2 measurement compensation in hPa. Must be between 700hPa and
70/// 1,200 hPa. The default value is 1,013 hPa.
71#[derive(Debug, PartialEq)]
72pub struct AmbientPressure(u16);
73
74impl TryFrom<u16> for AmbientPressure {
75    type Error = DataError;
76
77    /// Create an [`AmbientPressure`] value for CO2 compensation. Value ranges are checked
78    ///
79    /// # Errors
80    ///
81    /// - [`ValueOutOfRange`](crate::error::DataError::ValueOutOfRange): If the ambient pressure is
82    ///   not between 700 and 1,200 hPa.
83    fn try_from(value: u16) -> Result<Self, Self::Error> {
84        check_range(value, 700, 1_200, "Ambient Pressure", "hPa")?;
85        Ok(AmbientPressure(value))
86    }
87}
88
89impl TryFrom<&[u8]> for AmbientPressure {
90    type Error = DataError;
91
92    /// Parse the ambient pressure value from the received data.
93    ///
94    /// # Errors
95    ///
96    /// - [`CrcFailed`](crate::error::DataError::CrcFailed): If the received data CRC indicates
97    ///   corruption.
98    /// - [`ReceivedBufferWrongSize`](crate::error::DataError::ReceivedBufferWrongSize): If the
99    ///   received data buffer is not the expected size.
100    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
101        check_deserialization(data, 3)?;
102        Ok(AmbientPressure(u16::from_be_bytes([data[0], data[1]])))
103    }
104}
105
106impl From<AmbientPressure> for u16 {
107    fn from(value: AmbientPressure) -> Self {
108        value.0
109    }
110}
111
112impl Default for AmbientPressure {
113    /// Returns the default ambient pressure of 1,013 hPa.
114    fn default() -> Self {
115        Self(1013)
116    }
117}
118
119/// Sensor altitude for CO2 measurement compensation in m above sea level. Must be between 0 m and
120/// 3,000 m. The default value is 0 m.
121#[derive(Debug, PartialEq)]
122pub struct SensorAltitude(u16);
123
124impl TryFrom<u16> for SensorAltitude {
125    type Error = DataError;
126
127    /// Create an [`SensorAltitude`] value for CO2 compensation. Value ranges are checked.
128    ///
129    /// # Errors
130    ///
131    /// - [`ValueOutOfRange`](crate::error::DataError::ValueOutOfRange): If the sensor altitude is
132    ///   not between 0 and 3,000 m.
133    fn try_from(value: u16) -> Result<Self, Self::Error> {
134        check_range(value, 0, 3_000, "Sensor Altitude", "m")?;
135        Ok(SensorAltitude(value))
136    }
137}
138
139impl TryFrom<&[u8]> for SensorAltitude {
140    type Error = DataError;
141
142    /// Parse the sensor altitude from the received data.
143    ///
144    /// # Errors
145    ///
146    /// - [`CrcFailed`](crate::error::DataError::CrcFailed): If the received data CRC indicates
147    ///   corruption.
148    /// - [`ReceivedBufferWrongSize`](crate::error::DataError::ReceivedBufferWrongSize): If the
149    ///   received data buffer is not the expected size.
150    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
151        check_deserialization(data, 3)?;
152        Ok(SensorAltitude(u16::from_be_bytes([data[0], data[1]])))
153    }
154}
155
156impl From<SensorAltitude> for u16 {
157    fn from(value: SensorAltitude) -> Self {
158        value.0
159    }
160}
161
162impl Default for SensorAltitude {
163    /// Returns the default ambient pressure of 1,013 hPa.
164    fn default() -> Self {
165        Self(0)
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn target_co2_concentration_wraps_raw_value() {
175        let value = 12;
176        assert_eq!(u16::from(TargetCO2Concentration::from(value)), value)
177    }
178}