1use duplicate::duplicate_item;
2
3const ADDRESS: u8 = 0x61;
4const WRITE_FLAG: u8 = 0x00;
5const READ_FLAG: u8 = 0x01;
6
7#[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 i2c_trait test_macro;
20 ["blocking"] [blocking] [] [identity()] [embedded_hal::i2c::I2c<Error = I2cErr>] [test];
21 ["async"] [asynch] [async] [await.identity()] [embedded_hal_async::i2c::I2c<Error = I2cErr>] [tokio::test];
22)]
23pub mod module {
24 #[cfg(feature=feature_)]
27 mod inner {
28 use crate::{
29 command::Command,
30 data::{
31 AltitudeCompensation, AmbientPressureCompensation, AutomaticSelfCalibration,
32 DataStatus, FirmwareVersion, ForcedRecalibrationValue, Measurement,
33 MeasurementInterval, TemperatureOffset,
34 },
35 error::Scd30Error,
36 interface::{Identity, ADDRESS, READ_FLAG, WRITE_FLAG},
37 util::compute_crc8,
38 };
39
40 pub struct Scd30<I2C> {
42 i2c: I2C,
43 }
44
45 impl<I2C: i2c_trait, I2cErr: embedded_hal::i2c::Error> Scd30<I2C> {
46 pub fn new(i2c: I2C) -> Self {
48 Self { i2c }
49 }
50
51 pub async fn trigger_continuous_measurements(
58 &mut self,
59 pressure_compensation: Option<AmbientPressureCompensation>,
60 ) -> Result<(), Scd30Error<I2cErr>> {
61 let data = match pressure_compensation {
62 None => [0x0, 0x0],
63 Some(pres) => pres.to_be_bytes(),
64 };
65 self.write(Command::TriggerContinuousMeasurement, Some(&data))
66 .await
67 }
68
69 pub async fn stop_continuous_measurements(&mut self) -> Result<(), Scd30Error<I2cErr>> {
71 self.write(Command::StopContinuousMeasurement, None).await
72 }
73
74 pub async fn set_measurement_interval(
76 &mut self,
77 interval: MeasurementInterval,
78 ) -> Result<(), Scd30Error<I2cErr>> {
79 self.write(
80 Command::SetMeasurementInterval,
81 Some(&interval.to_be_bytes()),
82 )
83 .await
84 }
85
86 pub async fn get_measurement_interval(
88 &mut self,
89 ) -> Result<MeasurementInterval, Scd30Error<I2cErr>> {
90 let receive = self.read::<3>(Command::SetMeasurementInterval).await?;
91 Ok(MeasurementInterval::try_from(&receive[..])?)
92 }
93
94 pub async fn is_data_ready(&mut self) -> Result<DataStatus, Scd30Error<I2cErr>> {
96 let receive = self.read::<3>(Command::GetDataReady).await?;
97 Ok(DataStatus::try_from(&receive[..])?)
98 }
99
100 pub async fn read_measurement(&mut self) -> Result<Measurement, Scd30Error<I2cErr>> {
102 let receive = self.read::<18>(Command::ReadMeasurement).await?;
103 Ok(Measurement::try_from(&receive[..])?)
104 }
105
106 pub async fn set_automatic_self_calibration(
108 &mut self,
109 setting: AutomaticSelfCalibration,
110 ) -> Result<(), Scd30Error<I2cErr>> {
111 self.write(
112 Command::ActivateAutomaticSelfCalibration,
113 Some(&setting.to_be_bytes()),
114 )
115 .await
116 }
117
118 pub async fn get_automatic_self_calibration(
120 &mut self,
121 ) -> Result<AutomaticSelfCalibration, Scd30Error<I2cErr>> {
122 let receive = self
123 .read::<3>(Command::ActivateAutomaticSelfCalibration)
124 .await?;
125 Ok(AutomaticSelfCalibration::try_from(&receive[..])?)
126 }
127
128 pub async fn set_forced_recalibration(
131 &mut self,
132 frc: ForcedRecalibrationValue,
133 ) -> Result<(), Scd30Error<I2cErr>> {
134 self.write(Command::ForcedRecalibrationValue, Some(&frc.to_be_bytes()))
135 .await
136 }
137
138 pub async fn get_forced_recalibration(
140 &mut self,
141 ) -> Result<ForcedRecalibrationValue, Scd30Error<I2cErr>> {
142 let receive = self.read::<3>(Command::ForcedRecalibrationValue).await?;
143 Ok(ForcedRecalibrationValue::try_from(&receive[..])?)
144 }
145
146 pub async fn set_temperature_offset(
149 &mut self,
150 offset: TemperatureOffset,
151 ) -> Result<(), Scd30Error<I2cErr>> {
152 self.write(Command::SetTemperatureOffset, Some(&offset.to_be_bytes()))
153 .await
154 }
155
156 pub async fn get_temperature_offset(
158 &mut self,
159 ) -> Result<TemperatureOffset, Scd30Error<I2cErr>> {
160 let receive = self.read::<3>(Command::SetTemperatureOffset).await?;
161 Ok(TemperatureOffset::try_from(&receive[..])?)
162 }
163
164 pub async fn set_altitude_compensation(
167 &mut self,
168 altitude: AltitudeCompensation,
169 ) -> Result<(), Scd30Error<I2cErr>> {
170 self.write(
171 Command::SetAltitudeCompensation,
172 Some(&altitude.to_be_bytes()),
173 )
174 .await
175 }
176
177 pub async fn get_altitude_compensation(
179 &mut self,
180 ) -> Result<AltitudeCompensation, Scd30Error<I2cErr>> {
181 let receive = self.read::<3>(Command::SetAltitudeCompensation).await?;
182 Ok(AltitudeCompensation::try_from(&receive[..])?)
183 }
184
185 pub async fn read_firmware_version(
187 &mut self,
188 ) -> Result<FirmwareVersion, Scd30Error<I2cErr>> {
189 let receive = self.read::<3>(Command::ReadFirmwareVersion).await?;
190 Ok(FirmwareVersion::try_from(&receive[..])?)
191 }
192
193 pub async fn soft_reset(&mut self) -> Result<(), Scd30Error<I2cErr>> {
195 self.write(Command::SoftReset, None).await
196 }
197
198 async fn read<const DATA_SIZE: usize>(
199 &mut self,
200 command: Command,
201 ) -> Result<[u8; DATA_SIZE], Scd30Error<I2cErr>> {
202 self.write(command, None).await?;
203 let mut data = [0; DATA_SIZE];
204 self.i2c.read(ADDRESS | READ_FLAG, &mut data).await?;
205 Ok(data)
206 }
207
208 async fn write(
209 &mut self,
210 command: Command,
211 data: Option<&[u8]>,
212 ) -> Result<(), Scd30Error<I2cErr>> {
213 let mut sent = [command.to_be_bytes()[0], command.to_be_bytes()[1], 0, 0, 0];
214
215 let len = if let Some(data) = data {
216 if data.len() != 2 {
217 return Err(Scd30Error::SentDataToBig);
218 }
219 sent[2] = data[0];
220 sent[3] = data[1];
221 sent[4] = compute_crc8(data);
222 5
223 } else {
224 2
225 };
226 Ok(self.i2c.write(ADDRESS | WRITE_FLAG, &sent[..len]).await?)
227 }
228
229 #[cfg(not(tarpaulin_include))]
231 pub fn shutdown(self) -> I2C {
232 self.i2c
233 }
234 }
235
236 #[cfg(test)]
237 mod tests {
238 use super::*;
239 use crate::data::AmbientPressure;
240 use embedded_hal::i2c;
241 use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
242
243 #[test_macro]
244 async fn trigger_continuous_measurements_with_ambient_pressure_compensation() {
245 let expected_transactions = [I2cTransaction::write(
246 0x61 | 0x00,
247 vec![0x00, 0x10, 0x03, 0x20, 0x2A],
248 )];
249
250 let i2c = I2cMock::new(&expected_transactions);
251
252 let mut sensor = Scd30::new(i2c);
253
254 sensor
255 .trigger_continuous_measurements(Some(
256 AmbientPressureCompensation::CompensationPressure(
257 AmbientPressure::try_from(800).unwrap(),
258 ),
259 ))
260 .await
261 .unwrap();
262 sensor.shutdown().done();
263 }
264
265 #[test_macro]
266 async fn trigger_continuous_measurements_spec_example_with_none() {
267 let expected_transactions = [I2cTransaction::write(
268 0x61 | 0x00,
269 vec![0x00, 0x10, 0x00, 0x00, 0x81],
270 )];
271
272 let i2c = I2cMock::new(&expected_transactions);
273
274 let mut sensor = Scd30::new(i2c);
275
276 sensor.trigger_continuous_measurements(None).await.unwrap();
277 sensor.shutdown().done();
278 }
279
280 #[test_macro]
281 async fn trigger_continuous_measurements_spec_example() {
282 let expected_transactions = [I2cTransaction::write(
283 0x61 | 0x00,
284 vec![0x00, 0x10, 0x00, 0x00, 0x81],
285 )];
286
287 let i2c = I2cMock::new(&expected_transactions);
288
289 let mut sensor = Scd30::new(i2c);
290
291 sensor
292 .trigger_continuous_measurements(Some(
293 AmbientPressureCompensation::DefaultPressure,
294 ))
295 .await
296 .unwrap();
297 sensor.shutdown().done();
298 }
299
300 #[test_macro]
301 async fn stop_continuous_measurements_spec_example() {
302 let expected_transactions = [I2cTransaction::write(0x61 | 0x00, vec![0x01, 0x04])];
303
304 let i2c = I2cMock::new(&expected_transactions);
305
306 let mut sensor = Scd30::new(i2c);
307
308 sensor.stop_continuous_measurements().await.unwrap();
309 sensor.shutdown().done();
310 }
311
312 #[test_macro]
313 async fn set_measurement_interval_spec_example() {
314 let expected_transactions = [I2cTransaction::write(
315 0x61 | 0x00,
316 vec![0x46, 0x00, 0x00, 0x02, 0xE3],
317 )];
318
319 let i2c = I2cMock::new(&expected_transactions);
320
321 let mut sensor = Scd30::new(i2c);
322
323 sensor
324 .set_measurement_interval(MeasurementInterval::try_from(2).unwrap())
325 .await
326 .unwrap();
327 sensor.shutdown().done();
328 }
329
330 #[test_macro]
331 async fn get_measurement_interval_spec_example() {
332 let expected_transactions = [
333 I2cTransaction::write(0x61 | 0x00, vec![0x46, 0x00]),
334 I2cTransaction::read(0x61 | 0x01, vec![0x00, 0x02, 0xE3]),
335 ];
336
337 let i2c = I2cMock::new(&expected_transactions);
338
339 let mut sensor = Scd30::new(i2c);
340
341 let interval = sensor.get_measurement_interval().await.unwrap();
342 assert_eq!(interval, MeasurementInterval::try_from(2).unwrap());
343 sensor.shutdown().done();
344 }
345
346 #[test_macro]
347 async fn get_ready_status_sample_works() {
348 let expected_transactions = [
349 I2cTransaction::write(0x61 | 0x00, vec![0x02, 0x02]),
350 I2cTransaction::read(0x61 | 0x01, vec![0x00, 0x01, 0xB0]),
351 ];
352
353 let i2c = I2cMock::new(&expected_transactions);
354
355 let mut sensor = Scd30::new(i2c);
356
357 let ready_status = sensor.is_data_ready().await.unwrap();
358 assert_eq!(ready_status, DataStatus::Ready);
359 sensor.shutdown().done();
360 }
361
362 #[test_macro]
363 async fn read_measurement_spec_example() {
364 let expected_transactions = [
365 I2cTransaction::write(0x61 | 0x00, vec![0x03, 0x00]),
366 I2cTransaction::read(
367 0x61 | 0x01,
368 vec![
369 0x43, 0xDB, 0xCB, 0x8C, 0x2E, 0x8F, 0x41, 0xD9, 0x70, 0xE7, 0xFF, 0xF5,
370 0x42, 0x43, 0xBF, 0x3A, 0x1B, 0x74,
371 ],
372 ),
373 ];
374
375 let i2c = I2cMock::new(&expected_transactions);
376
377 let mut sensor = Scd30::new(i2c);
378
379 let measurement = sensor.read_measurement().await.unwrap();
380 assert_eq!(measurement.co2_concentration, 439.09515);
381 assert_eq!(measurement.temperature, 27.23828);
382 assert_eq!(measurement.humidity, 48.806744);
383 sensor.shutdown().done();
384 }
385
386 #[test_macro]
387 async fn set_automatic_self_calibration_spec_example() {
388 let expected_transactions = [I2cTransaction::write(
389 0x61 | 0x00,
390 vec![0x53, 0x06, 0x00, 0x00, 0x81],
391 )];
392
393 let i2c = I2cMock::new(&expected_transactions);
394
395 let mut sensor = Scd30::new(i2c);
396
397 sensor
398 .set_automatic_self_calibration(AutomaticSelfCalibration::Inactive)
399 .await
400 .unwrap();
401 sensor.shutdown().done();
402 }
403
404 #[test_macro]
405 async fn get_automatic_self_calibration_spec_example() {
406 let expected_transactions = [
407 I2cTransaction::write(0x61 | 0x00, vec![0x53, 0x06]),
408 I2cTransaction::read(0x61 | 0x01, vec![0x00, 0x00, 0x81]),
409 ];
410
411 let i2c = I2cMock::new(&expected_transactions);
412
413 let mut sensor = Scd30::new(i2c);
414
415 let asc = sensor.get_automatic_self_calibration().await.unwrap();
416 assert_eq!(asc, AutomaticSelfCalibration::Inactive);
417 sensor.shutdown().done();
418 }
419
420 #[test_macro]
421 async fn set_forced_recalibration_spec_example() {
422 let expected_transactions = [I2cTransaction::write(
423 0x61 | 0x00,
424 vec![0x52, 0x04, 0x01, 0xC2, 0x50],
425 )];
426
427 let i2c = I2cMock::new(&expected_transactions);
428
429 let mut sensor = Scd30::new(i2c);
430
431 sensor
432 .set_forced_recalibration(ForcedRecalibrationValue::try_from(450).unwrap())
433 .await
434 .unwrap();
435 sensor.shutdown().done();
436 }
437
438 #[test_macro]
439 async fn get_forced_recalibration_spec_example() {
440 let expected_transactions = [
441 I2cTransaction::write(0x61 | 0x00, vec![0x52, 0x04]),
442 I2cTransaction::read(0x61 | 0x01, vec![0x01, 0xC2, 0x50]),
443 ];
444
445 let i2c = I2cMock::new(&expected_transactions);
446
447 let mut sensor = Scd30::new(i2c);
448
449 let frc = sensor.get_forced_recalibration().await.unwrap();
450 assert_eq!(frc, ForcedRecalibrationValue::try_from(450).unwrap());
451 sensor.shutdown().done();
452 }
453
454 #[test_macro]
455 async fn set_temperature_offset_spec_example() {
456 let expected_transactions = [I2cTransaction::write(
457 0x61 | 0x00,
458 vec![0x54, 0x03, 0x01, 0xF4, 0x33],
459 )];
460
461 let i2c = I2cMock::new(&expected_transactions);
462
463 let mut sensor = Scd30::new(i2c);
464
465 sensor
466 .set_temperature_offset(TemperatureOffset::try_from(5.0).unwrap())
467 .await
468 .unwrap();
469 sensor.shutdown().done();
470 }
471
472 #[test_macro]
473 async fn get_temperature_offset_spec_example() {
474 let expected_transactions = [
475 I2cTransaction::write(0x61 | 0x00, vec![0x54, 0x03]),
476 I2cTransaction::read(0x61 | 0x01, vec![0x01, 0xF4, 0x33]),
477 ];
478
479 let i2c = I2cMock::new(&expected_transactions);
480
481 let mut sensor = Scd30::new(i2c);
482
483 let offset = sensor.get_temperature_offset().await.unwrap();
484 assert_eq!(offset, TemperatureOffset::try_from(5.0).unwrap());
485 sensor.shutdown().done();
486 }
487
488 #[test_macro]
489 async fn set_altitude_compensation_spec_example() {
490 let expected_transactions = [I2cTransaction::write(
491 0x61 | 0x00,
492 vec![0x51, 0x02, 0x03, 0xE8, 0xD4],
493 )];
494
495 let i2c = I2cMock::new(&expected_transactions);
496
497 let mut sensor = Scd30::new(i2c);
498
499 sensor
500 .set_altitude_compensation(AltitudeCompensation::try_from(1000).unwrap())
501 .await
502 .unwrap();
503 sensor.shutdown().done();
504 }
505
506 #[test_macro]
507 async fn get_altitude_compensation_spec_example() {
508 let expected_transactions = [
509 I2cTransaction::write(0x61 | 0x00, vec![0x51, 0x02]),
510 I2cTransaction::read(0x61 | 0x01, vec![0x03, 0xE8, 0xD4]),
511 ];
512
513 let i2c = I2cMock::new(&expected_transactions);
514
515 let mut sensor = Scd30::new(i2c);
516
517 let altitude = sensor.get_altitude_compensation().await.unwrap();
518 assert_eq!(altitude, AltitudeCompensation::try_from(1000).unwrap());
519 sensor.shutdown().done();
520 }
521
522 #[test_macro]
523 async fn read_firmware_spec_example() {
524 let expected_transactions = [
525 I2cTransaction::write(0x61 | 0x00, vec![0xD1, 0x00]),
526 I2cTransaction::read(0x61 | 0x01, vec![0x03, 0x42, 0xF3]),
527 ];
528
529 let i2c = I2cMock::new(&expected_transactions);
530
531 let mut sensor = Scd30::new(i2c);
532
533 let version = sensor.read_firmware_version().await.unwrap();
534 assert_eq!(version.major, 3);
535 assert_eq!(version.minor, 66);
536 sensor.shutdown().done();
537 }
538
539 #[test_macro]
540 async fn execute_soft_reset_spec_example() {
541 let expected_transactions = [I2cTransaction::write(0x61 | 0x00, vec![0xD3, 0x04])];
542
543 let i2c = I2cMock::new(&expected_transactions);
544
545 let mut sensor = Scd30::new(i2c);
546
547 sensor.soft_reset().await.unwrap();
548 sensor.shutdown().done();
549 }
550
551 #[test_macro]
552 async fn read_errors_on_i2c_error() {
553 let expected_transactions = [
554 I2cTransaction::write(0x61 | 0x00, vec![0xD1, 0x00]),
555 I2cTransaction::read(0x61 | 0x01, vec![0x03, 0x42, 0xF3])
556 .with_error(i2c::ErrorKind::Other),
557 ];
558 let i2c = I2cMock::new(&expected_transactions);
559
560 let mut sensor = Scd30::new(i2c);
561
562 let result = sensor.read::<3>(Command::ReadFirmwareVersion);
563 assert_eq!(
564 result.await.unwrap_err(),
565 Scd30Error::I2cError(i2c::ErrorKind::Other)
566 );
567 sensor.shutdown().done();
568 }
569
570 #[test_macro]
571 async fn write_errors_on_i2c_error() {
572 let expected_transactions = [I2cTransaction::write(0x61 | 0x00, vec![0xD3, 0x04])
573 .with_error(i2c::ErrorKind::Other)];
574 let i2c = I2cMock::new(&expected_transactions);
575
576 let mut sensor = Scd30::new(i2c);
577
578 let result = sensor.write(Command::SoftReset, None);
579 assert_eq!(
580 result.await.unwrap_err(),
581 Scd30Error::I2cError(i2c::ErrorKind::Other)
582 );
583 sensor.shutdown().done();
584 }
585
586 #[test_macro]
587 async fn write_errors_on_too_big_send_data() {
588 let i2c = I2cMock::new(&[]);
589
590 let mut sensor = Scd30::new(i2c);
591
592 let result = sensor.write(
593 Command::SetTemperatureOffset,
594 Some([0x00, 0x00, 0x00, 0x00].as_slice()),
595 );
596 assert_eq!(result.await.unwrap_err(), Scd30Error::SentDataToBig);
597 sensor.shutdown().done();
598 }
599 }
600 }
601
602 #[cfg(feature=feature_)]
603 pub use inner::*;
604}