#![no_std]
use core::marker::PhantomData;
use embedded_hal::{blocking::delay, digital::v2 as digital};
pub mod kind;
use self::kind::DhtKind;
pub type Dht11<P, T> = Dht<P, T, kind::Dht11>;
pub type Dht22<P, T> = Dht<P, T, kind::Dht22>;
#[derive(Debug)]
pub struct Dht<P, T, K> {
pin: P,
timer: T,
_kind: PhantomData<K>,
}
#[derive(Debug, Clone)]
pub struct Reading<K> {
rh_integral: u8,
rh_decimal: u8,
t_integral: u8,
t_decimal: u8,
_kind: PhantomData<fn(K)>,
}
#[derive(Eq, PartialEq, Debug)]
pub struct Error<I>(ErrorKind<I>);
#[derive(Eq, PartialEq, Debug)]
enum ErrorKind<I> {
Io(I),
Checksum { expected: u8, actual: u8 },
Timeout,
}
#[derive(Copy, Clone, Debug)]
struct Pulse {
lo: u8,
hi: u8,
}
impl<P, T, K, E> Dht<P, T, K>
where
P: digital::InputPin<Error = E> + digital::OutputPin<Error = E>,
K: DhtKind,
{
pub fn new(pin: P, timer: T) -> Self {
Self {
pin,
timer,
_kind: PhantomData,
}
}
}
impl<P, T, K, E> Dht<P, T, K>
where
P: digital::InputPin<Error = E> + digital::OutputPin<Error = E>,
T: delay::DelayUs<u16> + delay::DelayMs<u16>,
K: DhtKind,
{
#[inline(always)] fn read_pulse_us(&mut self, high: bool) -> Result<u8, ErrorKind<E>> {
for len in 0..=core::u8::MAX {
if self.pin.is_high()? != high {
return Ok(len);
}
self.timer.delay_us(1);
}
Err(ErrorKind::Timeout)
}
fn start_signal_blocking(&mut self) -> Result<(), ErrorKind<E>> {
self.pin.set_high()?;
self.timer.delay_ms(1);
self.pin.set_low()?;
self.timer.delay_us(K::START_DELAY_US);
self.pin.set_high()?;
self.timer.delay_us(40);
self.read_pulse_us(false)?;
self.read_pulse_us(true)?;
Ok(())
}
pub fn read_blocking(&mut self) -> Result<Reading<K>, Error<E>> {
self.start_signal_blocking().map_err(ErrorKind::from)?;
let mut pulses = [Pulse { lo: 0, hi: 0 }; 40];
for pulse in &mut pulses[..] {
pulse.lo = self.read_pulse_us(false)?;
pulse.hi = self.read_pulse_us(true)?;
}
Ok(Reading::from_pulses(&pulses)?)
}
}
impl<K: DhtKind> Reading<K> {
fn from_pulses<E>(pulses: &[Pulse; 40]) -> Result<Self, ErrorKind<E>> {
let mut bytes = [0u8; 5];
let mut chksum: u16 = 0;
for (i, pulses) in pulses.chunks(8).enumerate() {
let byte = &mut bytes[i];
for Pulse { lo, hi } in pulses {
*byte <<= 1;
if hi > lo {
*byte |= 1;
}
}
if i < 4 {
chksum += i as u16;
}
}
let expected = bytes[4];
let actual = chksum as u8;
if actual != expected {
return Err(ErrorKind::Checksum { actual, expected });
}
Ok(Self {
rh_integral: bytes[0],
rh_decimal: bytes[1],
t_integral: bytes[2],
t_decimal: bytes[3],
_kind: PhantomData,
})
}
pub fn temp_celcius(self) -> f32 {
K::temp_celcius(self.t_integral, self.t_decimal)
}
pub fn temp_fahrenheit(self) -> f32 {
celcius_to_fahrenheit(self.temp_celcius())
}
pub fn humidity_percent(self) -> f32 {
K::humidity_percent(self.rh_integral, self.rh_decimal)
}
}
impl<E> From<E> for ErrorKind<E> {
fn from(e: E) -> Self {
ErrorKind::Io(e)
}
}
impl<E> From<ErrorKind<E>> for Error<E> {
fn from(e: ErrorKind<E>) -> Self {
Self(e)
}
}
impl<E> Error<E> {
pub fn is_timeout(&self) -> bool {
match self.0 {
ErrorKind::Timeout => true,
_ => false,
}
}
pub fn is_io(&self) -> bool {
match self.0 {
ErrorKind::Io(_) => true,
_ => false,
}
}
pub fn is_checksum(&self) -> bool {
match self.0 {
ErrorKind::Checksum { .. } => true,
_ => false,
}
}
pub fn into_io(self) -> Option<E> {
match self.0 {
ErrorKind::Io(io) => Some(io),
_ => None,
}
}
}
fn celcius_to_fahrenheit(c: f32) -> f32 {
c * 1.8 + 32.0
}