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                if let State::Sleep = self.state.get() {
86                    if let Err((_error, buffer)) = self.i2c.write(buffer, 1) {
87                        self.buffer.replace(buffer);
88                        self.i2c.disable();
89                    } else {
90                        self.state.set(State::InitiateReading);
91                    }
92                }
93            })
94            .ok_or(ErrorCode::BUSY)
95    }
96}
97
98impl<'a, I: I2CDevice> TemperatureDriver<'a> for Hs3003<'a, I> {
99    fn set_client(&self, client: &'a dyn TemperatureClient) {
100        self.temperature_client.set(client);
101    }
102
103    fn read_temperature(&self) -> Result<(), ErrorCode> {
104        self.pending_temperature.set(true);
105        if !self.pending_humidity.get() {
106            self.start_reading()
107        } else {
108            Ok(())
109        }
110    }
111}
112
113impl<'a, I: I2CDevice> HumidityDriver<'a> for Hs3003<'a, I> {
114    fn set_client(&self, client: &'a dyn HumidityClient) {
115        self.humidity_client.set(client);
116    }
117
118    fn read_humidity(&self) -> Result<(), ErrorCode> {
119        self.pending_humidity.set(true);
120        if !self.pending_temperature.get() {
121            self.start_reading()
122        } else {
123            Ok(())
124        }
125    }
126}
127
128#[derive(Clone, Copy, Debug)]
129enum State {
130    Sleep,
131    InitiateReading,
132    Read,
133}
134
135impl<I: I2CDevice> I2CClient for Hs3003<'_, I> {
136    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
137        if let Err(i2c_err) = status {
138            self.state.set(State::Sleep);
139            self.buffer.replace(buffer);
140            self.temperature_client
141                .map(|client| client.callback(Err(i2c_err.into())));
142            self.humidity_client.map(|client| client.callback(0));
143            return;
144        }
145
146        match self.state.get() {
147            State::InitiateReading => {
148                if let Err((i2c_err, buffer)) = self.i2c.read(buffer, 4) {
149                    self.state.set(State::Sleep);
150                    self.buffer.replace(buffer);
151                    self.temperature_client
152                        .map(|client| client.callback(Err(i2c_err.into())));
153                    self.humidity_client.map(|client| client.callback(0));
154                } else {
155                    self.state.set(State::Read);
156                }
157            }
158            State::Read => {
159                let humidity_raw = (((buffer[0] & 0x3F) as u16) << 8) | buffer[1] as u16;
160                let humidity = ((humidity_raw as f32 / ((1 << 14) - 1) as f32) * 100.0) as usize;
161
162                let temperature_raw = ((buffer[2] as u16) << 8) | (buffer[3] as u16 >> 2);
163                // This operation follows the datasheet specification except dividing by 10. If its not done,
164                // the returned value will be in the hundreds (220 instead of 22 degrees celsius).
165                let temperature = ((((temperature_raw as f32 / ((1 << 14) - 1) as f32) * 165.0)
166                    - 40.0)
167                    / 10.0) as i32;
168
169                self.buffer.replace(buffer);
170                self.i2c.disable();
171                if self.pending_temperature.get() {
172                    self.pending_temperature.set(false);
173                    self.temperature_client
174                        .map(|client| client.callback(Ok(temperature)));
175                }
176                if self.pending_humidity.get() {
177                    self.pending_humidity.set(false);
178                    self.humidity_client.map(|client| client.callback(humidity));
179                }
180
181                self.state.set(State::Sleep);
182            }
183            State::Sleep => {} // should never happen
184        }
185    }
186}