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