1#![doc = include_str!("../README.md")]
2#![deny(unsafe_code)]
3#![cfg_attr(not(test), no_std)]
4
5use embedded_hal::{
6 delay::DelayNs,
7 digital::{InputPin, OutputPin},
8};
9use core::marker::PhantomData;
10
11#[cfg(feature = "use-dwt")]
12use fugit::HertzU32;
13
14#[cfg(feature = "use-dwt")]
15use cortex_m::peripheral::DWT;
16
17use crate::error::DHT11Error;
18
19#[allow(unused)]
21const TIMEOUT_US: u32 = 200;
22
23#[cfg(not(feature = "use-dwt"))]
24#[allow(unused)]
25const TIMEOUT_CYCLES: u32 = 2_000_000;
26
27pub mod error {
28 pub type Result<T, E> = core::result::Result<T, DHT11Error<E>>;
29
30 #[derive(Debug, Clone, Copy)]
31 #[cfg_attr(feature = "use-defmt", derive(defmt::Format))]
32 pub enum DHT11Error<E> {
33 NoResponse,
34 ChecksumMismatch,
35 PinError(E),
36 Timeout,
37 }
38}
39
40pub struct Idle;
42pub struct Uninitialized;
43pub struct Initialized;
44
45pub struct SensorValue {
46 pub integral: u8,
47 pub decimal: u8,
48}
49
50pub type Temperature = SensorValue;
51pub type Humidity = SensorValue;
52
53pub struct Measurement {
54 pub humidity: Humidity,
55 pub temperature: Temperature,
56 pub checksum: u8,
57}
58
59impl Measurement {
60 pub fn temp_as_f32(&self) -> f32 {
61 self.temperature.integral as f32 + (self.temperature.decimal as f32 / 10.0)
62 }
63
64 pub fn temp_as_fixed(&self) -> i16 {
65 (self.temperature.integral as i16 * 10) + (self.temperature.decimal as i16)
66 }
67
68 pub fn hum_as_f32(&self) -> f32 {
69 self.humidity.integral as f32 + (self.humidity.decimal as f32 / 10.0)
70 }
71
72 pub fn hum_as_fixed(&self) -> i16 {
73 (self.humidity.integral as i16 * 10) + (self.humidity.decimal as i16)
74 }
75}
76
77pub struct DHT11<PIN, STATE> {
78 pin: PIN,
79 _state: PhantomData<STATE>,
80 clock_info: u32,
81}
82
83impl<PIN, E, STATE> DHT11<PIN, STATE>
84where PIN: InputPin<Error = E> + OutputPin<Error = E>
85{
86 fn is_bit_high(&self, _low_duration: u32, high_duration: u32) -> bool {
87 #[cfg(feature = "use-dwt")]
88 {
89 let cycles_per_us = self.clock_info / 1_000_000;
90 high_duration > (40 * cycles_per_us)
91 }
92
93 #[cfg(not(feature = "use-dwt"))]
94 {
95 high_duration > _low_duration
96 }
97 }
98
99 fn wait_for_state(&mut self, target_high: bool, _delay: &mut impl DelayNs) -> error::Result<u32, E> {
101 #[cfg(feature = "use-dwt")]
102 {
103 let cycles_per_us = self.clock_info / 1_000_000;
104 let start = DWT::cycle_count();
105 while self.pin.is_low().map_err(DHT11Error::PinError)? == target_high {
107 if DWT::cycle_count().wrapping_sub(start) > (TIMEOUT_US * cycles_per_us) {
108 return Err(DHT11Error::Timeout);
109 }
110 }
111 Ok(DWT::cycle_count().wrapping_sub(start))
112 }
113
114 #[cfg(not(feature = "use-dwt"))]
115 {
116 let mut count: u32 = 0;
117 while self.pin.is_low().map_err(DHT11Error::PinError)? == target_high {
118 count += 1;
119 if count > TIMEOUT_CYCLES { return Err(DHT11Error::Timeout); }
120 }
121 Ok(count)
122 }
123 }
124
125 fn send_start_signal(&mut self, delay: &mut impl DelayNs) -> error::Result<(), E> {
127 self.pin.set_high().map_err(DHT11Error::PinError)?;
128 delay.delay_ms(1);
129
130 self.pin.set_low().map_err(DHT11Error::PinError)?;
132 delay.delay_ms(20);
133
134 self.pin.set_high().map_err(DHT11Error::PinError)?;
136
137 self.wait_for_state(false, delay)?;
139
140 Ok(())
141 }
142}
143
144impl<PIN, E> DHT11<PIN, Idle>
145where PIN: InputPin<Error = E> + OutputPin<Error = E>
146{
147 #[cfg(feature = "use-dwt")]
148 pub fn new(pin: PIN, clock_info: HertzU32) -> DHT11<PIN, Uninitialized> {
149 DHT11 {
150 pin,
151 _state: PhantomData,
152 clock_info: clock_info.to_Hz(),
153 }
154 }
155
156 #[cfg(not(feature = "use-dwt"))]
157 pub fn new(pin: PIN) -> DHT11<PIN, Uninitialized> {
158 DHT11 {
159 pin,
160 _state: PhantomData,
161 clock_info: 0,
162 }
163 }
164
165 #[cfg(feature = "use-dwt")]
168 pub fn new_and_initialized(pin: PIN, delay: &mut impl DelayNs, clock_info: HertzU32) -> error::Result<DHT11<PIN, Initialized>, E> {
169 let uninit = Self::new(pin, clock_info);
170
171 let init = uninit.initialize(delay)?;
172
173 delay.delay_ms(2_000);
174
175 Ok(init)
176 }
177
178 #[cfg(not(feature = "use-dwt"))]
181 pub fn new_and_initialized(pin: PIN, delay: &mut impl DelayNs) -> error::Result<DHT11<PIN, Initialized>, E> {
182 let uninit = Self::new(pin);
183
184 let init = uninit.initialize(delay)?;
185
186 delay.delay_ms(2_000);
187
188 Ok(init)
189 }
190}
191
192impl<PIN, E> DHT11<PIN, Uninitialized>
193where PIN: InputPin<Error = E> + OutputPin<Error = E>
194{
195 pub fn initialize(mut self, delay: &mut impl DelayNs) -> error::Result<DHT11<PIN, Initialized>, E> {
198 self.send_start_signal(delay)?;
200
201 Ok(DHT11 {
202 pin: self.pin,
203 _state: PhantomData,
204 clock_info: self.clock_info,
205 })
206 }
207}
208
209impl<PIN, E> DHT11<PIN, Initialized>
210where PIN: InputPin<Error = E> + OutputPin<Error = E>
211{
212 pub fn read_temp_and_hum(&mut self, delay: &mut impl DelayNs) -> error::Result<Measurement, E> {
214 self.send_start_signal(delay)?;
215
216 self.wait_for_state(true, delay)?; self.wait_for_state(false, delay)?; self.wait_for_state(true, delay)?;
222 self.wait_for_state(false, delay)?;
223
224 let mut data = [0u8; 5];
226 for i in 0..40 {
227 let low_duration = self.wait_for_state(true, delay)?;
229
230 let high_duration = self.wait_for_state(false, delay)?;
232
233 if self.is_bit_high(low_duration, high_duration) {
234 data[i / 8] |= 1 << (7 - (i % 8));
235 }
236 }
237
238 let checksum_calculated = data[0]
240 .wrapping_add(data[1])
241 .wrapping_add(data[2])
242 .wrapping_add(data[3]);
243
244 if checksum_calculated != data[4] { return Err(DHT11Error::ChecksumMismatch); }
245
246 Ok(Measurement {
248 humidity: Humidity {
249 integral: data[0],
250 decimal: data[1],
251 },
252 temperature: Temperature {
253 integral: data[2],
254 decimal: data[3],
255 },
256 checksum: checksum_calculated,
257 })
258 }
259}