Skip to content

Commit 181d5f5

Browse files
committed
Initial commit
0 parents  commit 181d5f5

File tree

7 files changed

+264
-0
lines changed

7 files changed

+264
-0
lines changed

Diff for: .cargo/config

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.thumbv7em-none-eabihf]
2+
rustflags = ["-C", "link-arg=-Tlink.x"]

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target/
2+
Cargo.lock

Diff for: Cargo.toml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
authors = ["Pietro Lorefice <[email protected]>"]
3+
categories = ["embedded", "hardware-support", "no-std"]
4+
description = "Platform-agnostic Rust driver for the DHT11 temperature and humidity sensor."
5+
edition = "2018"
6+
keywords = ["embedded-hal-driver"]
7+
license = "MIT OR Apache-2.0"
8+
name = "dht11"
9+
readme = "README.md"
10+
repository = "https://github.com/plorefice/dht11-rs"
11+
version = "0.1.0"
12+
13+
[dependencies]
14+
embedded-hal = { version = "0.2", features = ["unproven"] }
15+
16+
[dev-dependencies]
17+
cortex-m = "0.6"
18+
cortex-m-rt = "0.6"
19+
cortex-m-semihosting = "0.3"
20+
panic-semihosting = "0.5"
21+
22+
[dev-dependencies.stm32f4xx-hal]
23+
version = "0.8"
24+
features = ["rt", "stm32f407"]
25+
26+
[profile.release]
27+
lto = true

Diff for: README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# dht11-rs
2+
3+
Platform-agnostic Rust driver for the DHT11 temperature and humidity sensor,
4+
using [`embedded-hal`](https://github.com/rust-embedded/embedded-hal) traits.
5+
6+
## Requirements
7+
8+
- Rust 1.43+
9+
10+
## Usage
11+
12+
Include library as a dependency in your Cargo.toml
13+
14+
```toml
15+
[dependencies]
16+
dht11 = "0.1.0"
17+
```
18+
19+
```rust
20+
use dht11::Dht11;
21+
22+
// Create an instance of the DHT11 device
23+
let mut dht11 = Dht11::new(pin);
24+
25+
// Perform a sensor reading
26+
let measurement = dht11.perform_measurement(&mut delay).unwrap();
27+
println!("{:?}", measurement);
28+
```
29+
30+
## Examples
31+
32+
See the [examples](examples/) directory for an example on how to use this crate on an STM32F407 MCU.
33+
34+
By default, semihosting is used to display the value of the readings, using OpenOCD or similar.

Diff for: examples/stm32f407.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![deny(unsafe_code)]
2+
#![no_main]
3+
#![no_std]
4+
5+
extern crate panic_semihosting;
6+
7+
use cortex_m_rt::entry;
8+
use cortex_m_semihosting::hprintln;
9+
use hal::{prelude::*, stm32};
10+
use stm32f4xx_hal as hal;
11+
12+
use dht11::Dht11;
13+
14+
#[entry]
15+
fn main() -> ! {
16+
let dp = stm32::Peripherals::take().unwrap();
17+
let cp = cortex_m::peripheral::Peripherals::take().unwrap();
18+
19+
// The DATA pin is connected to PE2.
20+
let gpio = dp.GPIOE.split();
21+
let pin = gpio.pe2.into_open_drain_output();
22+
23+
// Create a delay abstraction based on SysTick.
24+
// We are using the HSE oscillator here for accurate delays.
25+
let rcc = dp.RCC.constrain();
26+
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(168.mhz()).freeze();
27+
let mut delay = hal::delay::Delay::new(cp.SYST, clocks);
28+
29+
// Create an instance of our device
30+
let mut dht11 = Dht11::new(pin);
31+
32+
loop {
33+
hprintln!("{:?}", dht11.perform_measurement(&mut delay)).unwrap();
34+
delay.delay_ms(1000_u16);
35+
}
36+
}

Diff for: memory.x

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Memory layout for an STM32F407VG microcontroller
2+
MEMORY
3+
{
4+
FLASH : ORIGIN = 0x08000000, LENGTH = 1024K
5+
RAM : ORIGIN = 0x20000000, LENGTH = 128K
6+
}

Diff for: src/lib.rs

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//! Platform-agnostic Rust driver for the DHT11 temperature and humidity sensor,
2+
//! using [`embedded-hal`](https://github.com/rust-embedded/embedded-hal) traits.
3+
4+
#![deny(unsafe_code)]
5+
#![deny(missing_docs)]
6+
#![cfg_attr(not(test), no_std)]
7+
8+
use embedded_hal::{
9+
blocking::delay::{DelayMs, DelayUs},
10+
digital::v2::{InputPin, OutputPin},
11+
};
12+
13+
/// How long to wait for a pulse on the data line (in microseconds).
14+
const TIMEOUT_US: u16 = 1_000;
15+
16+
/// Error type for this crate.
17+
#[derive(Debug)]
18+
pub enum Error<E> {
19+
/// Timeout during communication.
20+
Timeout,
21+
/// CRC mismatch.
22+
CrcMismatch,
23+
/// GPIO error.
24+
Gpio(E),
25+
}
26+
27+
/// A DHT11 device.
28+
pub struct Dht11<GPIO> {
29+
/// The concrete GPIO pin implementation.
30+
gpio: GPIO,
31+
}
32+
33+
/// Results of a reading performed by the DHT11.
34+
#[derive(Debug)]
35+
pub struct Measurement {
36+
/// The measured temperature.
37+
pub temperature: f32,
38+
/// The measured humidity.
39+
pub humidity: f32,
40+
}
41+
42+
impl<GPIO, E> Dht11<GPIO>
43+
where
44+
GPIO: InputPin<Error = E> + OutputPin<Error = E>,
45+
{
46+
/// Creates a new DHT11 device connected to the specified pin.
47+
pub fn new(gpio: GPIO) -> Self {
48+
Dht11 { gpio }
49+
}
50+
51+
/// Destroys the driver, returning the GPIO instance.
52+
pub fn destroy(self) -> GPIO {
53+
self.gpio
54+
}
55+
56+
/// Performs a reading of the sensor.
57+
pub fn perform_measurement<D>(&mut self, delay: &mut D) -> Result<Measurement, Error<E>>
58+
where
59+
D: DelayUs<u16> + DelayMs<u16>,
60+
{
61+
let mut data = [0u8; 5];
62+
63+
// Perform initial handshake
64+
self.perform_handshake(delay)?;
65+
66+
// Read bits
67+
for i in 0..40 {
68+
data[i / 8] <<= 1;
69+
if self.read_bit(delay)? {
70+
data[i / 8] |= 1;
71+
}
72+
}
73+
74+
// Finally wait for line to go idle again.
75+
self.wait_for_pulse(true, delay)?;
76+
77+
// Check CRC
78+
let crc = data[0]
79+
.wrapping_add(data[1])
80+
.wrapping_add(data[2])
81+
.wrapping_add(data[3]);
82+
if crc != data[4] {
83+
return Err(Error::CrcMismatch);
84+
}
85+
86+
// Compute temperature
87+
let mut temp = (data[2] & 0x7f) as f32 + data[3] as f32 * 0.1;
88+
if data[2] & 0x80 != 0 {
89+
temp = -temp;
90+
}
91+
92+
Ok(Measurement {
93+
temperature: temp,
94+
humidity: data[0] as f32 + data[1] as f32 * 0.1,
95+
})
96+
}
97+
98+
fn perform_handshake<D>(&mut self, delay: &mut D) -> Result<(), Error<E>>
99+
where
100+
D: DelayUs<u16> + DelayMs<u16>,
101+
{
102+
// Set pin as floating to let pull-up raise the line and start the reading process.
103+
self.set_input()?;
104+
delay.delay_ms(1);
105+
106+
// Pull line low for at least 18ms to send a start command.
107+
self.set_low()?;
108+
delay.delay_ms(20);
109+
110+
// Restore floating
111+
self.set_input()?;
112+
delay.delay_us(40);
113+
114+
// As a response, the device pulls the line low for 80us and then high for 80us.
115+
self.read_bit(delay)?;
116+
117+
Ok(())
118+
}
119+
120+
fn read_bit<D>(&mut self, delay: &mut D) -> Result<bool, Error<E>>
121+
where
122+
D: DelayUs<u16> + DelayMs<u16>,
123+
{
124+
let low = self.wait_for_pulse(true, delay)?;
125+
let high = self.wait_for_pulse(false, delay)?;
126+
Ok(high > low)
127+
}
128+
129+
fn wait_for_pulse<D>(&mut self, level: bool, delay: &mut D) -> Result<u16, Error<E>>
130+
where
131+
D: DelayUs<u16> + DelayMs<u16>,
132+
{
133+
let mut count = 0;
134+
135+
while self.read_line()? != level {
136+
count += 1;
137+
if count > TIMEOUT_US {
138+
return Err(Error::Timeout);
139+
}
140+
delay.delay_us(1);
141+
}
142+
143+
Ok(count)
144+
}
145+
146+
fn set_input(&mut self) -> Result<(), Error<E>> {
147+
self.gpio.set_high().map_err(Error::Gpio)
148+
}
149+
150+
fn set_low(&mut self) -> Result<(), Error<E>> {
151+
self.gpio.set_low().map_err(Error::Gpio)
152+
}
153+
154+
fn read_line(&self) -> Result<bool, Error<E>> {
155+
self.gpio.is_high().map_err(Error::Gpio)
156+
}
157+
}

0 commit comments

Comments
 (0)