halsensordht

A Rust DHT driver using embedded-hal interfaces.

An interface to the DHT Digital Humidity and Temperature sensors.

Design

The design of this crate is inspired by: - The Adatfruit DHT cpp code - The dht-hal-drv crate - The dht-hal crate - The dht-sensor crate - The dht22_pi crate

All of these libraries and also this library are basically the same, only the interfaces are a little different.

The code has been tested with DHT22 sensor, a Raspberry Pi 3B, and the rppal.

Issues

This library makes use of the traits provided by the embedded-hal crate. Unfortunately, a lot of stuff is still missing in embedded-hal, such as traits for reconfigurable GPIOs and disabling interrupts. Therefore it should be expected that this library will be change over time.

Usage

To create a driver for the DHT sensor, the caller have to provide the following items: - a GPIO pin, which implements the InputPin and OutputPin traits of embedded-hal. Additionally, it should be able to reconfigure the pin (from output to input). Unfortunately, this is currently not part of embedded-hal, so the trait from this crate needs to be implemented. - a timer providing the DelayMs and DelayUs traits. - interrupts need to be suppressed while we are reading the signal line. This crate provides a simple trait InterruptCtrl for this purpose.

Example: Using the library on a Raspberry Pi

Implementing the GPIO interfaces.

You can use rppal to control the GPIO pin. However, a wrapper needs to be implemented because of the "orphan" rule for implementation of external traits for external structs.

``` extern crate rppal; use rppal::gpio::{Gpio, Mode, PullUpDown}; extern crate halsensordht; use halsensordht::{DHTSensor, SensorType};

struct MyPin(rppal::gpio::IoPin);

impl MyPin { pub fn new(pin: rppal::gpio::Pin) -> MyPin { MyPin(pin.into_io(Mode::Input)) } }

impl InputPin for MyPin { type Error = ::Error; fn ishigh(&self) -> Result::Error> { Ok(self.0.ishigh()) } fn islow(&self) -> Result::Error> { Ok(self.0.islow()) } }

impl OutputPin for MyPin { type Error = ::Error; fn sethigh(&mut self) -> Result<(), ::Error> { Ok(self.0.sethigh()) } fn setlow(&mut self) -> Result<(), ::Error> { Ok(self.0.setlow()) } }

impl halsensordht::IoPin for MyPin { fn setinputpullupmode(&mut self) { self.0.setmode(Mode::Input); self.0.setpullupdown(PullUpDown::PullUp); } fn setoutputmode(&mut self) { self.0.setmode(Mode::Output); } } ```

Implementing the Delay interfaces.

The DelayMs is no problem, but microsecond delay is. However, only need a a tiny delay, therefore we use write and read operation to produce such small delay.

``` use std::thread; use std::time::Duration; use rppal::gpio::{Gpio, Mode, PullUpDown};

use std::ptr::readvolatile; use std::ptr::writevolatile; struct MyTimer {}

impl DelayUs for MyTimer { fn delayus(&mut self, t:u16) { let mut i = 0; unsafe { while readvolatile(&mut i) < t { writevolatile(&mut i, readvolatile(&mut i) + 1); } } } }

impl DelayMs for MyTimer { fn delayms(&mut self, ms: u16) { thread::sleep(Duration::frommillis(ms.into())); } } ```

Disabling Interrupts.

You will use sched_setscheduler from the libc crate for this purpose. This is good enough for reading the sensor data.

``` extern crate libc; use libc::schedparam; use libc::schedsetscheduler; use libc::SCHEDFIFO; use libc::SCHEDOTHER;

struct MyInterruptCtrl {}

impl halsensordht::InterruptCtrl for MyInterruptCtrl { fn enable(&mut self) { unsafe { let param = schedparam { schedpriority: 32 }; let result = schedsetscheduler(0, SCHEDFIFO, &param);

        if result != 0 {
            panic!("Error setting priority, you may not have cap_sys_nice capability");
        }
    }
}
fn disable(&mut self) {
    unsafe {
        let param = sched_param { sched_priority: 0 };
        let result = sched_setscheduler(0, SCHED_OTHER, &param);

        if result != 0 {
            panic!("Error setting priority, you may not have cap_sys_nice capability");
        }
    }
}

} ```

Putting it all together

OK, finally we are done! Here is some example code for the main function. Keep in mind, that there should be a delay between two calls of the read function. You will not get a valid result every time, the function is called. But it should be good enough to monitor the temperature of your room.

``` fn main() { let pinnumber = 12; if let Ok(gpio) = Gpio::new() { if let Ok(pin) = gpio.get(pinnumber) { let mypin = MyPin::new(pin); let mytimer = MyTimer{}; let myinterrupt = MyInterruptCtrl{}; let mut sensor = DHTSensor::new(SensorType::DHT22, mypin, mytimer, myinterrupt);

        for _i in 0 .. 200 {
            if let Ok(r) = sensor.read() {
                println!("Temperature = {} / {} and humidity = {}",
                r.temperature_celsius(),
                r.temperature_fahrenheit(),
                r.humidity_percent());
            }
            thread::sleep(Duration::from_secs(10));
        }
    } else {
        println!("Error: Could not get the pin!")
    }
} else {
    println!("Error: Could not get the GPIOs!")
}

} ```

Dependencies

For this example, you need the libc crate, the rppal crate, the embedded-hal crate, and of cause this crate!

``` [dependencies] libc = "0.2.21"

[dependencies.halsensordht] path = "../halsensordht" features = ["floats"]

[dependencies.embedded-hal] version = "0.2.4" features = ["unproven"]

[dependencies.rppal] version = "0.11.3" features = ["hal", "hal-unproven"] ```