#![no_std]
extern crate embedded_hal;
extern crate nb;
mod crc;
mod user_register;
pub use crate::user_register::{Resolution, SupplyVoltage, UserRegister};
use core::marker::PhantomData;
use core::slice;
use embedded_hal::blocking::i2c::{Read, Write, WriteRead};
use crate::crc::Crc;
const ADDRESS: u8 = 0x40;
mod sealed {
pub trait SealedFromRaw {
fn from_raw(raw: u16) -> Self;
}
}
use self::sealed::SealedFromRaw;
pub struct Htu2xd<I>(PhantomData<I>);
impl<I, E> Htu2xd<I>
where
I: Read<Error = E> + Write<Error = E> + WriteRead<Error = E>,
{
pub fn new() -> Self {
Htu2xd(PhantomData)
}
pub fn soft_reset(&mut self, i2c: &mut I) -> Result<(), E> {
i2c.write(ADDRESS, &[Command::SoftReset as u8])
}
pub fn read_humidity_blocking(&mut self, i2c: &mut I) -> Result<Reading<Humidity>, Error<E>> {
let mut buffer = [0u8; 3];
i2c.write_read(ADDRESS, &[Command::HumidityHoldMaster as u8], &mut buffer)?;
parse_and_check_reading(&buffer)
}
pub fn read_temperature_blocking(
&mut self,
i2c: &mut I,
) -> Result<Reading<Temperature>, Error<E>> {
let mut buffer = [0u8; 3];
i2c.write_read(
ADDRESS,
&[Command::TemperatureHoldMaster as u8],
&mut buffer,
)?;
parse_and_check_reading(&buffer)
}
pub fn read_humidity(&mut self, i2c: &mut I) -> Result<ResultReader<I, Humidity>, E> {
i2c.write(ADDRESS, &[Command::Humidity as u8])?;
Ok(ResultReader {
_driver: PhantomData,
_reading: PhantomData,
})
}
pub fn read_temperature(&mut self, i2c: &mut I) -> Result<ResultReader<I, Temperature>, E> {
i2c.write(ADDRESS, &[Command::Temperature as u8])?;
Ok(ResultReader {
_driver: PhantomData,
_reading: PhantomData,
})
}
pub fn read_user_register(&mut self, i2c: &mut I) -> Result<UserRegister, E> {
let mut register_value = 0u8;
i2c.write_read(
ADDRESS,
&[Command::ReadUser as u8],
slice::from_mut(&mut register_value),
)?;
Ok(UserRegister(register_value))
}
pub fn write_user_register(&mut self, i2c: &mut I, register: UserRegister) -> Result<(), E> {
i2c.write(ADDRESS, &[Command::WriteUser as u8, register.0])
}
}
impl<I, E> Default for Htu2xd<I>
where
I: Read<Error = E> + Write<Error = E> + WriteRead<Error = E>,
{
fn default() -> Self {
Htu2xd::new()
}
}
pub struct ResultReader<'h, I, M> {
_driver: PhantomData<&'h mut Htu2xd<I>>,
_reading: PhantomData<M>,
}
impl<'h, I, M> ResultReader<'h, I, M>
where
I: Read,
M: Measurement,
{
pub fn read_result<F>(
&mut self,
i2c: &mut I,
is_nak: F,
) -> nb::Result<Reading<M>, Error<I::Error>>
where
F: FnOnce(&I::Error) -> bool,
{
let mut buffer = [0u8; 3];
match i2c.read(ADDRESS, &mut buffer[..]) {
Ok(()) => parse_and_check_reading(&buffer).map_err(nb::Error::Other),
Err(e) => {
if is_nak(&e) {
Err(nb::Error::WouldBlock)
} else {
Err(nb::Error::Other(Error::I2c(e)))
}
}
}
}
}
fn parse_and_check_reading<M, E>(bytes: &[u8; 3]) -> Result<Reading<M>, Error<E>>
where
M: Measurement,
{
let mut crc = Crc::new();
crc.add_all(&*bytes);
if crc.value() != 0 {
return Err(Error::Crc);
}
let reading16 = (u16::from(bytes[0]) << 8) | u16::from(bytes[1]);
Ok(Reading::from_raw(reading16))
}
#[derive(Debug)]
pub enum Error<E> {
I2c(E),
Crc,
}
impl<E> From<E> for Error<E> {
fn from(inner: E) -> Self {
Error::I2c(inner)
}
}
#[derive(Debug, Clone)]
pub struct Temperature(u16);
impl Temperature {
pub fn as_raw(&self) -> u16 {
self.0
}
pub fn as_degrees_celsius(&self) -> f32 {
-46.85_f32 + 175.72_f32 / 65536.0_f32 * f32::from(self.0)
}
}
#[derive(Debug, Clone)]
pub struct Humidity(u16);
impl Humidity {
pub fn as_raw(&self) -> u16 {
self.0
}
pub fn as_percent_relative(&self) -> f32 {
-6.0_f32 + 125.0_f32 / 65536.0_f32 * f32::from(self.0)
}
}
pub trait Measurement: SealedFromRaw {}
impl SealedFromRaw for Temperature {
fn from_raw(raw: u16) -> Self {
Temperature(raw)
}
}
impl Measurement for Temperature {}
impl SealedFromRaw for Humidity {
fn from_raw(raw: u16) -> Self {
Humidity(raw)
}
}
impl Measurement for Humidity {}
#[derive(Debug, Clone)]
pub enum Reading<R> {
Ok(R),
ErrorLow,
ErrorHigh,
}
impl<R> Reading<R>
where
R: Measurement,
{
fn from_raw(raw: u16) -> Self {
match raw {
0x0000 => Reading::ErrorLow,
0xffff => Reading::ErrorHigh,
_ => {
Reading::Ok(R::from_raw(raw & 0xfffc))
}
}
}
}
enum Command {
TemperatureHoldMaster = 0xe3,
Temperature = 0xf3,
HumidityHoldMaster = 0xe5,
Humidity = 0xf5,
WriteUser = 0xe6,
ReadUser = 0xe7,
SoftReset = 0xfe,
}