sen66_interface/interface.rs
1use duplicate::duplicate_item;
2
3const ADDRESS: u8 = 0x6B;
4const WRITE_FLAG: u8 = 0x00;
5const READ_FLAG: u8 = 0x01;
6
7// `await` replacement needs to be a callable due to the dot notation. This tricks enables that
8// use case.
9#[cfg(not(tarpaulin_include))]
10trait Identity: Sized {
11 fn identity(self) -> Self {
12 core::convert::identity(self)
13 }
14}
15
16impl<T: Sized> Identity for T {}
17
18#[duplicate_item(
19 feature_ module async await delay_trait i2c_trait test_macro;
20 ["async"] [asynch] [async] [await.identity()] [embedded_hal_async::delay::DelayNs] [embedded_hal_async::i2c::I2c<Error = ERR>] [tokio::test];
21 ["blocking"] [blocking] [] [identity()] [embedded_hal::delay::DelayNs] [embedded_hal::i2c::I2c<Error = ERR>] [test];
22)]
23pub mod module {
24 //! Implementation of the SCD30's interface
25 #[cfg(feature=feature_)]
26 mod inner {
27 use crate::{
28 command::Command,
29 configuration::{
30 AmbientPressure, Co2Correction, NoxTuning, SensorAltitude, TargetCO2Concentration,
31 TemperatureAcceleration, TemperatureOffset, VocTuning,
32 },
33 data::{
34 AscState, Concentrations, DataStatus, DeviceStatusRegister, Measurement,
35 ProductName, RawMeasurement, SensorState, SerialNumber, VocAlgorithmState,
36 },
37 error::Sen66Error,
38 interface::{ADDRESS, Identity, READ_FLAG, WRITE_FLAG},
39 util::compute_crc8,
40 };
41
42 /// Interface for the SEN66.
43 pub struct Sen66<DELAY, I2C> {
44 delay: DELAY,
45 i2c: I2C,
46 state: SensorState,
47 }
48
49 impl<DELAY: delay_trait, I2C: i2c_trait, ERR: embedded_hal::i2c::Error> Sen66<DELAY, I2C> {
50 /// Creates a new SEN66 interface.
51 /// - `delay`: Delay provider, implementing embedded_hal's `DelayNs` trait.
52 /// - `i2c`: I2C peripheral implementing embedded_hal's `I2c` trait.
53 pub fn new(delay: DELAY, i2c: I2C) -> Self {
54 Self {
55 delay,
56 i2c,
57 state: SensorState::Idle,
58 }
59 }
60
61 /// Starts a continous measurement. The first result is available after roughly 1.1s
62 /// use [`is_data_ready`](Sen66::is_data_ready) to poll for available measurements.
63 /// Changes sensors state to [`Measuring`](crate::data::SensorState).
64 /// Execution Time: 50ms
65 /// <div class="warning">Only available in idle state</div>
66 ///
67 /// # Errors
68 ///
69 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
70 /// I2C bus occurs.
71 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
72 /// Measuring state.
73 pub async fn start_measurement(&mut self) -> Result<(), Sen66Error<ERR>> {
74 if self.state != SensorState::Idle {
75 return Err(Sen66Error::WrongState("Measuring"));
76 }
77 self.write::<2>(Command::StartContinuousMeasurement, None)
78 .await?;
79 self.state = SensorState::Measuring;
80 Ok(())
81 }
82
83 /// Stops continous measurements.
84 /// Changes sensors state to [`Idle`](crate::data::SensorState).
85 /// Execution Time: 1000ms
86 /// <div class="warning">Only available in measuring state</div>
87 ///
88 /// # Errors
89 ///
90 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
91 /// I2C bus occurs.
92 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
93 /// Idle state.
94 pub async fn stop_measurement(&mut self) -> Result<(), Sen66Error<ERR>> {
95 if self.state != SensorState::Measuring {
96 return Err(Sen66Error::WrongState("Idle"));
97 }
98 self.write::<2>(Command::StopMeasurement, None).await?;
99 self.state = SensorState::Idle;
100 Ok(())
101 }
102
103 /// Queries whether new data is available.
104 /// Execution Time: 20ms
105 /// <div class="warning">Only available in measuring state</div>
106 ///
107 /// # Errors
108 ///
109 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
110 /// I2C bus occurs.
111 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
112 /// Idle state.
113 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
114 /// corrupted or wrong.
115 pub async fn is_data_ready(&mut self) -> Result<DataStatus, Sen66Error<ERR>> {
116 if self.state != SensorState::Measuring {
117 return Err(Sen66Error::WrongState("Idle"));
118 }
119 let received = self.write_read::<2, 3>(Command::GetDataReady, None).await?;
120 Ok(DataStatus::try_from(&received[..])?)
121 }
122
123 /// Read a [`Measurement`](crate::data::Measurement) value from the sensor.
124 /// If new data is available clears the data ready flag. If no new data is available
125 /// the previous data point is returned. If no data at all is available all values are
126 /// set to their maximum value.
127 /// Execution Time: 20ms
128 /// <div class="warning">Only available in measuring state</div>
129 ///
130 /// # Errors
131 ///
132 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
133 /// I2C bus occurs.
134 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
135 /// Idle state.
136 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
137 /// corrupted or wrong.
138 pub async fn read_measured_values(&mut self) -> Result<Measurement, Sen66Error<ERR>> {
139 if self.state != SensorState::Measuring {
140 return Err(Sen66Error::WrongState("Idle"));
141 }
142 let received = self
143 .write_read::<2, 27>(Command::ReadMeasurement, None)
144 .await?;
145 Ok(Measurement::try_from(&received[..])?)
146 }
147
148 /// Read a [`RawMeasurement`](crate::data::RawMeasurement) value from the sensor.
149 /// If new data is available clears the data ready flag. If no new data is available
150 /// the previous data point is returned. If no data at all is available all values are
151 /// set to their maximum value.
152 /// Execution Time: 20ms
153 /// <div class="warning">Only available in measuring state</div>
154 ///
155 /// # Errors
156 ///
157 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
158 /// I2C bus occurs.
159 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
160 /// Idle state.
161 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
162 /// corrupted or wrong.
163 pub async fn read_measured_raw_values(
164 &mut self,
165 ) -> Result<RawMeasurement, Sen66Error<ERR>> {
166 if self.state != SensorState::Measuring {
167 return Err(Sen66Error::WrongState("Idle"));
168 }
169 let received = self
170 .write_read::<2, 15>(Command::ReadRawMeasurement, None)
171 .await?;
172 Ok(RawMeasurement::try_from(&received[..])?)
173 }
174
175 /// Read a [`Concentrations`](crate::data::Concentrations) value from the sensor.
176 /// If new data is available clears the data ready flag. If no new data is available
177 /// the previous data point is returned. If no data at all is available all values are
178 /// set to their maximum value.
179 /// Execution Time: 20ms
180 /// <div class="warning">Only available in measuring state</div>
181 ///
182 /// # Errors
183 ///
184 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
185 /// I2C bus occurs.
186 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
187 /// Idle state.
188 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
189 /// corrupted or wrong.
190 pub async fn read_number_concentrations(
191 &mut self,
192 ) -> Result<Concentrations, Sen66Error<ERR>> {
193 if self.state != SensorState::Measuring {
194 return Err(Sen66Error::WrongState("Idle"));
195 }
196 let received = self
197 .write_read::<2, 15>(Command::ReadNumberConcentrationValues, None)
198 .await?;
199 Ok(Concentrations::try_from(&received[..])?)
200 }
201
202 /// Set the temperature offset parameters.
203 /// - `parameter`: See [`TemperatureOffset`](crate::configuration::TemperatureOffset)
204 /// Execution Time: 20ms
205 ///
206 /// # Errors
207 ///
208 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
209 /// I2C bus occurs.
210 pub async fn set_temperature_offset(
211 &mut self,
212 parameter: TemperatureOffset,
213 ) -> Result<(), Sen66Error<ERR>> {
214 Ok(self
215 .write::<14>(
216 Command::SetTemperatureOffsetParameters,
217 Some(&(<[u16; 4]>::from(parameter))),
218 )
219 .await?)
220 }
221
222 /// Set the temperature acceleration parameters.
223 /// - `parameter`: See [`TemperatureAcceleration`](crate::configuration::TemperatureAcceleration)
224 /// Execution Time: 20ms
225 /// <div class="warning">Only available in Idle state</div>
226 ///
227 /// # Errors
228 ///
229 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
230 /// I2C bus occurs.
231 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
232 /// Measuring state.
233 pub async fn set_temperature_acceleration(
234 &mut self,
235 parameter: TemperatureAcceleration,
236 ) -> Result<(), Sen66Error<ERR>> {
237 if self.state != SensorState::Idle {
238 return Err(Sen66Error::WrongState("Measuring"));
239 }
240 Ok(self
241 .write::<14>(
242 Command::SetTemperatureAccelerationParameters,
243 Some(&(<[u16; 4]>::from(parameter))),
244 )
245 .await?)
246 }
247
248 /// Read out the sensor's product name
249 /// Execution Time: 20ms
250 ///
251 /// # Errors
252 ///
253 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
254 /// I2C bus occurs.
255 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
256 /// corrupted or wrong.
257 pub async fn get_product_name(&mut self) -> Result<ProductName, Sen66Error<ERR>> {
258 let received = self
259 .write_read::<2, 48>(Command::GetProductName, None)
260 .await?;
261 Ok(ProductName::try_from(&received[..])?)
262 }
263
264 /// Read out the sensor's serial number
265 /// Execution Time: 20ms
266 ///
267 /// # Errors
268 ///
269 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
270 /// I2C bus occurs.
271 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
272 /// corrupted or wrong.
273 pub async fn get_serial_number(&mut self) -> Result<SerialNumber, Sen66Error<ERR>> {
274 let received = self
275 .write_read::<2, 48>(Command::GetSerialNumber, None)
276 .await?;
277 Ok(SerialNumber::try_from(&received[..])?)
278 }
279
280 /// Read out the sensor's [`DeviceStatusRegister`](crate::data::DeviceStatusRegister).
281 /// Error flags are untouched by this.
282 /// Execution Time: 20ms
283 ///
284 /// # Errors
285 ///
286 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
287 /// I2C bus occurs.
288 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
289 /// corrupted or wrong.
290 pub async fn read_device_status(
291 &mut self,
292 ) -> Result<DeviceStatusRegister, Sen66Error<ERR>> {
293 let received = self
294 .write_read::<2, 6>(Command::GetDeviceStatus, None)
295 .await?;
296 Ok(DeviceStatusRegister::try_from(&received[..])?)
297 }
298
299 /// Read out the sensor's [`DeviceStatusRegister`](crate::data::DeviceStatusRegister) and
300 /// reset flags stored within.
301 /// Execution Time: 20ms
302 ///
303 /// # Errors
304 ///
305 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
306 /// I2C bus occurs.
307 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
308 /// corrupted or wrong.
309 pub async fn read_and_clear_device_status(
310 &mut self,
311 ) -> Result<DeviceStatusRegister, Sen66Error<ERR>> {
312 let received = self
313 .write_read::<2, 6>(Command::ReadAndClearDeviceStatus, None)
314 .await?;
315 Ok(DeviceStatusRegister::try_from(&received[..])?)
316 }
317
318 /// Reset the sensor, akin to a power cycle.
319 /// Execution Time: 20ms
320 /// <div class="warning">Only available in idle state</div>
321 ///
322 /// # Errors
323 ///
324 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
325 /// I2C bus occurs.
326 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
327 /// Measuring state.
328 pub async fn reset_device(&mut self) -> Result<(), Sen66Error<ERR>> {
329 if self.state != SensorState::Idle {
330 return Err(Sen66Error::WrongState("Measuring"));
331 }
332 self.write::<2>(Command::ResetDevice, None).await
333 }
334
335 /// Start the fan cleaning procedure.
336 /// The fan is set to maximum speed for 10s and then stopped. After issuing this
337 /// command wait at least 10s before starting a measurement.
338 /// Execution Time: 20ms
339 /// <div class="warning">Only available in idle state</div>
340 ///
341 /// # Errors
342 ///
343 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
344 /// I2C bus occurs.
345 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
346 /// Measuring state.
347 pub async fn start_fan_cleaning(&mut self) -> Result<(), Sen66Error<ERR>> {
348 if self.state != SensorState::Idle {
349 return Err(Sen66Error::WrongState("Measuring"));
350 }
351 self.write::<2>(Command::StartFanCleaning, None).await
352 }
353
354 /// Activate the SHT heater.
355 /// The heater runs with 200mW for 1s. Wait at least 20s after the command for the heat
356 /// to disapper, before taking the next measurement.
357 /// Execution Time: 1300ms
358 /// <div class="warning">Only available in idle state</div>
359 ///
360 /// # Errors
361 ///
362 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
363 /// I2C bus occurs.
364 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
365 /// Measuring state.
366 pub async fn activate_sht_heater(&mut self) -> Result<(), Sen66Error<ERR>> {
367 if self.state != SensorState::Idle {
368 return Err(Sen66Error::WrongState("Measuring"));
369 }
370 self.write::<2>(Command::ActivateShtHeater, None).await
371 }
372
373 /// Read the [`VocTuning`](crate::configuration::VocTuning) parameters from the sensor.
374 /// Execution Time: 20ms
375 /// <div class="warning">Only available in idle state</div>
376 ///
377 /// # Errors
378 ///
379 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
380 /// I2C bus occurs.
381 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
382 /// Idle state.
383 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
384 /// corrupted or wrong.
385 pub async fn get_voc_tuning_parameters(
386 &mut self,
387 ) -> Result<VocTuning, Sen66Error<ERR>> {
388 if self.state != SensorState::Idle {
389 return Err(Sen66Error::WrongState("Measuring"));
390 }
391 let received = self
392 .write_read::<2, 18>(Command::SetReadVocTuningParameters, None)
393 .await?;
394 Ok(VocTuning::try_from(&received[..])?)
395 }
396
397 /// Set the [`VocTuning`](crate::configuration::VocTuning) parameters for the sensor.
398 /// Execution Time: 20ms
399 /// <div class="warning">Only available in idle state</div>
400 ///
401 /// # Errors
402 ///
403 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
404 /// I2C bus occurs.
405 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
406 /// Idle state.
407 pub async fn set_voc_tuning_parameters(
408 &mut self,
409 parameter: VocTuning,
410 ) -> Result<(), Sen66Error<ERR>> {
411 if self.state != SensorState::Idle {
412 return Err(Sen66Error::WrongState("Measuring"));
413 }
414 self.write::<20>(
415 Command::SetReadVocTuningParameters,
416 Some(&(<[u16; 6]>::from(parameter))),
417 )
418 .await
419 }
420
421 /// Read the [`VocAlgorithmState`](crate::data::VocAlgorithmState) parameters
422 /// from the sensor.
423 /// The VOC algorithm state is lost after a device reset or power cycle, this enables
424 /// storing it persistently and using
425 /// [`set_voc_algorithm_state`](Sen66::set_voc_algorithm_state) to restore it.
426 /// Can be read every measurement.
427 /// Execution Time: 20ms
428 ///
429 /// # Errors
430 ///
431 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
432 /// I2C bus occurs.
433 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
434 /// corrupted or wrong.
435 pub async fn get_voc_algorithm_state(
436 &mut self,
437 ) -> Result<VocAlgorithmState, Sen66Error<ERR>> {
438 let received = self
439 .write_read::<2, 12>(Command::SetReadVocAlgorithmState, None)
440 .await?;
441 Ok(VocAlgorithmState::try_from(&received[..])?)
442 }
443
444 /// Set the [`VocAlgorithmState`](crate::data::VocAlgorithmState) parameters
445 /// for the sensor.
446 /// Use [`get_voc_algorithm_state`](Sen66::get_voc_algorithm_state) to retrive it.
447 /// Execution Time: 20ms
448 /// <div class="warning">Only available in idle state</div>
449 ///
450 /// # Errors
451 ///
452 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
453 /// I2C bus occurs.
454 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
455 /// Measuring state.
456 pub async fn set_voc_algorithm_state(
457 &mut self,
458 parameter: VocAlgorithmState,
459 ) -> Result<(), Sen66Error<ERR>> {
460 if self.state != SensorState::Idle {
461 return Err(Sen66Error::WrongState("Measuring"));
462 }
463 self.write::<14>(
464 Command::SetReadVocAlgorithmState,
465 Some(&(<[u16; 4]>::from(parameter))),
466 )
467 .await
468 }
469
470 /// Read the [`NoxTuning`](crate::configuration::NoxTuning) parameters from the sensor.
471 /// Execution Time: 20ms
472 /// <div class="warning">Only available in idle state</div>
473 ///
474 /// # Errors
475 ///
476 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
477 /// I2C bus occurs.
478 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
479 /// Idle state.
480 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
481 /// corrupted or wrong.
482 pub async fn get_nox_tuning_parameters(
483 &mut self,
484 ) -> Result<NoxTuning, Sen66Error<ERR>> {
485 if self.state != SensorState::Idle {
486 return Err(Sen66Error::WrongState("Measuring"));
487 }
488 let received = self
489 .write_read::<2, 18>(Command::SetReadNoxTuningParameters, None)
490 .await?;
491 Ok(NoxTuning::try_from(&received[..])?)
492 }
493
494 /// Set the [`NoxTuning`](crate::configuration::NoxTuning) parameters for the sensor.
495 /// Execution Time: 20ms
496 /// <div class="warning">Only available in idle state</div>
497 ///
498 /// # Errors
499 ///
500 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
501 /// I2C bus occurs.
502 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
503 /// Idle state.
504 pub async fn set_nox_tuning_parameters(
505 &mut self,
506 parameter: NoxTuning,
507 ) -> Result<(), Sen66Error<ERR>> {
508 if self.state != SensorState::Idle {
509 return Err(Sen66Error::WrongState("Measuring"));
510 }
511 self.write::<20>(
512 Command::SetReadNoxTuningParameters,
513 Some(&(<[u16; 6]>::from(parameter))),
514 )
515 .await
516 }
517
518 /// Execute the forced recalibration (FRC) for the CO2 sensor.
519 /// Wait at least 1000ms after power-on or 600ms after stopping the measurement before
520 /// issuing this command.
521 /// Execution Time: 500ms
522 /// <div class="warning">Only available in idle state</div>
523 ///
524 /// # Errors
525 ///
526 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
527 /// I2C bus occurs.
528 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
529 /// Idle state.
530 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
531 /// corrupted or wrong.
532 pub async fn perform_forced_co2_recalibration(
533 &mut self,
534 parameter: TargetCO2Concentration,
535 ) -> Result<Co2Correction, Sen66Error<ERR>> {
536 if self.state != SensorState::Idle {
537 return Err(Sen66Error::WrongState("Measuring"));
538 }
539 let received = self
540 .write_read::<5, 3>(
541 Command::ForcedRecalibration,
542 Some(&([u16::from(parameter)])),
543 )
544 .await?;
545 let value = Co2Correction::try_from(&received[..])?;
546 if !value.is_valid() {
547 Err(Sen66Error::FailedCo2Recalibration)
548 } else {
549 Ok(value)
550 }
551 }
552
553 /// Read out whether the automatic self calibration (ASC) for the CO2 sensor is
554 /// enabled or disabled.
555 /// Execution Time: 20ms
556 /// <div class="warning">Only available in idle state</div>
557 ///
558 /// # Errors
559 ///
560 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
561 /// I2C bus occurs.
562 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
563 /// Idle state.
564 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
565 /// corrupted or wrong.
566 pub async fn get_co2_asc_state(&mut self) -> Result<AscState, Sen66Error<ERR>> {
567 if self.state != SensorState::Idle {
568 return Err(Sen66Error::WrongState("Measuring"));
569 }
570 let received = self
571 .write_read::<2, 3>(Command::SetReadCo2AutomaticSelfCalibration, None)
572 .await?;
573 Ok(AscState::try_from(&received[..])?)
574 }
575
576 /// Set whether the automatic self calibration (ASC) for the CO2 sensor is
577 /// enabled or disabled.
578 /// Execution Time: 20ms
579 /// <div class="warning">Only available in idle state</div>
580 ///
581 /// # Errors
582 ///
583 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
584 /// I2C bus occurs.
585 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
586 /// Idle state.
587 pub async fn set_co2_asc_state(
588 &mut self,
589 new_state: AscState,
590 ) -> Result<(), Sen66Error<ERR>> {
591 if self.state != SensorState::Idle {
592 return Err(Sen66Error::WrongState("Measuring"));
593 }
594 self.write::<5>(
595 Command::SetReadCo2AutomaticSelfCalibration,
596 Some(&([u16::from(new_state)])),
597 )
598 .await
599 }
600
601 /// Read the configured ambient pressure for CO2 sensor compensation from the sensor.
602 /// Execution Time: 20ms
603 ///
604 /// # Errors
605 ///
606 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
607 /// I2C bus occurs.
608 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
609 /// corrupted or wrong.
610 pub async fn get_ambient_pressure(
611 &mut self,
612 ) -> Result<AmbientPressure, Sen66Error<ERR>> {
613 let received = self
614 .write_read::<2, 3>(Command::SetReadAmbientPreassure, None)
615 .await?;
616 Ok(AmbientPressure::try_from(&received[..])?)
617 }
618
619 /// Configure the ambient pressure for CO2 sensor compensation for the sensor.
620 /// Execution Time: 20ms
621 ///
622 /// # Errors
623 ///
624 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
625 /// I2C bus occurs.
626 pub async fn set_ambient_pressure(
627 &mut self,
628 parameter: AmbientPressure,
629 ) -> Result<(), Sen66Error<ERR>> {
630 self.write::<5>(
631 Command::SetReadAmbientPreassure,
632 Some(&([u16::from(parameter)])),
633 )
634 .await
635 }
636
637 /// Read the configured sensor altitude for CO2 sensor compensation from the sensor.
638 /// Execution Time: 20ms
639 /// <div class="warning">Only available in idle state</div>
640 ///
641 /// # Errors
642 ///
643 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
644 /// I2C bus occurs.
645 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
646 /// Idle state.
647 /// - [`DataError`](crate::error::Sen66Error::DataError): If the received data is
648 /// corrupted or wrong.
649 pub async fn get_sensor_altitude(&mut self) -> Result<SensorAltitude, Sen66Error<ERR>> {
650 if self.state != SensorState::Idle {
651 return Err(Sen66Error::WrongState("Measuring"));
652 }
653 let received = self
654 .write_read::<2, 3>(Command::SetReadSensorAltitude, None)
655 .await?;
656 Ok(SensorAltitude::try_from(&received[..])?)
657 }
658
659 /// Configure the sensor altitude for CO2 sensor compensation for the sensor.
660 /// Execution Time: 20ms
661 /// <div class="warning">Only available in idle state</div>
662 ///
663 /// # Errors
664 ///
665 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
666 /// I2C bus occurs.
667 /// - [`WrongState`](crate::error::Sen66Error::WrongState): If the command is called in
668 /// Idle state.
669 pub async fn set_sensor_altitude(
670 &mut self,
671 parameter: SensorAltitude,
672 ) -> Result<(), Sen66Error<ERR>> {
673 if self.state != SensorState::Idle {
674 return Err(Sen66Error::WrongState("Measuring"));
675 }
676 self.write::<5>(
677 Command::SetReadSensorAltitude,
678 Some(&([u16::from(parameter)])),
679 )
680 .await
681 }
682
683 /// Closes the sensor interface, stops active measuring if active and returns the
684 /// contained peripherals.
685 ///
686 /// # Errors
687 ///
688 /// - [`I2cError`](crate::error::Sen66Error::I2cError): If an error on the underlying
689 /// I2C bus occurs.
690 pub async fn shutdown(mut self) -> Result<(DELAY, I2C), Sen66Error<ERR>> {
691 if self.state == SensorState::Measuring {
692 self.stop_measurement().await?;
693 }
694 Ok((self.delay, self.i2c))
695 }
696
697 /// Closes the sensor interface, does not change sensor state.
698 pub async fn kill(self) -> (DELAY, I2C) {
699 (self.delay, self.i2c)
700 }
701
702 /// Writes the command and optional data to the sensor, waits for the execution time of
703 /// the command and reads the values returned.
704 async fn write_read<const TX_SIZE: usize, const RX_SIZE: usize>(
705 &mut self,
706 command: Command,
707 data: Option<&[u16]>,
708 ) -> Result<[u8; RX_SIZE], Sen66Error<ERR>> {
709 self.write::<TX_SIZE>(command, data).await?;
710 Ok(self.read().await?)
711 }
712
713 /// Writes the command and optional data to the sensor and waits for the execution time
714 /// of the command.
715 async fn write<const TX_SIZE: usize>(
716 &mut self,
717 command: Command,
718 data: Option<&[u16]>,
719 ) -> Result<(), Sen66Error<ERR>> {
720 let mut sent = [0; TX_SIZE];
721 let command_data = command.to_be_bytes();
722 sent[0] = command_data[0];
723 sent[1] = command_data[1];
724
725 let len = if let Some(data) = data {
726 for (i, datum) in data.iter().enumerate() {
727 let bytes = datum.to_be_bytes();
728 sent[2 + i * 3] = bytes[0];
729 sent[3 + i * 3] = bytes[1];
730 sent[4 + i * 3] = compute_crc8(&bytes);
731 }
732 2 + data.len() * 3
733 } else {
734 2
735 };
736 self.i2c.write(ADDRESS | WRITE_FLAG, &sent[..len]).await?;
737 self.delay.delay_ms(command.execution_time_ms()).await;
738 Ok(())
739 }
740
741 /// Reads data from the I2C bus.
742 async fn read<const RX_SIZE: usize>(
743 &mut self,
744 ) -> Result<[u8; RX_SIZE], Sen66Error<ERR>> {
745 let mut received = [0; RX_SIZE];
746 self.i2c.read(ADDRESS | READ_FLAG, &mut received).await?;
747 Ok(received)
748 }
749 }
750
751 #[cfg(test)]
752 mod tests {
753 use super::*;
754 use embedded_hal_mock::eh1::{
755 delay::NoopDelay,
756 i2c::{Mock as I2cMock, Transaction as I2cTransaction},
757 };
758
759 #[test_macro]
760 async fn start_measurements_works() {
761 let expected_transaction = [I2cTransaction::write(0x6B | 0x00, vec![0x00, 0x21])];
762 let i2c = I2cMock::new(&expected_transaction);
763 let delay = NoopDelay::new();
764 let mut sensor = Sen66::new(delay, i2c);
765
766 sensor.start_measurement().await.unwrap();
767 sensor.kill().await.1.done();
768 }
769
770 #[test_macro]
771 async fn stop_measurement_in_idle_yields_error() {
772 let expected_transaction = [];
773 let i2c = I2cMock::new(&expected_transaction);
774 let delay = NoopDelay::new();
775 let mut sensor = Sen66::new(delay, i2c);
776
777 assert!(sensor.stop_measurement().await.is_err());
778 sensor.kill().await.1.done();
779 }
780
781 #[test_macro]
782 async fn stop_measurement_works() {
783 let expected_transaction = [I2cTransaction::write(0x6B | 0x00, vec![0x01, 0x04])];
784 let i2c = I2cMock::new(&expected_transaction);
785 let delay = NoopDelay::new();
786 let mut sensor = Sen66::new(delay, i2c);
787 sensor.state = SensorState::Measuring;
788
789 sensor.stop_measurement().await.unwrap();
790 sensor.kill().await.1.done();
791 }
792
793 #[test_macro]
794 async fn if_data_ready_is_data_ready_yields_ready() {
795 let expected_transaction = [
796 I2cTransaction::write(0x6B | 0x00, vec![0x02, 0x02]),
797 I2cTransaction::read(0x6B | 0x01, vec![0x00, 0x01, 0xB0]),
798 ];
799 let i2c = I2cMock::new(&expected_transaction);
800 let delay = NoopDelay::new();
801 let mut sensor = Sen66::new(delay, i2c);
802 sensor.state = SensorState::Measuring;
803
804 assert_eq!(sensor.is_data_ready().await.unwrap(), DataStatus::Ready);
805 sensor.kill().await.1.done();
806 }
807
808 #[test_macro]
809 async fn if_data_not_ready_is_data_ready_yields_not_ready() {
810 let expected_transaction = [
811 I2cTransaction::write(0x6B | 0x00, vec![0x02, 0x02]),
812 I2cTransaction::read(0x6B | 0x01, vec![0x00, 0x00, 0x81]),
813 ];
814 let i2c = I2cMock::new(&expected_transaction);
815 let delay = NoopDelay::new();
816 let mut sensor = Sen66::new(delay, i2c);
817 sensor.state = SensorState::Measuring;
818
819 assert_eq!(sensor.is_data_ready().await.unwrap(), DataStatus::NotReady);
820 sensor.kill().await.1.done();
821 }
822
823 #[test_macro]
824 async fn read_measured_values_works() {
825 let expected_transaction = [
826 I2cTransaction::write(0x6B | 0x00, vec![0x03, 0x00]),
827 I2cTransaction::read(
828 0x6B | 0x01,
829 vec![
830 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A,
831 0x00, 0x64, 0xFE, 0x00, 0xC8, 0x7F, 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A,
832 0x00, 0x01, 0xB0,
833 ],
834 ),
835 ];
836 let i2c = I2cMock::new(&expected_transaction);
837 let delay = NoopDelay::new();
838 let mut sensor = Sen66::new(delay, i2c);
839 sensor.state = SensorState::Measuring;
840
841 assert_eq!(
842 sensor.read_measured_values().await.unwrap(),
843 Measurement {
844 pm1_0: 1.0,
845 pm2_5: 1.0,
846 pm4_0: 1.0,
847 pm10_0: 1.0,
848 relative_humidity: 1.0,
849 temperature: 1.0,
850 voc_index: 1.0,
851 nox_index: 1.0,
852 co2: 1,
853 }
854 );
855 sensor.kill().await.1.done();
856 }
857
858 #[test_macro]
859 async fn read_measured_raw_values_works() {
860 let expected_transaction = [
861 I2cTransaction::write(0x6B | 0x00, vec![0x04, 0x05]),
862 I2cTransaction::read(
863 0x6B | 0x01,
864 vec![
865 0x00, 0x64, 0xFe, 0x00, 0xC8, 0x7F, 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A,
866 0x00, 0x01, 0xB0,
867 ],
868 ),
869 ];
870 let i2c = I2cMock::new(&expected_transaction);
871 let delay = NoopDelay::new();
872 let mut sensor = Sen66::new(delay, i2c);
873 sensor.state = SensorState::Measuring;
874
875 assert_eq!(
876 sensor.read_measured_raw_values().await.unwrap(),
877 RawMeasurement {
878 relative_humidity: 1.0,
879 temperature: 1.0,
880 voc: 10,
881 nox: 10,
882 co2: 1,
883 }
884 );
885 sensor.kill().await.1.done();
886 }
887
888 #[test_macro]
889 async fn read_number_concentrations_works() {
890 let expected_transaction = [
891 I2cTransaction::write(0x6B | 0x00, vec![0x03, 0x16]),
892 I2cTransaction::read(
893 0x6B | 0x01,
894 vec![
895 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A, 0x00, 0x0A, 0x5A,
896 0x00, 0x0A, 0x5A,
897 ],
898 ),
899 ];
900 let i2c = I2cMock::new(&expected_transaction);
901 let delay = NoopDelay::new();
902 let mut sensor = Sen66::new(delay, i2c);
903 sensor.state = SensorState::Measuring;
904
905 assert_eq!(
906 sensor.read_number_concentrations().await.unwrap(),
907 Concentrations {
908 pm0_5: 1.0,
909 pm1_0: 1.0,
910 pm2_5: 1.0,
911 pm4_0: 1.0,
912 pm10_0: 1.0,
913 },
914 );
915 sensor.kill().await.1.done();
916 }
917
918 #[test_macro]
919 async fn set_temperature_offset_works() {
920 let expected_transaction = [I2cTransaction::write(
921 0x6B | 0x00,
922 vec![
923 0x60, 0xB2, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00,
924 0x00, 0x81,
925 ],
926 )];
927 let i2c = I2cMock::new(&expected_transaction);
928 let delay = NoopDelay::new();
929 let mut sensor = Sen66::new(delay, i2c);
930
931 let offset = TemperatureOffset::new(0, 0, 0, 0).unwrap();
932 sensor.set_temperature_offset(offset).await.unwrap();
933 sensor.kill().await.1.done();
934 }
935
936 #[test_macro]
937 async fn set_temperature_acceleration_works() {
938 let expected_transaction = [I2cTransaction::write(
939 0x6B | 0x00,
940 vec![
941 0x61, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00,
942 0x00, 0x81,
943 ],
944 )];
945 let i2c = I2cMock::new(&expected_transaction);
946 let delay = NoopDelay::new();
947 let mut sensor = Sen66::new(delay, i2c);
948
949 let acceleration = TemperatureAcceleration::new(0, 0, 0, 0).unwrap();
950 sensor
951 .set_temperature_acceleration(acceleration)
952 .await
953 .unwrap();
954 sensor.kill().await.1.done();
955 }
956
957 #[test_macro]
958 async fn get_product_name_works() {
959 let expected_transaction = [
960 I2cTransaction::write(0x6B | 0x00, vec![0xD0, 0x14]),
961 I2cTransaction::read(
962 0x6B | 0x01,
963 vec![
964 'S' as u8, 'E' as u8, 0x83, 'N' as u8, '6' as u8, 0x06, '6' as u8,
965 '\0' as u8, 0x69, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
966 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
967 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
968 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
969 ],
970 ),
971 ];
972 let i2c = I2cMock::new(&expected_transaction);
973 let delay = NoopDelay::new();
974 let mut sensor = Sen66::new(delay, i2c);
975 sensor.state = SensorState::Measuring;
976
977 assert_eq!(
978 sensor.get_product_name().await.unwrap().get_name_buffer(),
979 [
980 'S' as u8, 'E' as u8, 'N' as u8, '6' as u8, '6' as u8, '\0' as u8
981 ]
982 );
983 sensor.kill().await.1.done();
984 }
985
986 #[test_macro]
987 async fn get_serial_number_works() {
988 let expected_transaction = [
989 I2cTransaction::write(0x6B | 0x00, vec![0xD0, 0x33]),
990 I2cTransaction::read(
991 0x6B | 0x01,
992 vec![
993 'S' as u8, 'E' as u8, 0x83, 'N' as u8, '6' as u8, 0x06, '6' as u8,
994 '\0' as u8, 0x69, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
995 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
996 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
997 0x00, 0x00, 0x81, 0x00, 0x00, 0x81,
998 ],
999 ),
1000 ];
1001 let i2c = I2cMock::new(&expected_transaction);
1002 let delay = NoopDelay::new();
1003 let mut sensor = Sen66::new(delay, i2c);
1004 sensor.state = SensorState::Measuring;
1005
1006 assert_eq!(
1007 sensor
1008 .get_serial_number()
1009 .await
1010 .unwrap()
1011 .get_serial_buffer(),
1012 [
1013 'S' as u8, 'E' as u8, 'N' as u8, '6' as u8, '6' as u8, '\0' as u8
1014 ]
1015 );
1016 sensor.kill().await.1.done();
1017 }
1018
1019 #[test_macro]
1020 async fn read_device_status_works() {
1021 let expected_transaction = [
1022 I2cTransaction::write(0x6B | 0x00, vec![0xD2, 0x06]),
1023 I2cTransaction::read(0x6B | 0x01, vec![0x00, 0x00, 0x81, 0x00, 0x00, 0x81]),
1024 ];
1025 let i2c = I2cMock::new(&expected_transaction);
1026 let delay = NoopDelay::new();
1027 let mut sensor = Sen66::new(delay, i2c);
1028 sensor.state = SensorState::Measuring;
1029
1030 assert!(
1031 sensor
1032 .read_device_status()
1033 .await
1034 .unwrap()
1035 .has_error()
1036 .is_ok()
1037 );
1038 sensor.kill().await.1.done();
1039 }
1040
1041 #[test_macro]
1042 async fn read_and_clear_device_status_works() {
1043 let expected_transaction = [
1044 I2cTransaction::write(0x6B | 0x00, vec![0xD2, 0x10]),
1045 I2cTransaction::read(0x6B | 0x01, vec![0x00, 0x00, 0x81, 0x00, 0x00, 0x81]),
1046 ];
1047 let i2c = I2cMock::new(&expected_transaction);
1048 let delay = NoopDelay::new();
1049 let mut sensor = Sen66::new(delay, i2c);
1050 sensor.state = SensorState::Measuring;
1051
1052 assert!(
1053 sensor
1054 .read_and_clear_device_status()
1055 .await
1056 .unwrap()
1057 .has_error()
1058 .is_ok()
1059 );
1060 sensor.kill().await.1.done();
1061 }
1062
1063 #[test_macro]
1064 async fn reset_device_works() {
1065 let expected_transaction = [I2cTransaction::write(0x6B | 0x00, vec![0xD3, 0x04])];
1066 let i2c = I2cMock::new(&expected_transaction);
1067 let delay = NoopDelay::new();
1068 let mut sensor = Sen66::new(delay, i2c);
1069
1070 sensor.reset_device().await.unwrap();
1071 sensor.kill().await.1.done();
1072 }
1073
1074 #[test_macro]
1075 async fn start_fan_cleaning_works() {
1076 let expected_transaction = [I2cTransaction::write(0x6B | 0x00, vec![0x56, 0x07])];
1077 let i2c = I2cMock::new(&expected_transaction);
1078 let delay = NoopDelay::new();
1079 let mut sensor = Sen66::new(delay, i2c);
1080
1081 sensor.start_fan_cleaning().await.unwrap();
1082 sensor.kill().await.1.done();
1083 }
1084
1085 #[test_macro]
1086 async fn activate_sht_heater_works() {
1087 let expected_transaction = [I2cTransaction::write(0x6B | 0x00, vec![0x37, 0x30])];
1088 let i2c = I2cMock::new(&expected_transaction);
1089 let delay = NoopDelay::new();
1090 let mut sensor = Sen66::new(delay, i2c);
1091
1092 sensor.activate_sht_heater().await.unwrap();
1093 sensor.kill().await.1.done();
1094 }
1095
1096 #[test_macro]
1097 async fn get_voc_tuning_parameters_works() {
1098 let expected_transaction = [
1099 I2cTransaction::write(0x6B | 0x00, vec![0x60, 0xD0]),
1100 I2cTransaction::read(
1101 0x6B | 0x01,
1102 vec![
1103 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x81,
1104 0x00, 0x0A, 0x5A, 0x00, 0x01, 0xB0,
1105 ],
1106 ),
1107 ];
1108 let i2c = I2cMock::new(&expected_transaction);
1109 let delay = NoopDelay::new();
1110 let mut sensor = Sen66::new(delay, i2c);
1111 sensor.state = SensorState::Idle;
1112
1113 assert_eq!(
1114 sensor.get_voc_tuning_parameters().await.unwrap(),
1115 VocTuning::new(1, 1, 1, 0, 10, 1).unwrap()
1116 );
1117 sensor.kill().await.1.done();
1118 }
1119
1120 #[test_macro]
1121 async fn set_voc_tuning_parameters_works() {
1122 let expected_transaction = [I2cTransaction::write(
1123 0x6B | 0x00,
1124 vec![
1125 0x60, 0xD0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00,
1126 0x00, 0x81, 0x00, 0x0A, 0x5A, 0x00, 0x01, 0xB0,
1127 ],
1128 )];
1129 let i2c = I2cMock::new(&expected_transaction);
1130 let delay = NoopDelay::new();
1131 let mut sensor = Sen66::new(delay, i2c);
1132 sensor.state = SensorState::Idle;
1133
1134 sensor
1135 .set_voc_tuning_parameters(VocTuning::new(1, 1, 1, 0, 10, 1).unwrap())
1136 .await
1137 .unwrap();
1138 sensor.kill().await.1.done();
1139 }
1140
1141 #[test_macro]
1142 async fn get_voc_algorithm_state_works() {
1143 let expected_transaction = [
1144 I2cTransaction::write(0x6B | 0x00, vec![0x61, 0x81]),
1145 I2cTransaction::read(
1146 0x6B | 0x01,
1147 vec![
1148 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0,
1149 ],
1150 ),
1151 ];
1152 let i2c = I2cMock::new(&expected_transaction);
1153 let delay = NoopDelay::new();
1154 let mut sensor = Sen66::new(delay, i2c);
1155 sensor.state = SensorState::Idle;
1156
1157 assert_eq!(
1158 <[u16; 4]>::from(sensor.get_voc_algorithm_state().await.unwrap()),
1159 [0x0001, 0x0001, 0x0001, 0x0001]
1160 );
1161 sensor.kill().await.1.done();
1162 }
1163
1164 #[test_macro]
1165 async fn set_voc_algorithm_state_works() {
1166 let expected_transaction = [I2cTransaction::write(
1167 0x6B | 0x00,
1168 vec![
1169 0x61, 0x81, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00,
1170 0x01, 0xB0,
1171 ],
1172 )];
1173 let i2c = I2cMock::new(&expected_transaction);
1174 let delay = NoopDelay::new();
1175 let mut sensor = Sen66::new(delay, i2c);
1176 sensor.state = SensorState::Idle;
1177
1178 let state = VocAlgorithmState::try_from(
1179 &(vec![
1180 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0,
1181 ])[..],
1182 )
1183 .unwrap();
1184 sensor.set_voc_algorithm_state(state).await.unwrap();
1185 sensor.kill().await.1.done();
1186 }
1187
1188 #[test_macro]
1189 async fn get_nox_tuning_parameters_works() {
1190 let expected_transaction = [
1191 I2cTransaction::write(0x6B | 0x00, vec![0x60, 0xE1]),
1192 I2cTransaction::read(
1193 0x6B | 0x01,
1194 vec![
1195 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x81,
1196 0x00, 0x32, 0x26, 0x00, 0x01, 0xB0,
1197 ],
1198 ),
1199 ];
1200 let i2c = I2cMock::new(&expected_transaction);
1201 let delay = NoopDelay::new();
1202 let mut sensor = Sen66::new(delay, i2c);
1203 sensor.state = SensorState::Idle;
1204
1205 assert_eq!(
1206 sensor.get_nox_tuning_parameters().await.unwrap(),
1207 NoxTuning::new(1, 1, 1, 0, 1).unwrap()
1208 );
1209 sensor.kill().await.1.done();
1210 }
1211
1212 #[test_macro]
1213 async fn set_nox_tuning_parameters_works() {
1214 let expected_transaction = [I2cTransaction::write(
1215 0x6B | 0x00,
1216 vec![
1217 0x60, 0xE1, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00,
1218 0x00, 0x81, 0x00, 0x32, 0x26, 0x00, 0x01, 0xB0,
1219 ],
1220 )];
1221 let i2c = I2cMock::new(&expected_transaction);
1222 let delay = NoopDelay::new();
1223 let mut sensor = Sen66::new(delay, i2c);
1224 sensor.state = SensorState::Idle;
1225
1226 sensor
1227 .set_nox_tuning_parameters(NoxTuning::new(1, 1, 1, 0, 1).unwrap())
1228 .await
1229 .unwrap();
1230 sensor.kill().await.1.done();
1231 }
1232
1233 #[test_macro]
1234 async fn perform_forced_co2_recalibration_works() {
1235 let expected_transaction = [
1236 I2cTransaction::write(0x6B | 0x00, vec![0x67, 0x07, 0x03, 0xE8, 0xD4]),
1237 I2cTransaction::read(0x6B | 0x01, vec![0x83, 0xE8, 0xF7]),
1238 ];
1239 let i2c = I2cMock::new(&expected_transaction);
1240 let delay = NoopDelay::new();
1241 let mut sensor = Sen66::new(delay, i2c);
1242 sensor.state = SensorState::Idle;
1243 assert_eq!(
1244 u16::from(
1245 sensor
1246 .perform_forced_co2_recalibration(TargetCO2Concentration::from(1000))
1247 .await
1248 .unwrap()
1249 ),
1250 1000
1251 );
1252 sensor.kill().await.1.done();
1253 }
1254
1255 #[test_macro]
1256 async fn get_co2_asc_state_is_enabled_yields_enabled() {
1257 let expected_transaction = [
1258 I2cTransaction::write(0x6B | 0x00, vec![0x67, 0x11]),
1259 I2cTransaction::read(0x6B | 0x01, vec![0x00, 0x01, 0xb0]),
1260 ];
1261 let i2c = I2cMock::new(&expected_transaction);
1262 let delay = NoopDelay::new();
1263 let mut sensor = Sen66::new(delay, i2c);
1264 sensor.state = SensorState::Idle;
1265 assert_eq!(sensor.get_co2_asc_state().await.unwrap(), AscState::Enabled);
1266 sensor.kill().await.1.done();
1267 }
1268
1269 #[test_macro]
1270 async fn get_co2_asc_state_is_disabled_yields_disabled() {
1271 let expected_transaction = [
1272 I2cTransaction::write(0x6B | 0x00, vec![0x67, 0x11]),
1273 I2cTransaction::read(0x6B | 0x01, vec![0x00, 0x00, 0x81]),
1274 ];
1275 let i2c = I2cMock::new(&expected_transaction);
1276 let delay = NoopDelay::new();
1277 let mut sensor = Sen66::new(delay, i2c);
1278 sensor.state = SensorState::Idle;
1279 assert_eq!(
1280 sensor.get_co2_asc_state().await.unwrap(),
1281 AscState::Disabled
1282 );
1283 sensor.kill().await.1.done();
1284 }
1285
1286 #[test_macro]
1287 async fn set_co2_asc_state_works() {
1288 let expected_transaction = [I2cTransaction::write(
1289 0x6B | 0x00,
1290 vec![0x67, 0x11, 0x00, 0x01, 0xB0],
1291 )];
1292 let i2c = I2cMock::new(&expected_transaction);
1293 let delay = NoopDelay::new();
1294 let mut sensor = Sen66::new(delay, i2c);
1295 sensor.state = SensorState::Idle;
1296 sensor.set_co2_asc_state(AscState::Enabled).await.unwrap();
1297 sensor.kill().await.1.done();
1298 }
1299
1300 #[test_macro]
1301 async fn get_ambient_pressure_works() {
1302 let expected_transaction = [
1303 I2cTransaction::write(0x6B | 0x00, vec![0x67, 0x20]),
1304 I2cTransaction::read(0x6B | 0x01, vec![0x02, 0xBC, 0x9A]),
1305 ];
1306 let i2c = I2cMock::new(&expected_transaction);
1307 let delay = NoopDelay::new();
1308 let mut sensor = Sen66::new(delay, i2c);
1309 sensor.state = SensorState::Idle;
1310 assert_eq!(
1311 sensor.get_ambient_pressure().await.unwrap(),
1312 AmbientPressure::try_from(700).unwrap()
1313 );
1314 sensor.kill().await.1.done();
1315 }
1316
1317 #[test_macro]
1318 async fn set_ambient_pressure_works() {
1319 let expected_transaction = [I2cTransaction::write(
1320 0x6B | 0x00,
1321 vec![0x67, 0x20, 0x02, 0xBC, 0x9A],
1322 )];
1323 let i2c = I2cMock::new(&expected_transaction);
1324 let delay = NoopDelay::new();
1325 let mut sensor = Sen66::new(delay, i2c);
1326 sensor.state = SensorState::Idle;
1327 sensor
1328 .set_ambient_pressure(AmbientPressure::try_from(700).unwrap())
1329 .await
1330 .unwrap();
1331 sensor.kill().await.1.done();
1332 }
1333
1334 #[test_macro]
1335 async fn get_sensor_altitude_works() {
1336 let expected_transaction = [
1337 I2cTransaction::write(0x6B | 0x00, vec![0x67, 0x36]),
1338 I2cTransaction::read(0x6B | 0x01, vec![0x02, 0xBC, 0x9A]),
1339 ];
1340 let i2c = I2cMock::new(&expected_transaction);
1341 let delay = NoopDelay::new();
1342 let mut sensor = Sen66::new(delay, i2c);
1343 sensor.state = SensorState::Idle;
1344 assert_eq!(
1345 sensor.get_sensor_altitude().await.unwrap(),
1346 SensorAltitude::try_from(700).unwrap()
1347 );
1348 sensor.kill().await.1.done();
1349 }
1350
1351 #[test_macro]
1352 async fn set_sensor_altitude_works() {
1353 let expected_transaction = [I2cTransaction::write(
1354 0x6B | 0x00,
1355 vec![0x67, 0x36, 0x02, 0xBC, 0x9A],
1356 )];
1357 let i2c = I2cMock::new(&expected_transaction);
1358 let delay = NoopDelay::new();
1359 let mut sensor = Sen66::new(delay, i2c);
1360 sensor.state = SensorState::Idle;
1361 sensor
1362 .set_sensor_altitude(SensorAltitude::try_from(700).unwrap())
1363 .await
1364 .unwrap();
1365 sensor.kill().await.1.done();
1366 }
1367 }
1368 }
1369
1370 #[cfg(feature=feature_)]
1371 pub use inner::*;
1372}