1use crate::hal::{blocking::delay::DelayUs, digital::v2::OutputPin};
2use crate::{
3 hal, mode, register_access::get_errors, AlgorithmResult, BitFlags, Ccs811, Ccs811AppMode,
4 Ccs811Awake, Error, ErrorAwake, InterruptMode, MeasurementMode, ModeChangeError, Register,
5};
6
7impl<I2C, E> Ccs811AppMode for Ccs811Awake<I2C, mode::App>
8where
9 I2C: hal::blocking::i2c::Write<Error = E> + hal::blocking::i2c::WriteRead<Error = E>,
10{
11 type Error = ErrorAwake<E>;
12 type ModeChangeError = ModeChangeError<ErrorAwake<E>, Self>;
13 type BootModeType = Ccs811Awake<I2C, mode::Boot>;
14
15 fn set_mode(&mut self, mode: MeasurementMode) -> Result<(), Self::Error> {
16 let idle_mode = self.meas_mode_reg & 0b0000_1100;
17 let meas_mode = match mode {
18 MeasurementMode::Idle => idle_mode,
19 MeasurementMode::ConstantPower1s => idle_mode | 1 << 4,
20 MeasurementMode::PulseHeating10s => idle_mode | 2 << 4,
21 MeasurementMode::LowPowerPulseHeating60s => idle_mode | 3 << 4,
22 MeasurementMode::ConstantPower250ms => idle_mode | 4 << 4,
23 };
24 self.write_register_1byte(Register::MEAS_MODE, meas_mode)?;
25 self.meas_mode_reg = meas_mode;
26 Ok(())
27 }
28
29 fn has_data_ready(&mut self) -> Result<bool, Self::Error> {
30 let status = self.read_status()?;
31 Ok((status & BitFlags::DATA_READY) != 0)
32 }
33
34 fn raw_data(&mut self) -> Result<(u8, u16), Self::Error> {
35 let data = self.read_register_2bytes(Register::RAW_DATA)?;
36 Ok(handle_raw_data(data[0], data[1]))
37 }
38
39 fn data(&mut self) -> nb::Result<AlgorithmResult, Self::Error> {
40 let mut data = [0; 8];
41 self.i2c
42 .write_read(self.address, &[Register::ALG_RESULT_DATA], &mut data)
43 .map_err(ErrorAwake::I2C)?;
44 let status = data[4];
45 if (status & BitFlags::ERROR) != 0 {
46 get_errors(data[5]).map_err(ErrorAwake::Device)?;
47 } else if (status & BitFlags::DATA_READY) == 0 {
48 return Err(nb::Error::WouldBlock);
49 }
50 let raw = handle_raw_data(data[6], data[7]);
51 Ok(AlgorithmResult {
52 eco2: (u16::from(data[0]) << 8) | u16::from(data[1]),
53 etvoc: (u16::from(data[2]) << 8) | u16::from(data[3]),
54 raw_current: raw.0,
55 raw_voltage: raw.1,
56 })
57 }
58
59 fn set_environment(
60 &mut self,
61 humidity_percentage: f32,
62 temperature_celsius: f32,
63 ) -> Result<(), Self::Error> {
64 if humidity_percentage < 0.0
65 || humidity_percentage > 100.0
66 || temperature_celsius > 254.998_05
67 {
68 return Err(ErrorAwake::InvalidInputData);
69 }
70 let raw_humidity = get_raw_humidity(humidity_percentage);
71 let raw_temp = get_raw_temperature(temperature_celsius);
72 let raw = [
73 Register::ENV_DATA,
74 raw_humidity.0,
75 raw_humidity.1,
76 raw_temp.0,
77 raw_temp.1,
78 ];
79 self.i2c
80 .write(self.address, &raw)
81 .map_err(ErrorAwake::I2C)?;
82 self.check_status_error()
83 }
84
85 fn baseline(&mut self) -> Result<[u8; 2], Self::Error> {
86 self.read_register_2bytes(Register::BASELINE)
87 }
88
89 fn set_baseline(&mut self, baseline: [u8; 2]) -> Result<(), Self::Error> {
90 self.i2c
91 .write(
92 self.address,
93 &[Register::BASELINE, baseline[0], baseline[1]],
94 )
95 .map_err(ErrorAwake::I2C)?;
96 self.check_status_error()
97 }
98
99 fn set_eco2_thresholds(
100 &mut self,
101 low_to_medium: u16,
102 medium_to_high: u16,
103 ) -> Result<(), Self::Error> {
104 self.i2c
105 .write(
106 self.address,
107 &[
108 Register::THRESHOLDS,
109 (low_to_medium >> 8) as u8,
110 low_to_medium as u8,
111 (medium_to_high >> 8) as u8,
112 medium_to_high as u8,
113 ],
114 )
115 .map_err(ErrorAwake::I2C)?;
116 self.check_status_error()
117 }
118
119 fn set_interrupt_mode(&mut self, mode: InterruptMode) -> Result<(), Self::Error> {
120 let int_mask = match mode {
121 InterruptMode::Disabled => 0,
122 InterruptMode::OnDataReady => BitFlags::INTERRUPT,
123 InterruptMode::OnThresholdCrossed => BitFlags::INTERRUPT | BitFlags::THRESH,
124 };
125 let meas_mode = (self.meas_mode_reg & (0b111 << 4)) | int_mask;
126 self.write_register_1byte(Register::MEAS_MODE, meas_mode)?;
127 self.meas_mode_reg = meas_mode;
128 Ok(())
129 }
130
131 fn software_reset(mut self) -> Result<Self::BootModeType, Self::ModeChangeError> {
133 match self.write_sw_reset() {
134 Err(e) => Err(ModeChangeError::new(self, e)),
135 Ok(_) => Ok(Ccs811Awake::create(self.i2c, self.address)),
136 }
137 }
138}
139
140fn get_raw_humidity(humidity_percentage: f32) -> (u8, u8) {
141 get_raw_environment_data(humidity_percentage)
142}
143
144fn get_raw_temperature(temperature_celsius: f32) -> (u8, u8) {
145 let value = temperature_celsius + 25.0;
146 if value < 0.0 {
147 (0, 0)
148 } else {
149 get_raw_environment_data(value)
150 }
151}
152
153fn get_raw_environment_data(value: f32) -> (u8, u8) {
154 let main = (value as u8) << 1;
155 let rest = value - f32::from(value as u8);
156 let rest = (rest * 512.0) as u16;
157 (main | (((rest & (1 << 8)) >> 8) as u8), rest as u8)
158}
159
160fn handle_raw_data(data0: u8, data1: u8) -> (u8, u16) {
161 (
162 (data1 >> 2) as u8,
163 u16::from(data0) | (u16::from(data1 & 0x3) << 8),
164 )
165}
166
167impl<I2C, CommE, PinE, NWAKE, WAKEDELAY> Ccs811AppMode for Ccs811<I2C, NWAKE, WAKEDELAY, mode::App>
168where
169 I2C: hal::blocking::i2c::Write<Error = CommE> + hal::blocking::i2c::WriteRead<Error = CommE>,
170 NWAKE: OutputPin<Error = PinE>,
171 WAKEDELAY: DelayUs<u8>,
172{
173 type Error = Error<CommE, PinE>;
174 type ModeChangeError = ModeChangeError<Error<CommE, PinE>, Self>;
175 type BootModeType = Ccs811<I2C, NWAKE, WAKEDELAY, mode::Boot>;
176
177 fn set_mode(&mut self, mode: MeasurementMode) -> Result<(), Self::Error> {
178 self.on_awaken(|s| s.dev.set_mode(mode))
179 }
180
181 fn has_data_ready(&mut self) -> Result<bool, Self::Error> {
182 self.on_awaken(|s| s.dev.has_data_ready())
183 }
184
185 fn raw_data(&mut self) -> Result<(u8, u16), Self::Error> {
186 self.on_awaken(|s| s.dev.raw_data())
187 }
188
189 fn data(&mut self) -> nb::Result<AlgorithmResult, Self::Error> {
190 self.on_awaken_nb(|s| s.dev.data())
191 }
192
193 fn baseline(&mut self) -> Result<[u8; 2], Self::Error> {
194 self.on_awaken(|s| s.dev.baseline())
195 }
196
197 fn set_baseline(&mut self, baseline: [u8; 2]) -> Result<(), Self::Error> {
198 self.on_awaken(|s| s.dev.set_baseline(baseline))
199 }
200
201 fn set_environment(
202 &mut self,
203 humidity_percentage: f32,
204 temperature_celsius: f32,
205 ) -> Result<(), Self::Error> {
206 self.on_awaken(|s| {
207 s.dev
208 .set_environment(humidity_percentage, temperature_celsius)
209 })
210 }
211
212 fn set_eco2_thresholds(
213 &mut self,
214 low_to_medium: u16,
215 medium_to_high: u16,
216 ) -> Result<(), Self::Error> {
217 self.on_awaken(|s| s.dev.set_eco2_thresholds(low_to_medium, medium_to_high))
218 }
219
220 fn set_interrupt_mode(&mut self, mode: InterruptMode) -> Result<(), Self::Error> {
221 self.on_awaken(|s| s.dev.set_interrupt_mode(mode))
222 }
223
224 fn software_reset(self) -> Result<Self::BootModeType, Self::ModeChangeError> {
225 self.wrap_mode_change(|s| s.software_reset())
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn convert_humidity() {
235 assert_eq!((0, 0), get_raw_humidity(0.0));
236 assert_eq!((0x64, 0), get_raw_humidity(50.0));
237 assert_eq!((0x61, 0), get_raw_humidity(48.5));
238 assert_eq!((0x60, 0x80), get_raw_humidity(48.25));
239 assert_eq!((0x60, 0x40), get_raw_humidity(48.125));
240 assert_eq!((0x60, 0x20), get_raw_humidity(48.0625));
241 assert_eq!((0x60, 0x10), get_raw_humidity(48.03125));
242 assert_eq!((0x60, 0x08), get_raw_humidity(48.015_625));
243 assert_eq!((0x60, 0x04), get_raw_humidity(48.007_813));
244 assert_eq!((0x60, 0x02), get_raw_humidity(48.003_906));
245 assert_eq!((0x60, 0x01), get_raw_humidity(48.001_953));
246 assert_eq!((0x61, 0xFF), get_raw_humidity(48.998_047));
247 }
248
249 #[test]
250 fn convert_temperature() {
251 assert_eq!((0, 0), get_raw_temperature(-25.5));
252 assert_eq!((0, 0), get_raw_temperature(-25.0));
253 assert_eq!((0x64, 0), get_raw_temperature(25.0));
254 assert_eq!((0x61, 0), get_raw_temperature(23.5));
255 }
256}