#![no_std] #![no_main] /// Represents a reading from the sensor. pub struct SensorReading { pub humidity: T, pub temperature: T, } /// Possible errors when interacting with the sensor. #[derive(Debug)] pub enum SensorError { ChecksumMismatch, Timeout, PinError, } pub struct Dht20 { pub i2c: I, pub delay: D, } impl Dht20 { const SENSOR_ADDRESS: u8 = 0x38; pub fn new(i2c: I, delay: D) -> Self { Self { i2c, delay } } pub fn read(&mut self) -> Result, SensorError> { // Check status let mut status_response: [u8; 1] = [0; 1]; let _ = self .i2c .write_read(Self::SENSOR_ADDRESS, &[0x71], &mut status_response); // Calibration if needed if status_response[0] & 0x18 != 0x18 { let _ = self.i2c.write(Self::SENSOR_ADDRESS, &[0x1B, 0, 0]); let _ = self.i2c.write(Self::SENSOR_ADDRESS, &[0x1C, 0, 0]); let _ = self.i2c.write(Self::SENSOR_ADDRESS, &[0x1E, 0, 0]); } // Trigger the measurement self.delay.delay_ms(10); let _ = self.i2c.write(Self::SENSOR_ADDRESS, &[0xAC, 0x33, 0x00]); // Read the measurement status self.delay.delay_ms(80); loop { let mut measurement_status_response: [u8; 1] = [0; 1]; let _ = self .i2c .read(Self::SENSOR_ADDRESS, &mut measurement_status_response); let status_word = measurement_status_response[0]; if status_word & 0b1000_0000 == 0 { break; } self.delay.delay_ms(1); } // Read the measurement (1 status + 5 data + 1 crc) let mut measurement_response: [u8; 7] = [0; 7]; let _ = self .i2c .read(Self::SENSOR_ADDRESS, &mut measurement_response); // Humidity 20 bits (8 + 8 + 4) let mut raw_humidity = measurement_response[1] as u32; raw_humidity = (raw_humidity << 8) + measurement_response[2] as u32; raw_humidity = (raw_humidity << 4) + (measurement_response[3] >> 4) as u32; let humidity_percentage = (raw_humidity as f32 / ((1 << 20) as f32)) * 100.0; // Temperature 20 bits let mut raw_temperature = (measurement_response[3] & 0b1111) as u32; raw_temperature = (raw_temperature << 8) + measurement_response[4] as u32; raw_temperature = (raw_temperature << 8) + measurement_response[5] as u32; let temperature_percentage = (raw_temperature as f32 / ((1 << 20) as f32)) * 200.0 - 50.0; // Compare the calculated CRC with the received CRC let data = &measurement_response[..6]; let received_crc = measurement_response[6]; let calculated_crc = Self::calculate_crc(data); if received_crc != calculated_crc { return Err(SensorError::ChecksumMismatch); } Ok(SensorReading { humidity: humidity_percentage, temperature: temperature_percentage, }) } fn calculate_crc(data: &[u8]) -> u8 { let polynomial = 0x31u8; // x^8 + x^5 + x^4 + 1 let mut crc = 0xFFu8; for &byte in data { crc ^= byte; // CRC8 - process every bit for _ in 0..8 { if crc & 0x80 != 0 { crc = (crc << 1) ^ polynomial; } else { crc <<= 1; } } } crc } } use embedded_hal::{delay::DelayNs, i2c::I2c as I2cEmbedded}; use esp_backtrace as _; use esp_hal::{ clock::CpuClock, delay::Delay, gpio::{Level, Pull}, i2c::master::I2c, xtensa_lx_rt::entry, }; use fugit::{ExtU64, HertzU32}; #[entry] fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); esp_println::logger::init_logger_from_env(); let mut delay = Delay::new(); // TODO: Remove unwrap let i2c_for_dht20 = esp_hal::i2c::master::I2c::new(peripherals.I2C0, esp_hal::i2c::master::Config::default()) .unwrap() .with_sda(peripherals.pins.gpio21) .with_scl(peripherals.pins.gpio22); let mut dht20 = Dht20::new(i2c_for_dht20, delay); loop { delay.delay_ms(5000); match dht20.read() { Ok(sensor_reading) => log::info!( "DHT 20 Sensor - Temperature: {} °C, humidity: {} %", sensor_reading.temperature, sensor_reading.humidity ), Err(error) => log::error!("An error occurred while trying to read sensor: {:?}", error), } log::info!("-----"); } }