capsules_extra/
sht3x.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! SyscallDriver for SHT3x Temperature and Humidity Sensor
6//!
7//! Author: Cosmin Daniel Radu <cosmindanielradu19@gmail.com>
8//!
9//!
10
11use core::cell::Cell;
12use enum_primitive::cast::FromPrimitive;
13use enum_primitive::enum_from_primitive;
14use kernel::hil::i2c;
15use kernel::hil::time::{self, Alarm, ConvertTicks};
16use kernel::utilities::cells::{OptionalCell, TakeCell};
17use kernel::ErrorCode;
18
19pub static BASE_ADDR: u8 = 0x44;
20
21enum_from_primitive! {
22    enum Registers {
23        /// Measurement High Repeatability with Clock Stretch Enabled
24        MEASHIGHREPSTRETCH = 0x2C06,
25        /// Measurement Medium Repeatability with Clock Stretch Enabled
26        MEASMEDREPSTRETCH = 0x2C0D,
27        /// Measurement Low Repeatability with Clock Stretch Enabled
28        MEASLOWREPSTRETCH = 0x2C10,
29        /// Measurement High Repeatability with Clock Stretch Disabled
30        MEASHIGHREP = 0x2400,
31        /// Measurement Medium Repeatability with Clock Stretch Disabled
32        MEASMEDREP = 0x240B,
33        /// Measurement Low Repeatability with Clock Stretch Disabled
34        MEASLOWREP = 0x2416,
35        /// Read Out of Status Register
36        READSTATUS = 0xF32D,
37        /// Clear Status
38        CLEARSTATUS = 0x3041,
39        /// Soft Reset
40        SOFTRESET = 0x30A2,
41        /// Heater Enable
42        HEATEREN = 0x306D,
43        /// Heater Disable
44        HEATERDIS = 0x3066,
45        /// Status Register Heater Bit
46        REGHEATERBIT = 0x0d,
47    }
48}
49
50#[derive(Clone, Copy, PartialEq)]
51enum State {
52    Idle,
53    Read,
54    ReadData,
55}
56
57fn crc8(data: &[u8]) -> u8 {
58    let polynomial = 0x31;
59    let mut crc = 0xff;
60
61    for x in 0..data.len() {
62        crc ^= data[x];
63        for _i in 0..8 {
64            if (crc & 0x80) != 0 {
65                crc = crc << 1 ^ polynomial;
66            } else {
67                crc <<= 1;
68            }
69        }
70    }
71    crc
72}
73
74pub struct SHT3x<'a, A: Alarm<'a>, I: i2c::I2CDevice> {
75    i2c: &'a I,
76    humidity_client: OptionalCell<&'a dyn kernel::hil::sensors::HumidityClient>,
77    temperature_client: OptionalCell<&'a dyn kernel::hil::sensors::TemperatureClient>,
78    state: Cell<State>,
79    buffer: TakeCell<'static, [u8]>,
80    read_temp: Cell<bool>,
81    read_hum: Cell<bool>,
82    alarm: &'a A,
83}
84
85impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> SHT3x<'a, A, I> {
86    pub fn new(i2c: &'a I, buffer: &'static mut [u8], alarm: &'a A) -> SHT3x<'a, A, I> {
87        SHT3x {
88            i2c,
89            humidity_client: OptionalCell::empty(),
90            temperature_client: OptionalCell::empty(),
91            state: Cell::new(State::Idle),
92            buffer: TakeCell::new(buffer),
93            read_temp: Cell::new(false),
94            read_hum: Cell::new(false),
95            alarm,
96        }
97    }
98
99    fn read_humidity(&self) -> Result<(), ErrorCode> {
100        if self.read_hum.get() {
101            Err(ErrorCode::BUSY)
102        } else {
103            if self.state.get() == State::Idle {
104                self.read_hum.set(true);
105                self.read_temp_hum()
106            } else {
107                self.read_hum.set(true);
108                Ok(())
109            }
110        }
111    }
112
113    fn read_temperature(&self) -> Result<(), ErrorCode> {
114        if self.read_temp.get() {
115            Err(ErrorCode::BUSY)
116        } else {
117            if self.state.get() == State::Idle {
118                self.read_temp.set(true);
119                self.read_temp_hum()
120            } else {
121                self.read_temp.set(true);
122                Ok(())
123            }
124        }
125    }
126
127    fn read_temp_hum(&self) -> Result<(), ErrorCode> {
128        self.buffer.take().map_or_else(
129            || panic!("SHT3x No buffer available!"),
130            |buffer| {
131                self.state.set(State::Read);
132                self.i2c.enable();
133
134                buffer[0] = ((Registers::MEASHIGHREP as u16) >> 8) as u8;
135                buffer[1] = ((Registers::MEASHIGHREP as u16) & 0xff) as u8;
136
137                // TODO verify errors
138                let _ = self.i2c.write(buffer, 2);
139
140                Ok(())
141            },
142        )
143    }
144}
145
146impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> time::AlarmClient for SHT3x<'a, A, I> {
147    fn alarm(&self) {
148        let state = self.state.get();
149        match state {
150            State::Read => {
151                self.state.set(State::ReadData);
152                self.buffer.take().map_or_else(
153                    || panic!("SHT3x No buffer available!"),
154                    |buffer| {
155                        let _res = self.i2c.read(buffer, 6);
156                    },
157                );
158            }
159            _ => {
160                // This should never happen
161                panic!("SHT31 Invalid alarm!");
162            }
163        }
164    }
165}
166
167impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> i2c::I2CClient for SHT3x<'a, A, I> {
168    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
169        match status {
170            Ok(()) => {
171                let state = self.state.get();
172
173                match state {
174                    State::ReadData => {
175                        if self.read_temp.get() {
176                            self.read_temp.set(false);
177                            if crc8(&buffer[0..2]) == buffer[2] {
178                                let mut stemp = buffer[0] as u32;
179                                stemp <<= 8;
180                                stemp |= buffer[1] as u32;
181                                let stemp = ((4375 * stemp) >> 14) as i32 - 4500;
182                                self.temperature_client.map(|cb| cb.callback(Ok(stemp)));
183                            } else {
184                                self.temperature_client
185                                    .map(|cb| cb.callback(Err(ErrorCode::FAIL)));
186                            }
187                        }
188                        if self.read_hum.get() {
189                            self.read_hum.set(false);
190                            if crc8(&buffer[3..5]) == buffer[5] {
191                                let mut shum = buffer[3] as u32;
192                                shum <<= 8;
193                                shum |= buffer[4] as u32;
194                                shum = (625 * shum) >> 12;
195                                self.humidity_client.map(|cb| cb.callback(shum as usize));
196                            } else {
197                                self.humidity_client.map(|cb| cb.callback(usize::MAX));
198                            }
199                        }
200                        self.buffer.replace(buffer);
201                        self.state.set(State::Idle);
202                    }
203                    State::Read => {
204                        self.buffer.replace(buffer);
205                        let interval = self.alarm.ticks_from_ms(20);
206                        self.alarm.set_alarm(self.alarm.now(), interval);
207                    }
208                    _ => {}
209                }
210            }
211            Err(i2c_err) => {
212                self.buffer.replace(buffer);
213                self.i2c.disable();
214                if self.read_temp.get() {
215                    self.read_temp.set(false);
216                    self.temperature_client
217                        .map(|cb| cb.callback(Err(i2c_err.into())));
218                }
219                if self.read_hum.get() {
220                    self.read_hum.set(false);
221                    self.humidity_client.map(|cb| cb.callback(usize::MAX));
222                }
223            }
224        }
225    }
226}
227
228impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> kernel::hil::sensors::HumidityDriver<'a>
229    for SHT3x<'a, A, I>
230{
231    fn set_client(&self, client: &'a dyn kernel::hil::sensors::HumidityClient) {
232        self.humidity_client.set(client);
233    }
234
235    fn read_humidity(&self) -> Result<(), ErrorCode> {
236        self.read_humidity()
237    }
238}
239
240impl<'a, A: Alarm<'a>, I: i2c::I2CDevice> kernel::hil::sensors::TemperatureDriver<'a>
241    for SHT3x<'a, A, I>
242{
243    fn set_client(&self, client: &'a dyn kernel::hil::sensors::TemperatureClient) {
244        self.temperature_client.set(client);
245    }
246
247    fn read_temperature(&self) -> Result<(), ErrorCode> {
248        self.read_temperature()
249    }
250}