1#![cfg_attr(not(feature = "std"), no_std)]
2
3use core::time::Duration;
4use embedded_io_async::{Read, ReadExactError, Write};
5use smol::Timer;
6
7#[derive(Debug)]
8pub enum Error<E> {
9 WrongCrc,
10 Serial(E),
11 GotWrongResponse,
12}
13
14#[cfg(feature = "std")]
15impl<E: std::fmt::Display> std::fmt::Display for Error<E> {
16 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17 match self {
18 Self::Serial(e) => write!(f, "Serial error: {}", e),
19 Self::GotWrongResponse => write!(f, "Wrong response"),
20 Self::WrongCrc => write!(f, "Wrong checksum"),
21 }
22 }
23}
24
25#[cfg(feature = "std")]
26impl<E: std::error::Error> std::error::Error for Error<E> {}
27
28#[allow(dead_code)]
29enum Command {
30 StartContinuousMeasurement = 0x0036,
31 StopContinuousMeasurement = 0x0037,
32 SetMeasurementInterval = 0x0025,
33 GetDataReadyStatus = 0x0027,
34 ReadMeasurement = 0x0028,
35 SetAutomaticSelfCalibration = 0x003A,
36 ForcedRecalibrationValue = 0x0039,
37 SetTemperatureOffset = 0x003B,
38 SetAltitude = 0x0038,
39 ReadFirmwareVersion = 0x0020,
40 SoftReset = 0x0034,
41}
42
43enum RegFunction {
44 ReadHolding = 0x3,
45 #[allow(dead_code)]
46 ReadInput = 0x4,
47 WriteHolding = 0x6,
48}
49
50impl From<RegFunction> for u8 {
51 fn from(f: RegFunction) -> Self {
52 f as u8
53 }
54}
55
56const RESPONSE_TIME: Duration = Duration::from_millis(5);
57const MODBUS_ADDR: u8 = 0x61;
58
59#[derive(Debug)]
61pub struct Measurement {
62 pub co2: f32,
64 pub humidity: f32,
66 pub temperature: f32,
68}
69
70pub struct Scd30<Serial> {
72 serial: Serial,
73}
74
75impl<Serial> Scd30<Serial>
79where
80 Serial: Read + Write,
81{
82 pub const BAUDRATE: u16 = 19200;
83
84 pub fn new(serial: Serial) -> Self {
88 Scd30 { serial }
89 }
90
91 pub async fn soft_reset(&mut self) -> Result<(), Error<Serial::Error>> {
95 self.serial.flush().await.map_err(Error::Serial)?;
96 self.write(RegFunction::WriteHolding.into(), Command::SoftReset, 0x0001)
97 .await?;
98 if !self
99 .check_resp_data::<8, 6>(RegFunction::WriteHolding.into(), 4, 0x0001)
100 .await?
101 {
102 return Err(Error::GotWrongResponse);
103 }
104 Ok(())
105 }
106
107 pub async fn stop_measuring(&mut self) -> Result<(), Error<Serial::Error>> {
109 self.write(
110 RegFunction::WriteHolding.into(),
111 Command::StopContinuousMeasurement,
112 0x001,
113 )
114 .await?;
115 if !self
116 .check_resp_data::<8, 6>(RegFunction::WriteHolding.into(), 4, 0x0001)
117 .await?
118 {
119 return Err(Error::GotWrongResponse);
120 }
121 Ok(())
122 }
123
124 pub async fn set_automatic_calibration(
130 &mut self,
131 enable: bool,
132 ) -> Result<(), Error<Serial::Error>> {
133 let data = match enable {
134 true => 0x0001,
135 _ => 0,
136 };
137 self.write(
138 RegFunction::ReadHolding.into(),
139 Command::SetAutomaticSelfCalibration,
140 data,
141 )
142 .await?;
143 if !self
144 .check_resp_data::<7, 5>(RegFunction::ReadHolding.into(), 3, data)
145 .await?
146 {
147 return Err(Error::GotWrongResponse);
148 }
149 Ok(())
150 }
151
152 pub async fn force_recalibrate_with_value(
156 &mut self,
157 reference: u16,
158 ) -> Result<(), Error<Serial::Error>> {
159 self.write(
160 RegFunction::WriteHolding.into(),
161 Command::ForcedRecalibrationValue,
162 reference,
163 )
164 .await?;
165 if !self
166 .check_resp_data::<8, 6>(RegFunction::WriteHolding.into(), 4, reference)
167 .await?
168 {
169 return Err(Error::GotWrongResponse);
170 }
171 Ok(())
172 }
173
174 pub async fn get_forced_recalibration_value(&mut self) -> Result<u16, Error<Serial::Error>> {
176 self.write(
177 RegFunction::ReadHolding.into(),
178 Command::ForcedRecalibrationValue,
179 0x0001,
180 )
181 .await?;
182 Timer::after(RESPONSE_TIME).await;
183 let resp = self.read_n::<7, 5>(RegFunction::ReadHolding.into()).await?;
184 Ok(u16::from_be_bytes([resp[3], resp[4]]))
185 }
186
187 pub async fn set_temperature_offset(
195 &mut self,
196 offset: u16,
197 ) -> Result<(), Error<Serial::Error>> {
198 self.write(
199 RegFunction::WriteHolding.into(),
200 Command::SetTemperatureOffset,
201 offset,
202 )
203 .await?;
204 if !self
205 .check_resp_data::<8, 6>(RegFunction::WriteHolding.into(), 4, offset)
206 .await?
207 {
208 return Err(Error::GotWrongResponse);
209 }
210 Ok(())
211 }
212
213 pub async fn start_measuring(&mut self) -> Result<(), Error<Serial::Error>> {
215 self.start_measuring_with_mbar(0).await
216 }
217
218 pub async fn set_measurement_interval(
219 &mut self,
220 interval: Duration,
221 ) -> Result<(), Error<Serial::Error>> {
222 let secs = interval.as_secs();
223 debug_assert!((2..=1800).contains(&secs));
224 let secs = u16::try_from(secs).unwrap();
225 self.write(
226 RegFunction::WriteHolding.into(),
227 Command::SetMeasurementInterval,
228 secs,
229 )
230 .await?;
231 if !self
232 .check_resp_data::<8, 6>(RegFunction::WriteHolding.into(), 4, secs)
233 .await?
234 {
235 return Err(Error::GotWrongResponse);
236 }
237 Ok(())
238 }
239
240 pub async fn start_measuring_with_mbar(
256 &mut self,
257 pressure: u16,
258 ) -> Result<(), Error<Serial::Error>> {
259 debug_assert!(pressure == 0 || (700..=1400).contains(&pressure));
260 self.write(
261 RegFunction::WriteHolding.into(),
262 Command::StartContinuousMeasurement,
263 pressure,
264 )
265 .await?;
266 if !self
267 .check_resp_data::<8, 6>(RegFunction::WriteHolding.into(), 4, pressure)
268 .await?
269 {
270 return Err(Error::GotWrongResponse);
271 }
272 Ok(())
273 }
274
275 pub async fn data_ready(&mut self) -> Result<bool, Error<Serial::Error>> {
280 self.write(
281 RegFunction::ReadHolding.into(),
282 Command::GetDataReadyStatus,
283 0x0001,
284 )
285 .await?;
286 self.check_resp_data::<7, 5>(RegFunction::ReadHolding.into(), 3, 0x0001)
287 .await
288 }
289
290 pub async fn read(&mut self) -> Result<Option<Measurement>, Error<Serial::Error>> {
295 match self.data_ready().await {
296 Ok(true) => {
297 self.write(
298 RegFunction::ReadHolding.into(),
299 Command::ReadMeasurement,
300 0x0006,
301 )
302 .await?;
303 let buffer = self
304 .read_n::<17, 15>(RegFunction::ReadHolding.into())
305 .await?;
306 Ok(Some(Measurement {
307 co2: f32::from_bits(u32::from_be_bytes([
308 buffer[3], buffer[4], buffer[5], buffer[6],
309 ])),
310 temperature: f32::from_bits(u32::from_be_bytes([
311 buffer[7], buffer[8], buffer[9], buffer[10],
312 ])),
313 humidity: f32::from_bits(u32::from_be_bytes([
314 buffer[11], buffer[12], buffer[13], buffer[14],
315 ])),
316 }))
317 }
318 Ok(false) => Ok(None),
319 Err(e) => Err(e),
320 }
321 }
322
323 async fn check_resp_data<const NB: usize, const N: usize>(
324 &mut self,
325 func: u8,
326 high_byte_index: usize,
327 evaluate_data: u16,
328 ) -> Result<bool, Error<Serial::Error>> {
329 Timer::after(RESPONSE_TIME).await;
330 let resp = self.read_n::<NB, N>(func).await?;
331 Ok(u16::from_be_bytes([resp[high_byte_index], resp[high_byte_index + 1]]) == evaluate_data)
332 }
333
334 async fn read_n<const NB: usize, const N: usize>(
335 &mut self,
336 func: u8,
337 ) -> Result<[u8; N], Error<Serial::Error>> {
338 let mut buffer = [0u8; NB];
339
340 let mut byte_index = 0;
341 loop {
342 match byte_index {
343 0 => {
344 let _skipped = self.skip_until_byte(MODBUS_ADDR).await?;
345 buffer[0] = MODBUS_ADDR;
346 }
347 1 => {
348 let byte = self.read_byte().await?;
349 if byte == func {
350 buffer[1] = func;
351 } else {
352 byte_index = 0;
353 continue;
354 }
355 }
356 i if i == NB => match check_crc(&buffer) {
357 true => break,
358 _ => return Err(Error::WrongCrc),
359 },
360 i => buffer[i] = self.read_byte().await?,
361 }
362 byte_index += 1;
363 }
364
365 let mut output = [0u8; N];
366 output.copy_from_slice(&buffer[..N]);
367
368 Ok(output)
369 }
370
371 async fn read_byte(&mut self) -> Result<u8, Error<Serial::Error>> {
372 let mut buf = [0u8; 1];
373 self.serial
374 .read_exact(&mut buf)
375 .await
376 .map_err(|e| match e {
377 ReadExactError::Other(e) => Error::Serial(e),
378 ReadExactError::UnexpectedEof => panic!(),
379 })?;
380
381 Ok(buf[0])
382 }
383
384 async fn skip_until_byte(&mut self, byte: u8) -> Result<usize, Error<Serial::Error>> {
385 let mut skipped = 0;
386 loop {
387 let read_byte = self.read_byte().await?;
388 if read_byte == byte {
389 break;
390 }
391 skipped += 1;
392 }
393 Ok(skipped)
394 }
395
396 async fn write(
397 &mut self,
398 func: u8,
399 cmd: Command,
400 data: u16,
401 ) -> Result<(), Error<Serial::Error>> {
402 let mut buffer = [0u8; 6 + 2];
403
404 buffer[0] = MODBUS_ADDR;
405 buffer[1] = func;
406
407 buffer[2..=3].copy_from_slice(&(cmd as u16).to_be_bytes());
408 buffer[4..=5].copy_from_slice(&data.to_be_bytes());
409
410 let crc = calculate_crc(&buffer[..6]);
411 buffer[6..=7].copy_from_slice(&crc.to_be_bytes());
412 self.write_n(&buffer).await
413 }
414
415 async fn write_n(&mut self, data: &[u8]) -> Result<(), Error<Serial::Error>> {
416 self.serial.write_all(data).await.map_err(Error::Serial)
417 }
418}
419
420fn calculate_crc(data: &[u8]) -> u16 {
421 let mut crc = 0xFFFFu16;
422 for d in data {
423 crc ^= u16::from(*d);
424 for _ in 0..u8::BITS {
425 if (crc & 0x0001) != 0 {
426 crc >>= 1;
427 crc ^= 0xA001;
428 } else {
429 crc >>= 1;
430 }
431 }
432 }
433 (crc & 0x00FF) << 8 | (crc & 0xFF00) >> 8
434}
435
436fn check_crc(data: &[u8]) -> bool {
437 let target_length = data.len() - 2;
438 let calculated_crc = calculate_crc(&data[..target_length]);
439
440 calculated_crc.eq(&u16::from_be_bytes([
441 data[data.len() - 2],
442 data[data.len() - 1],
443 ]))
444}
445
446#[cfg(test)]
447mod tests {
448 use crate::{calculate_crc, check_crc};
449
450 #[test]
451 fn test_crc_check() {
452 const BYTES: [u8; 3] = [0x13, 0x12, 0x14];
453 let crc = calculate_crc(&BYTES);
454 let mut bytes_with_crc = [0u8; BYTES.len() + 2];
455 bytes_with_crc[..BYTES.len()].copy_from_slice(&BYTES);
456 bytes_with_crc[BYTES.len() + 0] = u8::try_from(crc >> u8::BITS).unwrap();
457 bytes_with_crc[BYTES.len() + 1] = u8::try_from(crc & 0xFF).unwrap();
458
459 assert!(check_crc(&bytes_with_crc))
460 }
461
462 #[test]
463 fn test_crc_from_sensor() {
464 const BYTES: [u8; 8] = [97, 6, 0, 52, 0, 1, 0, 100];
465 assert!(check_crc(&BYTES))
466 }
467}