embassy-dht-sensor 0.2.8

DHT sensor driver for the Embassy async runtime
use crate::DTHResponse;
use crate::DHTSensorError::InvalidData;
use crate::{
    DHTSensorError,
};
use cortex_m::interrupt::free;
use embassy_rp::gpio::Level::{High, Low};
use embassy_rp::gpio::{Flex, Level, Pull};
use embassy_time::{block_for, Duration};

const WAIT_FOR_READINESS_LEVEL_THRESHOLD: u32 = 80;
const LOW_LEVEL_THRESHOLD: u32 = 55;
const HIGH_LEVEL_THRESHOLD: u32 = 75;

pub struct DHTSensor<'a> {
    pin: Flex<'a>,
    last_response: Option<DTHResponse>,
    last_read_time: Option<embassy_time::Instant>,
}

impl<'a> DHTSensor<'a> {
    pub fn new(pin: Flex<'a>) -> Self {
        DHTSensor {
            pin,
            last_response: None,
            last_read_time: None,
        }
    }

    pub fn read(&mut self) -> Result<DTHResponse, DHTSensorError> {
        let now = embassy_time::Instant::now();
        if let Some(last_read_time) = self.last_read_time {
            if now - last_read_time < Duration::from_secs(crate::dht::MIN_REQUEST_INTERVAL_SECS) {
                if let Some(response) = &self.last_response {
                    return Ok(response.clone());
                }
            }
        }
        match self.read_raw_data() {
            Ok(data) => {
                let humidity_data: &[u16; 2] = &data[0..2].try_into().unwrap();
                let humidity = dht::humidity(humidity_data);
                let temperature_data: &[u16; 2] = &data[2..4].try_into().unwrap();
                let temperature = dht::temperature(temperature_data);
                if humidity <= 100.0 {
                    let response = DTHResponse {
                        humidity,
                        temperature,
                    };
                    self.last_response = Some(response.clone());
                    self.last_read_time = Some(embassy_time::Instant::now());
                    Ok(response)
                }
                else {
                    if let Some(response) = &self.last_response {
                        Ok(response.clone())
                    } else {
                        Err(InvalidData)
                    }
                }
            }
            Err(e) => {
                if let Some(response) = &self.last_response {
                    Ok(response.clone())
                } else {
                    Err(e)
                }
            }
        }
    }

    fn read_raw_data(&mut self) -> Result<[u16; 5], DHTSensorError> {
        let mut data: [u16; 5] = [0; 5];
        let mut all_bits_cycles: [u32; 80] = [0; 80];

        free(|_| {
            // Wake up the sensor
            self.pin.set_as_output();
            self.pin.set_low();
            block_for(Duration::from_micros(crate::dht::START_LOW_INTERVAL_US));

            // Ask for data
            self.pin.set_high();
            block_for(Duration::from_micros(25u64));

            self.pin.set_as_input();
            self.pin.set_pull(Pull::Up);
            block_for(Duration::from_micros(55u64));

            // Wait for DHT to signal data is ready (~80us low followed by ~80us high)
            _ = self.wait_while_level(Low, WAIT_FOR_READINESS_LEVEL_THRESHOLD);
            _ = self.wait_while_level(High, WAIT_FOR_READINESS_LEVEL_THRESHOLD);

            for bit in (0..80).step_by(2) {
                if let Ok(bit_cycles) = self.wait_while_level(Low, LOW_LEVEL_THRESHOLD) {
                    all_bits_cycles[bit] = bit_cycles;
                    if let Ok(bit_cycles) = self.wait_while_level(High, HIGH_LEVEL_THRESHOLD) {
                        all_bits_cycles[bit + 1] = bit_cycles;
                    }
                }
            }

            self.pin.set_as_output();
            self.pin.set_high();
            block_for(Duration::from_micros(1100u64));
        });

        for i in 0..40 {
            let low_cycles = all_bits_cycles[2 * i];
            let high_cycles = all_bits_cycles[2 * i + 1];
            if low_cycles < LOW_LEVEL_THRESHOLD || high_cycles < HIGH_LEVEL_THRESHOLD {
                data[i / 8] <<= 1;
                if high_cycles > low_cycles {
                    data[i / 8] |= 1;
                }
            } else {
                return Err(DHTSensorError::Timeout);
            }
        }
        let sum = data[0] + data[1] + data[2] + data[3];
        if data[4] == sum & 0x00FF {
            Ok(data)
        } else {
            Err(DHTSensorError::ChecksumError)
        }
    }

    // Wait for the pin to change from the specified level, or until the timeout is reached.
    fn wait_while_level(&mut self, level: Level, timeout_us: u32) -> Result<u32, DHTSensorError> {
        let mut elapsed_us = 0u32;
        while self.pin.get_level() == level && elapsed_us < timeout_us {
            block_for(Duration::from_micros(1u64));
            elapsed_us += 1;
        }
        if elapsed_us >= timeout_us {
            Err(DHTSensorError::Timeout)
        } else {
            Ok(elapsed_us)
        }
    }
}

#[cfg(feature = "dht2x")]
mod dht {
    pub(crate) fn humidity(data: &[u16; 2]) -> f32 {
        ((data[0] << 8) | data[1]) as f32 / 10.0
    }

    pub(crate) fn temperature(data: &[u16; 2]) -> f32 {
        let mut temperature = (((data[0] & 0x7F) << 8) | data[1]) as f32 / 10.0;
        if data[0] & 0x80 != 0 {
            temperature = -temperature;
        }
        temperature
    }
}


#[cfg(feature = "dht1x")]
mod dht {
    pub(crate) fn humidity(data: &[u16; 2]) -> f32 {
        data[0] as f32 + ((data[1] & 0x00FF) as f32 * 0.1)
    }

    pub(crate) fn temperature(data: &[u16; 2]) -> f32 {
        let mut temperature = data[0] as f32 + ((data[1] & 0x00FF) as f32 * 0.1);
        if data[1] & 0x8000 != 0 {
            temperature = -temperature;
        }
        temperature
    }
}