1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use embedded_hal::blocking::i2c::{Read, Write};
use heapless::{Vec, ArrayLength};
use heapless::consts::*;
use crc_all::Crc;
pub enum Command {
StartContinuousMeasurement = 0x0010,
StopContinuousMeasurement = 0x0104,
SetMeasurementInterval = 0x4600,
GetDataReadyStatus = 0x0202,
ReadMeasurement = 0x0300,
SetAutomaticSelfCalibration = 0x5306,
SetForcedRecalibrationValue = 0x5204,
SetTemperatureOffset = 0x5403,
SetAltitude = 0x5102,
ReadFirmwareVersion = 0xd100,
SoftReset = 0xd304,
}
const EXPECT_MSG: &str = "Vec was not large enough";
const ADDRESS: u8 = 0x61 << 1;
pub struct Scd30<T> {
comm: T,
address: u8,
}
#[derive(Debug)]
pub struct Measurement {
pub co2: f32,
pub humidity: f32,
pub temperature: f32,
}
impl<T, E> Scd30<T> where T: Read<Error = E> + Write<Error = E> {
fn add_argument<N>(&mut self, buf: &mut Vec<u8, N>, data: &[u8]) -> Result<(), ()> where N: ArrayLength<u8> {
buf.extend_from_slice(data)?;
let mut crc = Crc::<u8>::new(0x31, 8, 0xff, 0, false);
crc.update(data);
buf.push(crc.finish()).map_err(|_| ())
}
pub fn new(i2c: T) -> Self {
Scd30 {
comm: i2c,
address: ADDRESS
}
}
pub fn new_with_address(i2c: T, address: u8) -> Self {
Scd30 {
comm: i2c,
address
}
}
pub fn soft_reset(&mut self) -> Result<(), E> {
self.comm.write(self.address, &(Command::SoftReset as u16).to_be_bytes())
}
pub fn stop_measuring(&mut self) -> Result<(), E> {
self.comm.write(self.address, &(Command::StopContinuousMeasurement as u16).to_be_bytes())
}
pub fn set_automatic_calibration(&mut self, enable: bool) -> Result<(), E> {
let mut vec: Vec<u8, U5> = Vec::new();
vec.extend_from_slice(&(Command::SetAutomaticSelfCalibration as u16).to_be_bytes()).expect(EXPECT_MSG);
self.add_argument(&mut vec, &(enable as u16).to_be_bytes()).expect(EXPECT_MSG);
self.comm.write(self.address, &vec)
}
pub fn set_forced_recalibration_value(&mut self, co2: u16) -> Result<(), E> {
let mut vec: Vec<u8, U5> = Vec::new();
vec.extend_from_slice(&(Command::SetForcedRecalibrationValue as u16).to_be_bytes()).expect(EXPECT_MSG);
self.add_argument(&mut vec, &co2.to_be_bytes()).expect(EXPECT_MSG);
self.comm.write(self.address, &vec)
}
pub fn start_measuring(&mut self) -> Result<(), E> {
self.start_measuring_with_mbar(0)
}
pub fn set_measurement_interval(&mut self, seconds: u16) -> Result<(), E> {
let mut vec: Vec<u8, U5> = Vec::new();
vec.extend_from_slice(&(Command::SetMeasurementInterval as u16).to_be_bytes()).expect(EXPECT_MSG);
self.add_argument(&mut vec, &seconds.to_be_bytes()).expect(EXPECT_MSG);
self.comm.write(self.address, &vec)
}
pub fn start_measuring_with_mbar(&mut self, pressure: u16) -> Result<(), E> {
let mut vec: Vec<u8, U5> = Vec::new();
vec.extend_from_slice(&(Command::StartContinuousMeasurement as u16).to_be_bytes()).expect(EXPECT_MSG);
self.add_argument(&mut vec, &pressure.to_be_bytes()).expect(EXPECT_MSG);
self.comm.write(self.address, &vec)
}
pub fn data_ready(&mut self) -> Result<bool, E> {
let mut buf = [0u8; 2];
self.comm.write(self.address, &(Command::GetDataReadyStatus as u16).to_be_bytes())?;
self.comm.read(self.address, &mut buf)?;
Ok(u16::from_be_bytes(buf) == 1)
}
pub fn read(&mut self) -> Result<Option<Measurement>, E> {
match self.data_ready() {
Ok(true) => {
let mut buf = [0u8; 6 * 3];
self.comm.write(self.address, &(Command::ReadMeasurement as u16).to_be_bytes())?;
self.comm.read(self.address, &mut buf)?;
Ok(Some(Measurement {
co2: f32::from_bits(u32::from_be_bytes([ buf[0], buf[1], buf[3], buf[4] ])),
temperature: f32::from_bits(u32::from_be_bytes([ buf[6], buf[7], buf[9], buf[10] ])),
humidity: f32::from_bits(u32::from_be_bytes([ buf[12], buf[13], buf[15], buf[16] ])),
}))
},
Ok(false) => Ok(None),
Err(e) => Err(e),
}
}
}