hts221_async/
lib.rs

1#![no_std]
2#![feature(type_alias_impl_trait)]
3#![doc = include_str!("../README.md")]
4#![warn(missing_docs)]
5
6use core::fmt::{LowerHex, UpperHex};
7use core::{
8    fmt::{Debug, Display, Formatter},
9    marker::PhantomData,
10    ops::{Add, Div, Sub},
11};
12use embedded_hal_async::i2c::*;
13use register::{
14    calibration::*,
15    ctrl1::{BlockDataUpdate, Ctrl1, OutputDataRate},
16    ctrl2::Ctrl2,
17    ctrl3::Ctrl3,
18    h_out::Hout,
19    status::Status,
20    t_out::Tout,
21};
22
23mod register;
24
25const ADDR: u8 = 0x5F;
26
27/// Error returned by Hts221 driver
28pub enum Hts221Error<E> {
29    /// Error from I2C.
30    I2c(E),
31    /// Attempting to read before calibration.
32    NotCalibrated,
33    /// Not the expected sensor device
34    InvalidSensor,
35}
36
37/// An instance of the HTS221 driver using I2C transport from embedded-hal-async.
38pub struct Hts221<I>
39where
40    I: I2c<SevenBitAddress> + 'static,
41    <I as ErrorType>::Error: Send,
42{
43    i2c: I,
44    address: I2cAddress,
45    calibration: Option<Calibration>,
46}
47
48impl<I> Hts221<I>
49where
50    I: I2c<SevenBitAddress> + 'static,
51    <I as ErrorType>::Error: Send,
52{
53    /// Create a new instance of the driver using a given I2C peripheral.
54    pub fn new(i2c: I) -> Self {
55        Self {
56            i2c,
57            address: I2cAddress(ADDR),
58            calibration: None,
59        }
60    }
61
62    /// Initialize the driver. Must be run before reading sensor values.
63    pub async fn initialize(&mut self) -> Result<(), Hts221Error<I::Error>> {
64        Ctrl2::modify(self.address, &mut self.i2c, |reg| {
65            reg.boot();
66        })
67        .await?;
68
69        Ctrl1::modify(self.address, &mut self.i2c, |reg| {
70            reg.power_active()
71                .output_data_rate(OutputDataRate::Hz1)
72                .block_data_update(BlockDataUpdate::MsbLsbReading);
73        })
74        .await?;
75
76        Ctrl3::modify(self.address, &mut self.i2c, |reg| {
77            reg.enable(true);
78        })
79        .await?;
80
81        loop {
82            // Ensure status is emptied
83            if let Ok(status) = Status::read(self.address, &mut self.i2c).await {
84                if !status.any_available() {
85                    break;
86                }
87            }
88            Hout::read(self.address, &mut self.i2c).await?;
89            Tout::read(self.address, &mut self.i2c).await?;
90        }
91
92        self.calibration
93            .replace(Calibration::read(self.address, &mut self.i2c).await?);
94        Ok(())
95    }
96
97    /// Read sensor values from driver.
98    pub async fn read(&mut self) -> Result<SensorAcquisition<Celsius>, Hts221Error<I::Error>> {
99        if let Some(calibration) = &self.calibration {
100            let t_out = Tout::read(self.address, &mut self.i2c).await? as i16;
101            let temperature = calibration.calibrated_temperature(t_out);
102
103            let h_out = Hout::read(self.address, &mut self.i2c).await?;
104            let relative_humidity = calibration.calibrated_humidity(h_out);
105
106            Ok(SensorAcquisition {
107                temperature,
108                relative_humidity,
109            })
110        } else {
111            Err(Hts221Error::NotCalibrated)
112        }
113    }
114}
115
116impl<E> From<E> for Hts221Error<E>
117where
118    E: Send,
119{
120    fn from(e: E) -> Hts221Error<E> {
121        Hts221Error::I2c(e)
122    }
123}
124
125#[derive(Copy, Clone, Debug, Eq, PartialEq)]
126pub(crate) struct I2cAddress(u8);
127
128impl I2cAddress {
129    pub fn new(val: u8) -> Self {
130        Self(val)
131    }
132}
133
134impl Into<u8> for I2cAddress {
135    fn into(self) -> u8 {
136        self.0
137    }
138}
139
140impl Into<I2cAddress> for u8 {
141    fn into(self) -> I2cAddress {
142        I2cAddress::new(self)
143    }
144}
145
146impl LowerHex for I2cAddress {
147    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
148        LowerHex::fmt(&self.0, f)
149    }
150}
151
152impl UpperHex for I2cAddress {
153    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
154        UpperHex::fmt(&self.0, f)
155    }
156}
157
158/// Trait representing a temperature scale.
159pub trait TemperatureScale: Send {
160    /// Letter describing temperature
161    const LETTER: char;
162}
163
164/// Discriminant for the _Kelvin_ temperature scale.
165#[derive(Clone)]
166pub struct Kelvin;
167
168impl TemperatureScale for Kelvin {
169    const LETTER: char = 'K';
170}
171
172impl Debug for Kelvin {
173    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
174        f.write_str("°K")
175    }
176}
177
178#[cfg(feature = "defmt")]
179impl defmt::Format for Kelvin {
180    fn format(&self, f: defmt::Formatter<'_>) {
181        defmt::write!(f, "°K");
182    }
183}
184
185/// Discriminant for the _Celsius_ temperature scale.
186#[derive(Clone)]
187pub struct Celsius;
188
189impl Debug for Celsius {
190    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
191        f.write_str("°C")
192    }
193}
194
195#[cfg(feature = "defmt")]
196impl defmt::Format for Celsius {
197    fn format(&self, f: defmt::Formatter<'_>) {
198        defmt::write!(f, "°C");
199    }
200}
201
202impl TemperatureScale for Celsius {
203    const LETTER: char = 'C';
204}
205
206/// Discriminant for the _Fahrenheit_ temperature scale.
207#[derive(Clone)]
208pub struct Fahrenheit;
209
210impl Debug for Fahrenheit {
211    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
212        f.write_str("°F")
213    }
214}
215
216#[cfg(feature = "defmt")]
217impl defmt::Format for Fahrenheit {
218    fn format(&self, f: defmt::Formatter<'_>) {
219        defmt::write!(f, "°F");
220    }
221}
222
223impl TemperatureScale for Fahrenheit {
224    const LETTER: char = 'F';
225}
226
227/// A temperature value with its associated scale.
228pub struct Temperature<S: TemperatureScale> {
229    value: f32,
230    _marker: PhantomData<S>,
231}
232
233impl<S: TemperatureScale> Clone for Temperature<S> {
234    fn clone(&self) -> Self {
235        Self {
236            value: self.value,
237            _marker: PhantomData::default(),
238        }
239    }
240}
241
242impl<S: TemperatureScale> Debug for Temperature<S> {
243    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
244        write!(f, "{}°{}", &self.value, S::LETTER)
245    }
246}
247
248#[cfg(feature = "defmt")]
249impl<S: TemperatureScale> defmt::Format for Temperature<S> {
250    fn format(&self, f: defmt::Formatter<'_>) {
251        defmt::write!(f, "{}°{}", &self.value, S::LETTER)
252    }
253}
254
255impl<S: TemperatureScale> Copy for Temperature<S> {}
256
257impl<S: TemperatureScale> Temperature<S> {
258    fn new(value: f32) -> Self {
259        Self {
260            value,
261            _marker: PhantomData::default(),
262        }
263    }
264
265    /// Read the raw value.
266    pub fn raw_value(&self) -> f32 {
267        self.value
268    }
269}
270
271impl Temperature<Celsius> {
272    /// Convert celsius into fahrenheit
273    pub fn into_fahrenheit(self) -> Temperature<Fahrenheit> {
274        Temperature::new((self.value * 9.0 / 5.0) + 32.0)
275    }
276}
277
278impl Into<Temperature<Celsius>> for i16 {
279    fn into(self) -> Temperature<Celsius> {
280        Temperature::<Celsius>::new(self as f32)
281    }
282}
283
284impl Into<Temperature<Celsius>> for f32 {
285    fn into(self) -> Temperature<Celsius> {
286        Temperature::<Celsius>::new(self as f32)
287    }
288}
289
290impl<S: TemperatureScale> Sub for Temperature<S> {
291    type Output = Self;
292
293    fn sub(self, rhs: Self) -> Self::Output {
294        Self::new(self.value - rhs.value)
295    }
296}
297
298impl<S: TemperatureScale> Add for Temperature<S> {
299    type Output = Self;
300
301    fn add(self, rhs: Self) -> Self::Output {
302        Self::new(self.value + rhs.value)
303    }
304}
305
306impl<S: TemperatureScale> Add<f32> for Temperature<S> {
307    type Output = Self;
308
309    fn add(self, rhs: f32) -> Self::Output {
310        Self::new(self.value + rhs)
311    }
312}
313
314impl<S: TemperatureScale> Div<f32> for Temperature<S> {
315    type Output = f32;
316
317    fn div(self, rhs: f32) -> Self::Output {
318        self.value / rhs
319    }
320}
321
322impl<S: TemperatureScale> Display for Temperature<S> {
323    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
324        Display::fmt(&self.value, f)?;
325        write!(f, "°{}", S::LETTER)
326    }
327}
328
329/// Values read from the driver with the given scale.
330#[derive(Copy, Clone)]
331pub struct SensorAcquisition<S: TemperatureScale> {
332    /// Sensor temperature value.
333    pub temperature: Temperature<S>,
334    /// Relative humidity.
335    pub relative_humidity: f32,
336}
337
338impl<S: TemperatureScale> Debug for SensorAcquisition<S> {
339    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
340        f.debug_struct("SensorAcquisition")
341            .field("temperature", &self.temperature)
342            .field("relative_humidity", &self.relative_humidity)
343            .finish()
344    }
345}
346
347#[cfg(feature = "defmt")]
348impl<S: TemperatureScale> defmt::Format for SensorAcquisition<S> {
349    fn format(&self, f: defmt::Formatter<'_>) {
350        defmt::write!(
351            f,
352            "SensorAcquisition(temperature: {}, relative_humidity: {})",
353            &self.temperature,
354            &self.relative_humidity
355        );
356    }
357}