use async_trait::async_trait;
use rppal::gpio::{Gpio, Level, Mode};
use std::time::{Duration, Instant};
use tokio::task;
use crate::error::SensorError;
use crate::sensors::traits::TemperatureSensor;
#[derive(Debug, Clone, Copy)]
pub struct Dht11Data {
pub temperature: f32,
pub humidity: f32,
}
pub struct Dht11Sensor {
gpio_pin: u8,
}
impl Dht11Sensor {
pub fn new(pin: u8) -> Self {
Dht11Sensor { gpio_pin: pin }
}
fn read_internal(&self) -> Result<Dht11Data, SensorError> {
let gpio = Gpio::new()?;
let mut pin = gpio.get(self.gpio_pin)?.into_io(Mode::Output);
pin.write(Level::Low);
std::thread::sleep(Duration::from_millis(20)); pin.write(Level::High);
pin.set_mode(Mode::Input);
let timeout = Instant::now() + Duration::from_millis(100);
while pin.read() == Level::High {
if Instant::now() > timeout {
return Err(SensorError::Timeout(
"Waiting for DHT11 response timed out".into(),
));
}
}
while pin.read() == Level::Low {
if Instant::now() > timeout {
return Err(SensorError::Timeout(
"DHT11 response signal timed out".into(),
));
}
}
while pin.read() == Level::High {
if Instant::now() > timeout {
return Err(SensorError::Timeout("DHT11 ready signal timed out".into()));
}
}
let mut data = [0u8; 5];
for byte in data.iter_mut() {
for j in 0..8 {
while pin.read() == Level::Low {
if Instant::now() > timeout {
return Err(SensorError::Timeout(
"Timed out while reading data bit".into(),
));
}
}
let start = Instant::now();
while pin.read() == Level::High {
if Instant::now() > timeout {
return Err(SensorError::Timeout(
"Timed out during high level data bit reading".into(),
));
}
}
let duration = start.elapsed();
if duration > Duration::from_micros(40) {
*byte |= 1 << (7 - j);
}
}
}
if data[4] != (data[0] + data[1] + data[2] + data[3]) {
return Err(SensorError::DataValidation("Checksum error".into()));
}
let humidity = data[0] as f32;
let temperature = data[2] as f32;
Ok(Dht11Data {
temperature,
humidity,
})
}
}
#[async_trait]
impl TemperatureSensor for Dht11Sensor {
fn read(&self) -> Result<Dht11Data, SensorError> {
self.read_internal()
}
async fn read_async(&self) -> Result<Dht11Data, SensorError> {
let pin = self.gpio_pin;
task::spawn_blocking(move || {
let sensor = Dht11Sensor::new(pin);
sensor.read()
})
.await
.map_err(|e| SensorError::SensorError(format!("Task join error: {}", e)))?
}
}