use embedded_hal::delay::DelayNs; use embedded_hal::i2c::{I2c, SevenBitAddress};
use crate::sensor_reading::SensorReading;
use crate::utils::{compute_crc8, convert_humidity, convert_temperature, extract_readings};
#[derive(Debug)]
pub enum DHT20Error<E> {
I2C(E),
CrcMismatch,
NotInitialized,
}
impl<E> From<E> for DHT20Error<E> {
fn from(err: E) -> Self {
DHT20Error::I2C(err)
}
}
#[repr(u8)] enum OpCode {
CheckStatus = 0x71,
TriggerMeasurement = 0xAC,
StatusReady = 0x80,
}
const I2C_ADDRESS: SevenBitAddress = 0x38; const RESET_REGISTERS: [u8; 3] = [0x1B, 0x1C, 0x1E];
pub struct Dht20<I2C> {
i2c: I2C,
address: SevenBitAddress,
initialized: bool,
}
impl<I2C, E> Dht20<I2C>
where
I2C: I2c<Error = E>,
{
pub fn new(i2c: I2C) -> Self {
Self {
i2c, address: I2C_ADDRESS,
initialized: false,
}
}
pub fn init<D: DelayNs>(&mut self, delay: &mut D) -> Result<(), DHT20Error<E>> {
delay.delay_ms(100);
self.check_init(delay)
}
pub fn take_reading<D: DelayNs>(
&mut self,
delay: &mut D,
) -> Result<SensorReading, DHT20Error<E>> {
if !self.initialized {
return Err(DHT20Error::NotInitialized);
}
self.trigger_measurement(delay)?;
self.wait_for_ready(delay)?;
let data = self.read_measurement()?;
let (raw_humidity, raw_temperature) = extract_readings(&data);
let humidity = convert_humidity(raw_humidity);
let temperature = convert_temperature(raw_temperature);
Ok(SensorReading::new(temperature, humidity))
}
pub fn read_raw<D: DelayNs>(&mut self, delay: &mut D) -> Result<[u8; 6], DHT20Error<E>> {
if !self.initialized {
return Err(DHT20Error::NotInitialized);
}
self.trigger_measurement(delay)?;
self.wait_for_ready(delay)?;
let data = self.read_measurement()?;
Ok(data)
}
fn check_init<D: DelayNs>(&mut self, delay: &mut D) -> Result<(), DHT20Error<E>> {
let mut buffer = [0u8; 1];
self.i2c
.write_read(self.address, &[OpCode::CheckStatus as u8], &mut buffer)?;
let status = buffer[0];
if (status & 0x18) != 0x18 {
for reg in RESET_REGISTERS.iter() {
self.reset_register(delay, *reg)?;
}
}
delay.delay_ms(10);
self.initialized = true;
Ok(())
}
fn reset_register<D: DelayNs>(&mut self, delay: &mut D, reg: u8) -> Result<(), DHT20Error<E>> {
let mut buffer = [0u8; 3];
self.i2c.write(self.address, &[reg, 0x00, 0x00])?;
delay.delay_ms(5);
self.i2c.write_read(self.address, &[reg], &mut buffer)?;
delay.delay_ms(5);
self.i2c
.write(self.address, &[0xB0 | reg, buffer[1], buffer[2]])?;
delay.delay_ms(5);
Ok(())
}
fn trigger_measurement<D: DelayNs>(&mut self, delay: &mut D) -> Result<(), DHT20Error<E>> {
let command = [OpCode::TriggerMeasurement as u8, 0x33, 0x00];
self.i2c.write(self.address, &command)?;
delay.delay_ms(80);
Ok(())
}
fn wait_for_ready<D: DelayNs>(&mut self, delay: &mut D) -> Result<(), DHT20Error<E>> {
let mut buffer = [0u8; 1];
loop {
self.i2c
.write_read(self.address, &[OpCode::CheckStatus as u8], &mut buffer)?;
if buffer[0] & (OpCode::StatusReady as u8) == 0 {
return Ok(()); }
delay.delay_ms(0);
}
}
fn read_measurement(&mut self) -> Result<[u8; 6], DHT20Error<E>> {
let mut buffer = [0u8; 7];
self.i2c.read(self.address, &mut buffer)?;
let crc = buffer[6];
let crc_check = compute_crc8(&buffer[..6]);
if crc != crc_check {
return Err(DHT20Error::CrcMismatch);
}
Ok(buffer[..6].try_into().unwrap()) }
}