am2320/
lib.rs

1//! # am2320
2//!
3//! A platform-agnostic driver to interface with the AM2320 I2c temperature & humidity
4//! sensor using `embedded-hal` traits.
5//!
6#![no_std]
7#![deny(warnings, missing_docs)]
8
9use embedded_hal::blocking::{delay, i2c};
10
11const DEVICE_I2C_ADDR: u8 = 0x5c;
12
13/// Describes potential errors
14#[derive(Debug)]
15pub enum Error {
16    /// Something went wrong while writing to the sensor
17    WriteError,
18    /// Something went wrong while reading from the sensor
19    ReadError,
20    /// The sensor returned data that is out of spec
21    SensorError,
22}
23
24/// Representation of a measurement from the sensor
25#[derive(Debug)]
26pub struct Measurement {
27    /// Temperature in degrees celsius (°C)
28    pub temperature: f32,
29    /// Humidity in percent (%)
30    pub humidity: f32,
31}
32
33/// Sensor configuration
34pub struct Am2320<I2C, Delay> {
35    /// I2C master device to use to communicate with the sensor
36    device: I2C,
37    /// Delay device to be able to sleep in-between commands
38    delay: Delay,
39}
40
41#[inline(always)]
42fn crc16(data: &[u8]) -> u16 {
43    let mut crc: u16 = 0xFFFF;
44    for e in data.iter() {
45        crc ^= u16::from(*e);
46        for _ in 0..8 {
47            if crc & 0x0001 == 0x0001 {
48                crc >>= 1;
49                crc ^= 0xA001;
50            } else {
51                crc >>= 1;
52            }
53        }
54    }
55    crc
56}
57
58impl<I2C, Delay, E> Am2320<I2C, Delay>
59where
60    I2C: i2c::Read<Error = E> + i2c::Write<Error = E>,
61    Delay: delay::DelayUs<u16>,
62{
63    /// Create a AM2320 temperature sensor driver.
64    ///
65    /// Example with `rppal`:
66    ///
67    /// ```!ignore
68    /// use am2320::*;
69    /// use rppal::{hal::Delay, i2c::I2c};
70    /// fn main() -> Result<(), Error> {
71    ///     let device = I2c::new().expect("could not initialize I2c on your RPi");
72    ///     let delay = Delay::new();
73    ///
74    ///     let mut am2320 = Am2320::new(device, delay);
75    ///
76    ///     println!("{:?}", am2320.read());
77    ///     Ok(())
78    /// }
79    /// ```
80    pub fn new(device: I2C, delay: Delay) -> Self {
81        Self { device, delay }
82    }
83
84    /// Reads one `Measurement` from the sensor
85    ///
86    /// The operation is blocking, and should take ~3 ms according the spec.
87    /// This is because the sensor goes into sleep and has to be waken up first.
88    /// Then it'll wait a while before sending data in-order for the measurement
89    /// to be more accurate.
90    ///
91    pub fn read(&mut self) -> Result<Measurement, Error> {
92        // We need to wake up the AM2320, since it goes to sleep in order not
93        // to warm up and affect the humidity sensor. This write will fail as
94        // the AM2320 won't ACK this write.
95        let _ = self.device.write(DEVICE_I2C_ADDR, &[0x00]);
96        // Wait at least 0.8ms, at most 3ms.
97        self.delay.delay_us(900);
98
99        // Send read command.
100        self.device
101            .write(DEVICE_I2C_ADDR, &[0x03, 0x00, 0x04])
102            .map_err(|_| Error::WriteError)?;
103        // Wait at least 1.5ms for the result.
104        self.delay.delay_us(1600);
105
106        // read out 8 bytes of result data
107        // byte 0: Should be Modbus function code 0x03
108        // byte 1: Should be number of registers to read (0x04)
109        // byte 2: Humidity msb
110        // byte 3: Humidity lsb
111        // byte 4: Temperature msb
112        // byte 5: Temperature lsb
113        // byte 6: CRC lsb byte
114        // byte 7: CRC msb byte
115        let mut data = [0; 8];
116        self.device
117            .read(DEVICE_I2C_ADDR, &mut data)
118            .map_err(|_| Error::ReadError)?;
119
120        // check that the operation was reported as succesful
121        if data[0] != 0x03 || data[1] != 0x04 {
122            return Err(Error::SensorError);
123        }
124
125        // CRC check
126        let crc = crc16(&data[0..6]);
127        if crc != u16::from_le_bytes([data[6], data[7]]) {
128            return Err(Error::SensorError);
129        }
130
131        let mut temperature = i16::from_be_bytes([data[4] & 0b0111_1111, data[5]]);
132        if data[4] & 0b1000_0000 != 0 {
133          temperature = -temperature;
134        }
135
136        let humidity = u16::from_be_bytes([data[2], data[3]]);
137
138        Ok(Measurement {
139            temperature: f32::from(temperature) / 10.0,
140            humidity: f32::from(humidity) / 10.0,
141        })
142    }
143}
144
145#[test]
146fn test_crc16() {
147    assert_eq!(crc16(&[]), 0xFFFF);
148    assert_eq!(crc16(&[0x03, 0x04, 0x02, 0x36, 0x0, 0xDB]), 0x0550);
149}