1#![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 pub fn new(i2c: I2C, delay: D) -> Self {
28 SHTC1 { i2c, delay }
29 }
30
31 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 pub fn measure(&mut self) -> Result<Measurement, Error<E>> {
40 let raw = self.measure_raw()?;
41 Ok(convert(&raw))
42 }
43
44 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 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
86pub 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#[derive(Debug)]
119pub enum Error<E> {
120 Crc,
122 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 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 Command::SoftReset => [0x80, 0x5D],
172
173 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}