capsules_extra/
hs3003.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 2023.
4
5//! Sensor Driver for the Renesas HS3003 Temperature/Humidity sensor
6//! using the I2C bus.
7//!
8//! <https://www.renesas.com/us/en/document/dst/hs300x-datasheet>
9//!
10//! > The HS300x (HS3001 and HS3003) series is a highly accurate,
11//! > fully calibrated relative humidity and temperature sensor.
12//! > The MEMS sensor features a proprietary sensor-level protection,
13//! > ensuring high reliability and long-term stability. The high
14//! > accuracy, fast measurement response time, and long-term stability
15//! > combined with the small package size makes the HS300x series ideal
16//! > for a wide number of applications ranging from portable devices to
17//! > products designed for harsh environments.
18//!
19//! Driver Semantics
20//! ----------------
21//!
22//! This driver exposes the HS3003's temperature and humidity functionality via
23//! the [TemperatureDriver] and [HumidityDriver] HIL interfaces. If the driver
24//! receives a request for either temperature or humidity while a request for the
25//! other is outstanding, both will be returned to their respective clients when
26//! the I2C transaction is completed, rather than performing two separate transactions.
27//!
28//! Limitations
29//! -----------
30//!
31//! The driver uses floating point math to adjust the readings. This must be
32//! done and macthes the chip's datasheet. This could decrease performance
33//! on platforms that don't have hardware support for floating point math.
34//!
35//! Usage
36//! -----
37//!
38//! ```rust,ignore
39//! # use kernel::static_init;
40//!
41//! let hs3003_i2c = static_init!(
42//!     capsules::virtual_i2c::I2CDevice,
43//!     capsules::virtual_i2c::I2CDevice::new(i2c_bus, 0x44));
44//! let hs3003 = static_init!(
45//!     capsules::hs3003::Hs3003<'static>,
46//!     capsules::hs3003::Hs3003::new(hs3003_i2c,
47//!         &mut capsules::hs3003::BUFFER));
48//! hs3003_i2c.set_client(hs3003);
49//! ```
50
51use core::cell::Cell;
52use kernel::hil::i2c::{self, I2CClient, I2CDevice};
53use kernel::hil::sensors::{HumidityClient, HumidityDriver, TemperatureClient, TemperatureDriver};
54use kernel::utilities::cells::{OptionalCell, TakeCell};
55use kernel::ErrorCode;
56
57pub struct Hs3003<'a, I: I2CDevice> {
58    buffer: TakeCell<'static, [u8]>,
59    i2c: &'a I,
60    temperature_client: OptionalCell<&'a dyn TemperatureClient>,
61    humidity_client: OptionalCell<&'a dyn HumidityClient>,
62    state: Cell<State>,
63    pending_temperature: Cell<bool>,
64    pending_humidity: Cell<bool>,
65}
66
67impl<'a, I: I2CDevice> Hs3003<'a, I> {
68    pub fn new(i2c: &'a I, buffer: &'static mut [u8]) -> Self {
69        Hs3003 {
70            buffer: TakeCell::new(buffer),
71            i2c,
72            temperature_client: OptionalCell::empty(),
73            humidity_client: OptionalCell::empty(),
74            state: Cell::new(State::Sleep),
75            pending_temperature: Cell::new(false),
76            pending_humidity: Cell::new(false),
77        }
78    }
79
80    pub fn start_reading(&self) -> Result<(), ErrorCode> {
81        self.buffer
82            .take()
83            .map(|buffer| {
84                self.i2c.enable();
85                match self.state.get() {
86                    State::Sleep => {
87                        if let Err((_error, buffer)) = self.i2c.write(buffer, 1) {
88                            self.buffer.replace(buffer);
89                            self.i2c.disable();
90                        } else {
91                            self.state.set(State::InitiateReading);
92                        }
93                    }
94                    _ => {}
95                }
96            })
97            .ok_or(ErrorCode::BUSY)
98    }
99}
100
101impl<'a, I: I2CDevice> TemperatureDriver<'a> for Hs3003<'a, I> {
102    fn set_client(&self, client: &'a dyn TemperatureClient) {
103        self.temperature_client.set(client);
104    }
105
106    fn read_temperature(&self) -> Result<(), ErrorCode> {
107        self.pending_temperature.set(true);
108        if !self.pending_humidity.get() {
109            self.start_reading()
110        } else {
111            Ok(())
112        }
113    }
114}
115
116impl<'a, I: I2CDevice> HumidityDriver<'a> for Hs3003<'a, I> {
117    fn set_client(&self, client: &'a dyn HumidityClient) {
118        self.humidity_client.set(client);
119    }
120
121    fn read_humidity(&self) -> Result<(), ErrorCode> {
122        self.pending_humidity.set(true);
123        if !self.pending_temperature.get() {
124            self.start_reading()
125        } else {
126            Ok(())
127        }
128    }
129}
130
131#[derive(Clone, Copy, Debug)]
132enum State {
133    Sleep,
134    InitiateReading,
135    Read,
136}
137
138impl<I: I2CDevice> I2CClient for Hs3003<'_, I> {
139    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
140        if let Err(i2c_err) = status {
141            self.state.set(State::Sleep);
142            self.buffer.replace(buffer);
143            self.temperature_client
144                .map(|client| client.callback(Err(i2c_err.into())));
145            self.humidity_client.map(|client| client.callback(0));
146            return;
147        }
148
149        match self.state.get() {
150            State::InitiateReading => {
151                if let Err((i2c_err, buffer)) = self.i2c.read(buffer, 4) {
152                    self.state.set(State::Sleep);
153                    self.buffer.replace(buffer);
154                    self.temperature_client
155                        .map(|client| client.callback(Err(i2c_err.into())));
156                    self.humidity_client.map(|client| client.callback(0));
157                } else {
158                    self.state.set(State::Read);
159                }
160            }
161            State::Read => {
162                let humidity_raw = (((buffer[0] & 0x3F) as u16) << 8) | buffer[1] as u16;
163                let humidity = ((humidity_raw as f32 / ((1 << 14) - 1) as f32) * 100.0) as usize;
164
165                let temperature_raw = ((buffer[2] as u16) << 8) | (buffer[3] as u16 >> 2);
166                // This operation follows the datasheet specification except dividing by 10. If its not done,
167                // the returned value will be in the hundreds (220 instead of 22 degrees celsius).
168                let temperature = ((((temperature_raw as f32 / ((1 << 14) - 1) as f32) * 165.0)
169                    - 40.0)
170                    / 10.0) as i32;
171
172                self.buffer.replace(buffer);
173                self.i2c.disable();
174                if self.pending_temperature.get() {
175                    self.pending_temperature.set(false);
176                    self.temperature_client
177                        .map(|client| client.callback(Ok(temperature)));
178                }
179                if self.pending_humidity.get() {
180                    self.pending_humidity.set(false);
181                    self.humidity_client.map(|client| client.callback(humidity));
182                }
183
184                self.state.set(State::Sleep);
185            }
186            State::Sleep => {} // should never happen
187        }
188    }
189}