1use num_enum::{IntoPrimitive, TryFromPrimitive};
2
3#[derive(IntoPrimitive)]
4#[repr(u8)]
5pub enum Register {
6 ProdId = 0x0,
7 SensorStatus = 0x01,
8 MeasurementRate = 0x02,
9 MeasurementMode = 0x04,
10 Co2Ppm = 0x05,
11 MeasurementStatus = 0x07,
12 InterruptConfig = 0x08,
13 AlarmThreshold = 0x09,
14 PressureReference = 0x0B,
15 CalibrationReference = 0x0D,
16 ScratchPad = 0x0F,
17 SensorReset = 0x10,
18}
19
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[derive(Clone, Copy, PartialEq, Debug)]
22pub struct Status {
23 pub ready: bool,
25 pub pwm_dis: bool,
27 pub temperature_error: bool,
29 pub voltage_error: bool,
31 pub communication_error: bool,
33}
34
35impl From<u8> for Status {
36 fn from(value: u8) -> Self {
37 Self {
38 ready: (value & 0b1000_0000) != 0,
39 pwm_dis: (value & 0b0100_0000) != 0,
40 temperature_error: (value & 0b0010_0000) != 0,
41 voltage_error: (value & 0b0001_0000) != 0,
42 communication_error: (value & 0b0000_1000) != 0,
43 }
44 }
45}
46
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48#[derive(Clone, Copy, PartialEq, Debug)]
49pub struct MeasurementStatus {
50 pub data_ready: bool,
52 pub int_active: bool,
54 pub alarm: bool,
56}
57
58impl From<u8> for MeasurementStatus {
59 fn from(value: u8) -> Self {
60 Self {
61 data_ready: (value & 0b0001_0000) != 0,
62 int_active: (value & 0b0000_1000) != 0,
63 alarm: (value & 0b0000_0100) != 0,
64 }
65 }
66}
67
68#[cfg_attr(feature = "defmt", derive(defmt::Format))]
69#[derive(Clone, Copy)]
70pub struct MeasurementMode {
71 pub pwm_out_enable: bool,
73 pub pwm_mode: PwmMode,
75 pub baseline_offset_comp: BaselineOffsetCompensation,
77 pub operating_mode: OperatingMode,
79}
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81#[derive(Clone, Copy)]
82pub enum PwmMode {
83 SinglePulse = 0,
84 PulseTrain = 1,
85}
86
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88#[derive(Clone, Copy)]
89pub enum BaselineOffsetCompensation {
90 Disabled = 0b00,
91 Enabled = 0b01,
92 Forced = 0b10,
93 _Reserved = 0b11,
94}
95
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97#[derive(Clone, Copy)]
98pub enum OperatingMode {
99 Idle = 0b00,
100 SingleShot = 0b01,
101 Continuous = 0b10,
102 _Reserved = 0b11,
103}
104
105impl Default for MeasurementMode {
106 fn default() -> Self {
107 Self::from(0x24)
108 }
109}
110
111impl From<MeasurementMode> for u8 {
112 fn from(value: MeasurementMode) -> Self {
113 (value.pwm_out_enable as u8) << 5
114 | (value.pwm_mode as u8) << 4
115 | (value.baseline_offset_comp as u8) << 2
116 | value.operating_mode as u8
117 }
118}
119
120impl From<u8> for MeasurementMode {
121 fn from(value: u8) -> Self {
122 Self {
123 pwm_out_enable: (value & 0b0010_0000) != 0,
124 pwm_mode: match (value & 0b0001_0000) >> 4 {
125 0 => PwmMode::SinglePulse,
126 _ => PwmMode::PulseTrain,
127 },
128 baseline_offset_comp: match (value & 0b0000_1100) >> 2 {
129 0b00 => BaselineOffsetCompensation::Disabled,
130 0b01 => BaselineOffsetCompensation::Enabled,
131 0b10 => BaselineOffsetCompensation::Forced,
132 _ => BaselineOffsetCompensation::_Reserved,
133 },
134 operating_mode: match value & 0b0000_0011 {
135 0b00 => OperatingMode::Idle,
136 0b01 => OperatingMode::SingleShot,
137 0b10 => OperatingMode::Continuous,
138 _ => OperatingMode::_Reserved,
139 },
140 }
141 }
142}
143
144#[cfg_attr(feature = "defmt", derive(defmt::Format))]
145#[derive(Clone, Copy, PartialEq, Debug)]
146pub struct InterruptConfig {
147 pub int_pin_active_high: bool,
149 pub int_function_config: IntFunctionConfig,
151 pub alarm_crossing_up: bool,
153}
154
155#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
157#[repr(u8)]
158pub enum IntFunctionConfig {
159 Inactive = 0x0,
161 Alarm = 0x1,
163 DataReady = 0x2,
165 Busy = 0x3,
167 EarlyMeasurementStart = 0x4,
169}
170
171impl Default for InterruptConfig {
172 fn default() -> Self {
173 Self {
174 int_pin_active_high: true,
175 int_function_config: IntFunctionConfig::Inactive,
176 alarm_crossing_up: true,
177 }
178 }
179}
180
181impl From<InterruptConfig> for u8 {
182 fn from(value: InterruptConfig) -> Self {
183 (value.int_pin_active_high as u8) << 4
184 | (value.int_function_config as u8) << 1
185 | value.alarm_crossing_up as u8
186 }
187}
188
189impl TryFrom<u8> for InterruptConfig {
190 type Error = crate::ResponseError;
191 fn try_from(value: u8) -> Result<Self, Self::Error> {
192 Ok(Self {
193 int_pin_active_high: (value & 0b0001_0000) != 0,
194 int_function_config: ((value & 0b0000_1110) >> 1)
195 .try_into()
196 .map_err(|_| Self::Error::InvalidRegisterValue)?,
197 alarm_crossing_up: (value & 0b0000_0001) != 0,
198 })
199 }
200}
201
202#[derive(IntoPrimitive)]
203#[repr(u8)]
204pub enum SoftReset {
206 SoftReset = 0xA3,
208 AbocReset = 0xBC,
210 SaveForceCalibNvm = 0xCF,
212 DisableStepwiseReractiveIirFilter = 0xDF,
214 ResetForcedCalibCorrectionFactor = 0xFC,
216 EnableStepwiseReaciveIirFilter = 0xFE,
218}
219
220#[cfg(test)]
221mod test {
222 use super::*;
223
224 #[test]
225 fn test_status_bitmask() {
226 let status = Status {
227 ready: true,
228 pwm_dis: false,
229 temperature_error: false,
230 voltage_error: true,
231 communication_error: true,
232 };
233
234 let bitmask: u8 = 0b1001_1000;
235
236 assert_eq!(status, Status::from(bitmask))
237 }
238
239 #[test]
240 fn test_measurement_status_bitmask() {
241 let status = MeasurementStatus {
242 data_ready: true,
243 int_active: false,
244 alarm: true,
245 };
246
247 let bitmask: u8 = 0b0001_0100;
248
249 assert_eq!(status, MeasurementStatus::from(bitmask));
250
251 assert_ne!(status, MeasurementStatus::from(bitmask ^ 0b0000_0100));
253 }
254
255 #[test]
256 fn test_measurement_mode_bitmask() {
257 let mode = MeasurementMode {
258 pwm_out_enable: true, pwm_mode: PwmMode::SinglePulse, baseline_offset_comp: BaselineOffsetCompensation::Forced, operating_mode: OperatingMode::Continuous, };
263
264 let bitmask: u8 = mode.into();
265
266 assert_eq!(bitmask, 0b0010_1010)
267 }
268
269 #[test]
270 fn test_interrupt_config_bitmask() {
271 let config = InterruptConfig {
272 int_pin_active_high: true,
273 int_function_config: IntFunctionConfig::DataReady,
274 alarm_crossing_up: true,
275 };
276
277 let bitmask: u8 = config.into();
278
279 assert_eq!(bitmask, 0b0001_0101);
280
281 let config_from = InterruptConfig::try_from(bitmask).unwrap();
282 assert_eq!(config, config_from);
283 }
284}