1use embedded_hal::{
2 delay::DelayNs,
3 digital::{InputPin, OutputPin},
4};
5
6use crate::error::DhtError;
7
8const TIMEOUT_US: u8 = 100;
12
13pub struct Dht22<PIN, D> {
15 pin: PIN,
16 delay: D,
17}
18
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[derive(Clone, Copy, Debug, PartialEq)]
22pub struct Reading {
23 pub temperature: f32,
25 pub relative_humidity: f32,
27}
28
29impl<PIN, DELAY, E> Dht22<PIN, DELAY>
30where
31 PIN: InputPin<Error = E> + OutputPin<Error = E>,
32 DELAY: DelayNs,
33{
34 pub fn new(pin: PIN, delay: DELAY) -> Self {
41 Dht22 { pin, delay }
42 }
43
44 pub fn read(&mut self) -> Result<Reading, DhtError<E>> {
55 self.start()?;
56
57 let mut data = [0; 4];
58
59 for b in data.iter_mut() {
60 *b = self.read_byte()?;
61 }
62
63 let checksum = self.read_byte()?;
64 if data.iter().fold(0u8, |sum, v| sum.wrapping_add(*v)) != checksum {
65 Err(DhtError::ChecksumMismatch)
66 } else {
67 Ok(self.parse_data(data))
68 }
69 }
70
71 fn parse_data(&self, data: [u8; 4]) -> Reading {
73 let [hum_hi, hum_lo, temp_hi, temp_lo] = data;
74
75 let joined_humidity = u16::from_be_bytes([hum_hi, hum_lo]);
76 let relative_humidity = joined_humidity as f32 / 10.0;
77
78 let is_temp_negative = (temp_hi >> 7) != 0;
79 let temp_hi = temp_hi & 0b0111_1111;
80 let joined_temp = u16::from_be_bytes([temp_hi, temp_lo]);
81 let mut temperature = joined_temp as f32 / 10.0;
82 if is_temp_negative {
83 temperature = -temperature;
84 }
85
86 Reading {
87 temperature,
88 relative_humidity,
89 }
90 }
91
92 fn start(&mut self) -> Result<(), DhtError<E>> {
98 self.pin.set_low()?;
100 self.delay.delay_ms(1);
101 self.pin.set_high()?;
102 self.delay.delay_us(40);
103
104 self.wait_for_low()?; self.wait_for_high()?; Ok(())
108 }
109
110 fn read_byte(&mut self) -> Result<u8, DhtError<E>> {
117 let mut byte: u8 = 0;
118
119 for i in 0..8 {
120 let bit_mask = 1 << (7 - i);
121 if self.read_bit()? {
122 byte |= bit_mask;
123 }
124 }
125
126 Ok(byte)
127 }
128
129 fn read_bit(&mut self) -> Result<bool, DhtError<E>> {
134 self.wait_for_low()?; self.wait_for_high()?;
139
140 self.delay.delay_us(35);
142
143 let bit_is_one = self.pin.is_high()?;
145 self.wait_for_low()?;
146
147 Ok(bit_is_one)
148 }
149
150 fn wait_for_high(&mut self) -> Result<(), DhtError<E>> {
152 Self::wait_for_state(&mut self.delay, || self.pin.is_high())
153 }
154
155 fn wait_for_low(&mut self) -> Result<(), DhtError<E>> {
157 Self::wait_for_state(&mut self.delay, || self.pin.is_low())
158 }
159
160 fn wait_for_state<F>(delay: &mut DELAY, mut condition: F) -> Result<(), DhtError<E>>
171 where
172 F: FnMut() -> Result<bool, E>,
173 {
174 for _ in 0..TIMEOUT_US {
175 if condition()? {
176 return Ok(());
177 }
178 delay.delay_us(1);
179 }
180 Err(DhtError::Timeout)
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use embedded_hal_mock::eh1::delay::CheckedDelay;
188 use embedded_hal_mock::eh1::delay::NoopDelay;
189 use embedded_hal_mock::eh1::delay::Transaction as DelayTx;
191 use embedded_hal_mock::eh1::digital::{
192 Mock as PinMock, State as PinState, Transaction as PinTx,
193 };
194
195 fn start_sequence() -> Vec<PinTx> {
196 vec![
197 PinTx::set(PinState::High), PinTx::set(PinState::Low),
200 PinTx::set(PinState::High),
201 PinTx::get(PinState::Low),
203 PinTx::get(PinState::High),
204 ]
205 }
206
207 fn encode_byte(byte: u8) -> Vec<PinTx> {
209 (0..8)
210 .flat_map(|i| {
211 let bit = (byte >> (7 - i)) & 1;
213 vec![
214 PinTx::get(PinState::Low), PinTx::get(PinState::High), PinTx::get(if bit == 1 {
217 PinState::High
219 } else {
220 PinState::Low
221 }),
222 PinTx::get(PinState::Low), ]
224 })
225 .collect()
226 }
227
228 #[test]
229 fn test_start_sequence() {
230 let mut expect = vec![];
231 expect.extend_from_slice(&start_sequence());
232
233 let mut pin = PinMock::new(&expect);
234 pin.set_high().unwrap();
235
236 let delay_transactions = vec![DelayTx::delay_ms(1), DelayTx::delay_us(40)];
237 let mut delay = CheckedDelay::new(&delay_transactions);
238
239 let mut dht = Dht22::new(pin.clone(), &mut delay);
240 dht.start().unwrap();
241
242 pin.done();
243 delay.done();
244 }
245
246 #[test]
247 fn test_wait_for_state() {
248 let mut expect = vec![];
249
250 expect.extend_from_slice(&[
251 PinTx::set(PinState::High),
253 PinTx::get(PinState::Low), PinTx::get(PinState::Low), PinTx::get(PinState::High),
257 PinTx::get(PinState::Low),
259 ]);
260
261 let mut pin = PinMock::new(&expect);
262 pin.set_high().unwrap();
263
264 let delay_transactions = vec![DelayTx::delay_us(1), DelayTx::delay_us(1)];
265 let mut delay = CheckedDelay::new(&delay_transactions);
266
267 let mut dht = Dht22::new(pin.clone(), &mut delay);
268 dht.wait_for_high().unwrap();
269 dht.wait_for_low().unwrap();
270
271 pin.done();
272 delay.done();
273 }
274
275 #[test]
276 fn test_read_bit_one() {
277 let mut pin = PinMock::new(&[
278 PinTx::get(PinState::Low), PinTx::get(PinState::High), PinTx::get(PinState::High), PinTx::get(PinState::Low), ]);
288
289 let delay_transactions = vec![
290 DelayTx::delay_us(35),
292 ];
293 let mut delay = CheckedDelay::new(&delay_transactions);
294
295 let mut dht = Dht22::new(pin.clone(), &mut delay);
296
297 let bit = dht.read_bit().unwrap();
298 assert!(bit);
299
300 pin.done();
301 delay.done();
302 }
303
304 #[test]
305 fn test_read_bit_zero() {
306 let mut pin = PinMock::new(&[
307 PinTx::get(PinState::High), PinTx::get(PinState::Low),
310 PinTx::get(PinState::Low), PinTx::get(PinState::High), PinTx::get(PinState::Low), PinTx::get(PinState::High), PinTx::get(PinState::Low), ]);
319
320 let delay_transactions = vec![
321 DelayTx::delay_us(1), DelayTx::delay_us(1), DelayTx::delay_us(35), DelayTx::delay_us(1), ];
326 let mut delay = CheckedDelay::new(&delay_transactions);
327
328 let mut dht = Dht22::new(pin.clone(), &mut delay);
329
330 let bit = dht.read_bit().unwrap();
331 assert!(!bit);
332
333 pin.done();
334 delay.done();
335 }
336
337 #[test]
338 fn test_read_timeout() {
339 let pin_expects: Vec<PinTx> = (0..100).map(|_| PinTx::get(PinState::High)).collect();
340 let mut pin = PinMock::new(&pin_expects);
341
342 let delay_expects: Vec<DelayTx> = (0..100).map(|_| DelayTx::delay_us(1)).collect();
343
344 let mut delay = CheckedDelay::new(&delay_expects);
345
346 let mut dht = Dht22::new(pin.clone(), &mut delay);
347
348 assert_eq!(dht.read_bit().unwrap_err(), DhtError::Timeout);
349
350 pin.done();
351 delay.done();
352 }
353
354 #[test]
355 fn test_parse_data_positive_temp() {
356 let mut pin = PinMock::new(&[]);
357
358 let dht = Dht22::new(pin.clone(), NoopDelay);
359 let data = [0x02, 0x2B, 0x00, 0xF6];
362
363 let reading = dht.parse_data(data);
364
365 assert_eq!(
366 reading,
367 Reading {
368 relative_humidity: 55.5,
369 temperature: 24.6,
370 }
371 );
372 pin.done();
373 }
374
375 #[test]
376 fn test_parse_data_negative_temp() {
377 let mut pin = PinMock::new(&[]);
378
379 let dht = Dht22::new(pin.clone(), NoopDelay);
380
381 let data = [0x01, 0x90, 0x80, 0x0A];
386
387 let reading = dht.parse_data(data);
388
389 assert_eq!(
390 reading,
391 Reading {
392 relative_humidity: 40.0,
393 temperature: -1.0,
394 }
395 );
396 pin.done();
397 }
398
399 #[test]
400 fn test_read_byte() {
401 let pin_states = encode_byte(0b10111010);
402
403 let mut pin = PinMock::new(&pin_states);
404 let delay_expects = vec![DelayTx::delay_us(35); 8];
405 let mut delay = CheckedDelay::new(&delay_expects);
406
407 let mut dht = Dht22::new(pin.clone(), &mut delay);
408 let byte = dht.read_byte().unwrap();
409 assert_eq!(byte, 0b10111010);
410
411 pin.done();
412 delay.done();
413 }
414
415 #[test]
416 fn test_read_valid() {
417 let mut pin_states = start_sequence();
421
422 let data_bytes = [0x01, 0x90, 0x00, 0xF6];
423 let checksum = 0x87;
424
425 for byte in data_bytes.iter().chain(std::iter::once(&checksum)) {
426 pin_states.extend(encode_byte(*byte));
427 }
428
429 let mut pin = PinMock::new(&pin_states);
430 pin.set_high().unwrap();
431
432 let mut delay_transactions = vec![DelayTx::delay_ms(1), DelayTx::delay_us(40)];
434 delay_transactions.extend(std::iter::repeat_n(DelayTx::delay_us(35), 40));
436
437 let mut delay = CheckedDelay::new(&delay_transactions);
438
439 let mut dht = Dht22::new(pin.clone(), &mut delay);
440 let reading = dht.read().unwrap();
441
442 assert_eq!(
443 reading,
444 Reading {
445 relative_humidity: 40.0,
446 temperature: 24.6,
447 }
448 );
449
450 pin.done();
451 delay.done();
452 }
453
454 #[test]
455 fn test_read_invalid() {
456 let mut pin_states = start_sequence();
460
461 let data_bytes = [0x01, 0x90, 0x00, 0xF6];
462 let checksum = 0x81; for byte in data_bytes.iter().chain(std::iter::once(&checksum)) {
465 pin_states.extend(encode_byte(*byte));
466 }
467
468 let mut pin = PinMock::new(&pin_states);
469 pin.set_high().unwrap();
470
471 let mut delay_transactions = vec![DelayTx::delay_ms(1), DelayTx::delay_us(40)];
473 delay_transactions.extend(std::iter::repeat_n(DelayTx::delay_us(35), 40));
475
476 let mut delay = CheckedDelay::new(&delay_transactions);
477
478 let mut dht = Dht22::new(pin.clone(), &mut delay);
479 assert_eq!(dht.read().unwrap_err(), DhtError::ChecksumMismatch);
480
481 pin.done();
482 delay.done();
483 }
484}