#![no_std]
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![deny(warnings)]
use embedded_hal::delay::DelayNs;
use embedded_hal::i2c::I2c;
pub const HS3003_I2C_ADDRESS: u8 = 0x44;
const MEASUREMENT_TIME_US: u32 = 100_000;
#[derive(Debug)]
pub struct Hs3003<I2C> {
i2c: I2C,
address: u8,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Measurement {
pub temperature: f32,
pub humidity: f32,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Error<E> {
I2c(E),
}
impl<E> From<E> for Error<E> {
fn from(error: E) -> Self {
Error::I2c(error)
}
}
impl<I2C, E> Hs3003<I2C>
where
I2C: I2c<Error = E>,
{
pub fn new(i2c: I2C) -> Self {
Self::new_with_address(i2c, HS3003_I2C_ADDRESS)
}
pub fn new_with_address(i2c: I2C, address: u8) -> Self {
Self { i2c, address }
}
pub fn read<D>(&mut self, delay: &mut D) -> Result<Measurement, Error<E>>
where
D: DelayNs,
{
self.i2c.write(self.address, &[0x00])?;
delay.delay_us(MEASUREMENT_TIME_US);
let mut buffer = [0u8; 4];
self.i2c.read(self.address, &mut buffer)?;
Ok(Self::parse_measurement(&buffer))
}
pub fn destroy(self) -> I2C {
self.i2c
}
}
impl<I2C> Hs3003<I2C> {
fn parse_measurement(data: &[u8; 4]) -> Measurement {
let humidity_raw = u16::from_be_bytes([data[0] & 0x3F, data[1]]);
let humidity = (f32::from(humidity_raw) / 16383.0) * 100.0;
let temp_raw = u16::from_be_bytes([data[2], data[3]]) >> 2;
let temperature = ((f32::from(temp_raw) / 16383.0) * 165.0) - 40.0;
Measurement {
temperature,
humidity,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_measurement_typical() {
let data = [
0x1F, 0xFF, 0x66, 0x64, ];
let measurement = Hs3003::<()>::parse_measurement(&data);
assert!((measurement.humidity - 50.0).abs() < 0.1);
assert!(
(measurement.temperature - 26.0).abs() < 0.5,
"Temperature was {}",
measurement.temperature
);
}
#[test]
fn test_parse_measurement_min_max() {
let data_min = [0x00, 0x00, 0x00, 0x00];
let measurement_min = Hs3003::<()>::parse_measurement(&data_min);
assert!((measurement_min.humidity - 0.0).abs() < 0.1);
assert!((measurement_min.temperature - (-40.0)).abs() < 0.5);
let data_max = [0xFF, 0xFF, 0xFF, 0xFC];
let measurement_max = Hs3003::<()>::parse_measurement(&data_max);
assert!((measurement_max.humidity - 100.0).abs() < 0.1);
assert!((measurement_max.temperature - 125.0).abs() < 0.5);
}
#[test]
fn test_default_address() {
assert_eq!(HS3003_I2C_ADDRESS, 0x44);
}
}