shtc1/
lib.rs

1//! Driver for Sensirion SHTC1 digital humidity sensor
2
3#![no_std]
4
5extern crate byteorder;
6extern crate embedded_hal;
7
8use byteorder::{ByteOrder, BigEndian};
9
10use embedded_hal::blocking::delay::DelayMs;
11use embedded_hal::blocking::i2c::{Read, Write, WriteRead};
12
13const CRC8_POLYNOMIAL: u8 = 0x31;
14const I2C_ADDRESS: u8 = 0x70;
15
16pub struct SHTC1<I2C, D> {
17    i2c: I2C,
18    delay: D,
19}
20
21impl<I2C, D, E> SHTC1<I2C, D>
22where
23    I2C: Read<Error = E> + Write<Error = E> + WriteRead<Error = E>,
24    D: DelayMs<u8>,
25{
26	/// Creates a new driver
27    pub fn new(i2c: I2C, delay: D) -> Self {
28        SHTC1 { i2c, delay }
29    }
30
31	/// Send an I2C command
32    fn command(&mut self, command: Command) -> Result<(), Error<E>> {
33        self.i2c
34            .write(I2C_ADDRESS, &command.value())
35            .map_err(Error::I2c)
36    }
37
38    /// Take a temperature and humidity measurement
39    pub fn measure(&mut self) -> Result<Measurement, Error<E>> {
40        let raw = self.measure_raw()?;
41        Ok(convert(&raw))
42    }
43
44    /// Take a temperature and humidity measurement
45    pub fn measure_raw(&mut self) -> Result<MeasurementRaw, Error<E>> {
46        self.command(Command::Measure(ClockStretch::Disabled, MeasurementOrder::TFirst))?;
47        self.delay.delay_ms(15);
48        let mut buf = [0; 6];
49        self.i2c.read(I2C_ADDRESS, &mut buf)
50                .map_err(Error::I2c)?;
51        self.validate_crc(&buf[0..3])?;
52        self.validate_crc(&buf[3..6])?;
53        let temperature = BigEndian::read_u16(&buf[0..2]);
54        let humidity = BigEndian::read_u16(&buf[3..5]);
55        Ok(MeasurementRaw{ temperature, humidity })
56    }
57
58    /// Read the ID register
59    pub fn read_id(&mut self) -> Result<u16, Error<E>> {
60        self.command(Command::ReadID)?;
61        let mut id_bytes = [0; 3];
62        self.i2c
63            .read(I2C_ADDRESS, &mut id_bytes)
64            .map_err(Error::I2c)?;
65        self.validate_crc(&id_bytes[0..3])?;
66        Ok(BigEndian::read_u16(&id_bytes[0..2]))
67    }
68
69    pub fn release(self) -> I2C {
70        self.i2c
71    }
72
73    pub fn reset(&mut self) -> Result<(), Error<E>> {
74        self.command(Command::SoftReset)?;
75        Ok(())
76    }
77
78    fn validate_crc(&self, data: &[u8]) -> Result<(), Error<E>> {
79        match crc8(data) {
80            0x00 => Ok(()),
81            _ => Err(Error::Crc),
82        }
83    }
84}
85
86/// Convert MeasurementRaw to Measurement
87pub fn convert(m: &MeasurementRaw) -> Measurement {
88    Measurement{
89        temperature: convert_temperature(m.temperature),
90        humidity: convert_humidity(m.humidity),
91    }
92}
93
94fn convert_temperature(raw: u16) -> i32 {
95    -4500 + (17500 * raw as i32) / 65535
96}
97
98fn convert_humidity(raw: u16) -> i32 {
99    (10000 * raw as i32) / 65535
100}
101
102fn crc8(data: &[u8]) -> u8 {
103    let mut crc: u8 = 0xff;
104    for byte in data {
105        crc ^= byte;
106        for _ in 0..8 {
107            if (crc & 0x80) > 0 {
108                crc = (crc << 1) ^ CRC8_POLYNOMIAL;
109            } else {
110                crc <<= 1;
111            }
112        }
113    }
114    crc
115}
116
117/// Errors
118#[derive(Debug)]
119pub enum Error<E> {
120    /// Wrong CRC
121    Crc,
122    /// I2C bus error
123    I2c(E),
124}
125
126enum Command {
127    Measure(ClockStretch, MeasurementOrder),
128    SoftReset,
129    ReadID,
130}
131
132#[allow(dead_code)]
133enum ClockStretch {
134    Enabled,
135    Disabled,
136}
137
138#[derive(Copy, Clone)]
139pub enum MeasurementOrder {
140    TFirst,
141    HFirst,
142}
143
144#[derive(Debug)]
145pub struct Measurement {
146    pub temperature: i32,
147    pub humidity: i32,
148}
149
150#[derive(Debug)]
151pub struct MeasurementRaw {
152    pub temperature: u16,
153    pub humidity: u16,
154}
155
156impl Command {
157    fn value(&self) -> [u8; 2] {
158        use ClockStretch::Enabled as CSEnabled;
159        use ClockStretch::Disabled as CSDisabled;
160        use MeasurementOrder::*;
161        match *self {
162            // 5.2 Measurement Commands
163            // Table 9
164            Command::Measure(CSEnabled,  TFirst) => [0x7Cu8, 0xA2u8],
165            Command::Measure(CSEnabled,  HFirst) => [0x5Cu8, 0x24u8],
166            Command::Measure(CSDisabled, TFirst) => [0x78u8, 0x66u8],
167            Command::Measure(CSDisabled, HFirst) => [0x58u8, 0xE0u8],
168
169            // 5.6 Soft Reset
170            // Table 10
171            Command::SoftReset => [0x80, 0x5D],
172
173            // 5.7 Read-out of ID register
174            // Table 11
175            Command::ReadID  => [0xEF, 0xC8],
176        }
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183    #[test]
184    fn crc() {
185        assert_eq!(crc8(&[0x00u8]), 0xAC);
186        assert_eq!(crc8(&[0xBEu8, 0xEFu8]), 0x92);
187    }
188}