1use core::marker::PhantomData;
12
13use log::debug;
14
15use embedded_hal::delay::DelayNs;
16use embedded_hal::i2c::I2c;
17
18use crate::constants::DEFAULT_ADDRESS;
19use crate::sample::Sample;
20use crate::Altitude;
21use crate::Co2;
22use crate::Error;
23use crate::Idle;
24use crate::Measuring;
25use crate::Pressure;
26use crate::State;
27use crate::Temperature;
28
29use super::commands;
30use super::Command;
31
32pub struct Scd4x<I2c, Delay, State> {
34 i2c: I2c,
36
37 address: u8,
39
40 delay: Delay,
42
43 _state: PhantomData<State>,
45}
46
47impl<I2C, D> Scd4x<I2C, D, Idle>
48where
49 I2C: I2c,
50 D: DelayNs,
51{
52 pub fn new(i2c: I2C, delay: D) -> Self {
55 Self::new_with_address(i2c, DEFAULT_ADDRESS, delay)
56 }
57
58 pub fn new_with_address(i2c: I2C, address: u8, delay: D) -> Self {
61 Self {
62 i2c,
63 address,
64 delay,
65 _state: PhantomData,
66 }
67 }
68
69 pub fn start_periodic_measurement(mut self) -> Result<Scd4x<I2C, D, Measuring>, Error> {
75 debug!("Send command 'start_periodic_measurement'");
76
77 commands::StartPeriodicMeasurement.execute(
78 self.address,
79 &mut self.i2c,
80 &mut self.delay,
81 (),
82 )?;
83
84 Ok(Scd4x {
85 i2c: self.i2c,
86 address: self.address,
87 delay: self.delay,
88 _state: PhantomData,
89 })
90 }
91
92 pub fn set_temperature_offset(&mut self, temperature_offset: Temperature) -> Result<(), Error> {
98 debug!("Send command 'set_temperature_offset'");
99
100 commands::SetTemperatureOffset.execute(
101 self.address,
102 &mut self.i2c,
103 &mut self.delay,
104 temperature_offset,
105 )
106 }
107
108 pub fn get_temperature_offset(&mut self) -> Result<Temperature, Error> {
114 debug!("Send command 'get_temperature_offset'");
115
116 commands::GetTemperatureOffset.execute(self.address, &mut self.i2c, &mut self.delay, ())
117 }
118
119 pub fn set_sensor_altitude(&mut self, sensor_altitude: Altitude) -> Result<(), Error> {
125 debug!("Send command 'set_sensor_altitude'");
126
127 commands::SetSensorAltitude.execute(
128 self.address,
129 &mut self.i2c,
130 &mut self.delay,
131 sensor_altitude,
132 )
133 }
134
135 pub fn get_sensor_altitude(&mut self) -> Result<Altitude, Error> {
141 debug!("Send command 'get_sensor_altitude'");
142
143 commands::GetSensorAltitude.execute(self.address, &mut self.i2c, &mut self.delay, ())
144 }
145
146 pub fn perform_forced_recalibration(&mut self, co2: Co2) -> Result<Option<Co2>, Error> {
152 debug!("Send command 'perform_forced_recalibration'");
153
154 commands::PerformForcedRecalibration.execute(
155 self.address,
156 &mut self.i2c,
157 &mut self.delay,
158 co2,
159 )
160 }
161
162 pub fn set_automatic_self_calibration_enabled(&mut self, enabled: bool) -> Result<(), Error> {
168 debug!("Send command 'set_automatic_self_calibration_enabled'");
169
170 commands::SetAutomaticSelfCalibrationEnabled.execute(
171 self.address,
172 &mut self.i2c,
173 &mut self.delay,
174 enabled,
175 )
176 }
177
178 pub fn get_automatic_self_calibration_enabled(&mut self) -> Result<bool, Error> {
184 debug!("Send command 'get_automatic_self_calibration_enabled'");
185
186 commands::GetAutomaticSelfCalibrationEnabled.execute(
187 self.address,
188 &mut self.i2c,
189 &mut self.delay,
190 (),
191 )
192 }
193
194 pub fn start_low_power_periodic_measurement(
200 mut self,
201 ) -> Result<Scd4x<I2C, D, Measuring>, Error> {
202 debug!("Send command 'start_low_power_periodic_measurement'");
203
204 commands::StartLowPowerPeriodicMeasurement.execute(
205 self.address,
206 &mut self.i2c,
207 &mut self.delay,
208 (),
209 )?;
210
211 Ok(Scd4x {
212 i2c: self.i2c,
213 address: self.address,
214 delay: self.delay,
215 _state: PhantomData,
216 })
217 }
218
219 pub fn persist_settings(&mut self) -> Result<(), Error> {
225 debug!("Send command 'persist_settings'");
226
227 commands::PersistSettings.execute(self.address, &mut self.i2c, &mut self.delay, ())
228 }
229
230 pub fn get_serial_number(&mut self) -> Result<u64, Error> {
236 debug!("Send command 'get_serial_number'");
237
238 commands::GetSerialNumber.execute(self.address, &mut self.i2c, &mut self.delay, ())
239 }
240
241 pub fn perform_self_test(&mut self) -> Result<bool, Error> {
247 debug!("Send command 'perform_self_test'");
248
249 commands::PerformSelfTest.execute(self.address, &mut self.i2c, &mut self.delay, ())
250 }
251
252 pub fn perform_factory_reset(&mut self) -> Result<(), Error> {
258 debug!("Send command 'perform_factory_reset'");
259
260 commands::PerformFactoryReset.execute(self.address, &mut self.i2c, &mut self.delay, ())
261 }
262
263 pub fn reinit(&mut self) -> Result<(), Error> {
275 debug!("Send command 'reinit'");
276
277 commands::Reinitialize.execute(self.address, &mut self.i2c, &mut self.delay, ())
278 }
279
280 pub fn measure_single_shot(mut self) -> Result<Scd4x<I2C, D, Measuring>, Error> {
286 debug!("Send command 'measure_single_shot'");
287
288 commands::MeasureSingleShot.execute(self.address, &mut self.i2c, &mut self.delay, ())?;
289
290 Ok(Scd4x {
291 i2c: self.i2c,
292 address: self.address,
293 delay: self.delay,
294 _state: PhantomData,
295 })
296 }
297
298 pub fn measure_single_shot_rht_only(mut self) -> Result<Scd4x<I2C, D, Measuring>, Error> {
304 debug!("Send command 'measure_single_shot_rht_only'");
305
306 commands::MeasureSingleShotRhtOnly.execute(
307 self.address,
308 &mut self.i2c,
309 &mut self.delay,
310 (),
311 )?;
312
313 Ok(Scd4x {
314 i2c: self.i2c,
315 address: self.address,
316 delay: self.delay,
317 _state: PhantomData,
318 })
319 }
320}
321
322impl<I2C, D> Scd4x<I2C, D, Measuring>
323where
324 I2C: I2c,
325 D: DelayNs,
326{
327 pub fn new_in_measuring(i2c: I2C, delay: D) -> Self {
330 Self::new_in_measuring_with_address(i2c, DEFAULT_ADDRESS, delay)
331 }
332
333 pub fn new_in_measuring_with_address(i2c: I2C, address: u8, delay: D) -> Self {
336 Self {
337 i2c,
338 address,
339 delay,
340 _state: PhantomData,
341 }
342 }
343
344 pub fn read_measurement(&mut self) -> Result<Sample, Error> {
350 debug!("Send command 'read_measurement'");
351
352 commands::ReadMeasurement.execute(self.address, &mut self.i2c, &mut self.delay, ())
353 }
354
355 pub fn get_data_ready_status(&mut self) -> Result<bool, Error> {
361 debug!("Send command 'get_data_ready_status'");
362
363 commands::GetDataReadyStatus.execute(self.address, &mut self.i2c, &mut self.delay, ())
364 }
365}
366
367impl<I2C, D, S> Scd4x<I2C, D, S>
368where
369 I2C: I2c,
370 D: DelayNs,
371 S: State,
372{
373 pub fn release(self) -> I2C {
375 self.i2c
376 }
377
378 pub fn stop_periodic_measurement(mut self) -> Result<Scd4x<I2C, D, Idle>, Error> {
384 debug!("Send command 'stop_periodic_measurement'");
385
386 commands::StopPeriodicMeasurement.execute(
387 self.address,
388 &mut self.i2c,
389 &mut self.delay,
390 (),
391 )?;
392
393 Ok(Scd4x {
394 i2c: self.i2c,
395 address: self.address,
396 delay: self.delay,
397 _state: PhantomData,
398 })
399 }
400
401 pub fn set_ambient_pressure(&mut self, ambient_pressure: Pressure) -> Result<(), Error> {
407 debug!("Send command 'set_ambient_pressure'");
408
409 commands::SetAmbientPressure.execute(
410 self.address,
411 &mut self.i2c,
412 &mut self.delay,
413 ambient_pressure,
414 )
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 #![allow(clippy::panic_in_result_fn)]
421
422 use super::*;
423
424 use embedded_hal_mock::eh1::delay::NoopDelay as DelayMock;
425 use embedded_hal_mock::eh1::i2c::Mock as I2cMock;
426 use embedded_hal_mock::eh1::i2c::Transaction as I2cTransaction;
427
428 use crate::sample::altitude_from_meter;
429 use crate::sample::co2_from_ppm;
430 use crate::sample::humidity_from_number;
431 use crate::sample::pressure_from_hectopascal;
432 use crate::sample::temperature_from_celsius;
433 use crate::Error;
434
435 #[test]
436 fn test_get_serial_number() -> Result<(), Error> {
437 let expectations = [
438 I2cTransaction::write(DEFAULT_ADDRESS, vec![0x36, 0x82]),
439 I2cTransaction::read(
440 DEFAULT_ADDRESS,
441 vec![0xf8, 0x96, 0x31, 0x9f, 0x07, 0xc2, 0x3b, 0xbe, 0x89],
442 ),
443 ];
444 let i2c = I2cMock::new(&expectations);
445
446 let mut scd4x = Scd4x::new(i2c, DelayMock);
447
448 let serial_number = scd4x.get_serial_number()?;
449
450 assert_eq!(serial_number, 273_325_796_834_238);
451
452 scd4x.release().done();
453 Ok(())
454 }
455
456 #[test]
457 fn test_reinit() -> Result<(), Error> {
458 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x36, 0x46])];
459 let i2c = I2cMock::new(&expectations);
460
461 let mut scd4x = Scd4x::new(i2c, DelayMock);
462
463 scd4x.reinit()?;
464
465 scd4x.release().done();
466 Ok(())
467 }
468
469 #[test]
470 fn test_measure_single_shot() -> Result<(), Error> {
471 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x21, 0x9d])];
472 let i2c = I2cMock::new(&expectations);
473
474 let scd4x = Scd4x::new(i2c, DelayMock);
475
476 let scd4x = scd4x.measure_single_shot()?;
477
478 scd4x.release().done();
479 Ok(())
480 }
481
482 #[test]
483 fn test_measure_single_shot_rht_only() -> Result<(), Error> {
484 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x21, 0x96])];
485 let i2c = I2cMock::new(&expectations);
486
487 let scd4x = Scd4x::new(i2c, DelayMock);
488
489 let scd4x = scd4x.measure_single_shot_rht_only()?;
490
491 scd4x.release().done();
492 Ok(())
493 }
494
495 #[test]
496 fn test_perform_factory_reset() -> Result<(), Error> {
497 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x36, 0x32])];
498 let i2c = I2cMock::new(&expectations);
499
500 let mut scd4x = Scd4x::new(i2c, DelayMock);
501
502 scd4x.perform_factory_reset()?;
503
504 scd4x.release().done();
505 Ok(())
506 }
507
508 #[test]
509 fn test_perform_self_test() -> Result<(), Error> {
510 let expectations = [
511 I2cTransaction::write(DEFAULT_ADDRESS, vec![0x36, 0x39]),
512 I2cTransaction::read(DEFAULT_ADDRESS, vec![0x00, 0x00, 0x81]),
513 ];
514 let i2c = I2cMock::new(&expectations);
515
516 let mut scd4x = Scd4x::new(i2c, DelayMock);
517
518 let result = scd4x.perform_self_test()?;
519 assert!(result);
520
521 scd4x.release().done();
522 Ok(())
523 }
524
525 #[test]
526 fn test_persist_settings() -> Result<(), Error> {
527 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x36, 0x15])];
528 let i2c = I2cMock::new(&expectations);
529
530 let mut scd4x = Scd4x::new(i2c, DelayMock);
531
532 scd4x.persist_settings()?;
533
534 scd4x.release().done();
535 Ok(())
536 }
537
538 #[test]
539 fn test_get_data_ready_status() -> Result<(), Error> {
540 let expectations = [
541 I2cTransaction::write(DEFAULT_ADDRESS, vec![0xe4, 0xb8]),
542 I2cTransaction::read(DEFAULT_ADDRESS, vec![0x80, 0x00, 0xa2]),
543 ];
544 let i2c = I2cMock::new(&expectations);
545
546 let mut scd4x = Scd4x::new_in_measuring(i2c, DelayMock);
547
548 let ready = scd4x.get_data_ready_status()?;
549 assert!(!ready);
550
551 scd4x.release().done();
552 Ok(())
553 }
554
555 #[test]
556 fn test_start_low_power_periodic_measurement() -> Result<(), Error> {
557 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x21, 0xac])];
558 let i2c = I2cMock::new(&expectations);
559
560 let scd4x = Scd4x::new(i2c, DelayMock);
561
562 let scd4x = scd4x.start_low_power_periodic_measurement()?;
563
564 scd4x.release().done();
565 Ok(())
566 }
567
568 #[test]
569 fn test_get_automatic_self_calibration_enabled() -> Result<(), Error> {
570 let expectations = [
571 I2cTransaction::write(DEFAULT_ADDRESS, vec![0x23, 0x13]),
572 I2cTransaction::read(DEFAULT_ADDRESS, vec![0x00, 0x00, 0x81]),
573 ];
574 let i2c = I2cMock::new(&expectations);
575
576 let mut scd4x = Scd4x::new(i2c, DelayMock);
577
578 let enabled = scd4x.get_automatic_self_calibration_enabled()?;
579 assert!(!enabled);
580
581 scd4x.release().done();
582 Ok(())
583 }
584
585 #[test]
586 fn test_set_automatic_self_calibration_enabled() -> Result<(), Error> {
587 let expectations = [I2cTransaction::write(
588 DEFAULT_ADDRESS,
589 vec![0x24, 0x16, 0x00, 0x01, 0xb0],
590 )];
591 let i2c = I2cMock::new(&expectations);
592
593 let mut scd4x = Scd4x::new(i2c, DelayMock);
594
595 scd4x.set_automatic_self_calibration_enabled(true)?;
596
597 scd4x.release().done();
598 Ok(())
599 }
600
601 #[test]
602 fn test_perform_forced_recalibration() -> Result<(), Error> {
603 let expectations = [
604 I2cTransaction::write(DEFAULT_ADDRESS, vec![0x36, 0x2f, 0x01, 0xe0, 0xb4]),
605 I2cTransaction::read(DEFAULT_ADDRESS, vec![0x7f, 0xce, 0x7b]),
606 ];
607 let i2c = I2cMock::new(&expectations);
608
609 let mut scd4x = Scd4x::new(i2c, DelayMock);
610
611 let correction = scd4x.perform_forced_recalibration(co2_from_ppm(480.0))?;
612 assert_eq!(correction, Some(co2_from_ppm(-50.0)));
613
614 scd4x.release().done();
615 Ok(())
616 }
617
618 #[test]
619 fn test_perform_forced_recalibration_failure() -> Result<(), Error> {
620 let expectations = [
621 I2cTransaction::write(DEFAULT_ADDRESS, vec![0x36, 0x2f, 0x01, 0xe0, 0xb4]),
622 I2cTransaction::read(DEFAULT_ADDRESS, vec![0xff, 0xff, 0xac]),
623 ];
624 let i2c = I2cMock::new(&expectations);
625
626 let mut scd4x = Scd4x::new(i2c, DelayMock);
627
628 let correction = scd4x.perform_forced_recalibration(co2_from_ppm(480.0))?;
629 assert_eq!(correction, None);
630
631 scd4x.release().done();
632 Ok(())
633 }
634
635 #[test]
636 fn test_set_ambient_pressure() -> Result<(), Error> {
637 let expectations = [I2cTransaction::write(
638 DEFAULT_ADDRESS,
639 vec![0xe0, 0x00, 0x03, 0xdb, 0x42],
640 )];
641 let i2c = I2cMock::new(&expectations);
642
643 let mut scd4x = Scd4x::new(i2c, DelayMock);
644
645 scd4x.set_ambient_pressure(pressure_from_hectopascal(987.0))?;
646
647 scd4x.release().done();
648 Ok(())
649 }
650
651 #[test]
652 fn test_get_sensor_altitude() -> Result<(), Error> {
653 let expectations = [
654 I2cTransaction::write(DEFAULT_ADDRESS, vec![0x23, 0x22]),
655 I2cTransaction::read(DEFAULT_ADDRESS, vec![0x04, 0x4c, 0x42]),
656 ];
657 let i2c = I2cMock::new(&expectations);
658
659 let mut scd4x = Scd4x::new(i2c, DelayMock);
660
661 let sensor_altitude = scd4x.get_sensor_altitude()?;
662 assert_eq!(sensor_altitude, altitude_from_meter(1100.0));
663
664 scd4x.release().done();
665 Ok(())
666 }
667
668 #[test]
669 fn test_set_sensor_altitude() -> Result<(), Error> {
670 let expectations = [I2cTransaction::write(
671 DEFAULT_ADDRESS,
672 vec![0x24, 0x27, 0x07, 0x9e, 0x09],
673 )];
674 let i2c = I2cMock::new(&expectations);
675
676 let mut scd4x = Scd4x::new(i2c, DelayMock);
677
678 scd4x.set_sensor_altitude(altitude_from_meter(1950.0))?;
679
680 scd4x.release().done();
681 Ok(())
682 }
683
684 #[test]
685 fn test_get_temperature_offset() -> Result<(), Error> {
686 let expectations = [
687 I2cTransaction::write(DEFAULT_ADDRESS, vec![0x23, 0x18]),
688 I2cTransaction::read(DEFAULT_ADDRESS, vec![0x09, 0x12, 0x63]),
689 ];
690 let i2c = I2cMock::new(&expectations);
691
692 let mut scd4x = Scd4x::new(i2c, DelayMock);
693
694 let temperature_offset = scd4x.get_temperature_offset()?;
695 assert_eq!(temperature_offset, temperature_from_celsius(6.200_409));
696
697 scd4x.release().done();
698 Ok(())
699 }
700
701 #[test]
702 fn test_set_temperature_offset() -> Result<(), Error> {
703 let expectations = [I2cTransaction::write(
704 DEFAULT_ADDRESS,
705 vec![0x24, 0x1d, 0x07, 0xe6, 0x48],
706 )];
707 let i2c = I2cMock::new(&expectations);
708
709 let mut scd4x = Scd4x::new(i2c, DelayMock);
710
711 scd4x.set_temperature_offset(temperature_from_celsius(5.4))?;
712
713 scd4x.release().done();
714 Ok(())
715 }
716
717 #[test]
718 fn test_stop_periodic_measurement() -> Result<(), Error> {
719 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x3f, 0x86])];
720 let i2c = I2cMock::new(&expectations);
721
722 let scd4x = Scd4x::new(i2c, DelayMock);
723
724 let scd4x = scd4x.stop_periodic_measurement()?;
725
726 scd4x.release().done();
727 Ok(())
728 }
729
730 #[test]
731 fn test_read_measurement() -> Result<(), Error> {
732 let expectations = [
733 I2cTransaction::write(DEFAULT_ADDRESS, vec![0xec, 0x05]),
734 I2cTransaction::read(
735 DEFAULT_ADDRESS,
736 vec![0x01, 0xf4, 0x33, 0x66, 0x67, 0xa2, 0x5e, 0xb9, 0x3c],
737 ),
738 ];
739 let i2c = I2cMock::new(&expectations);
740
741 let mut scd4x = Scd4x::new_in_measuring(i2c, DelayMock);
742
743 let sample = scd4x.read_measurement()?;
744 let expected = Sample {
745 co2: co2_from_ppm(500.0),
746 temperature: temperature_from_celsius(25.001_602),
747 humidity: humidity_from_number(37.001_038),
748 };
749
750 assert_eq!(sample, expected);
751
752 scd4x.release().done();
753 Ok(())
754 }
755
756 #[test]
757 fn test_start_periodic_measurement() -> Result<(), Error> {
758 let expectations = [I2cTransaction::write(DEFAULT_ADDRESS, vec![0x21, 0xb1])];
759 let i2c = I2cMock::new(&expectations);
760
761 let scd4x = Scd4x::new(i2c, DelayMock);
762
763 let scd4x = scd4x.start_periodic_measurement()?;
764
765 scd4x.release().done();
766
767 Ok(())
768 }
769}