Contiki-NG
dht11-sensor.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2021 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * 3. Neither the name of the copyright holder nor the names of its
15  * contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*---------------------------------------------------------------------------*/
32 
33 /**
34  * \addtogroup dht11-sensor
35  * @{
36  * \file
37  * DHT 11 sensor implementation
38  *
39  * \see https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
40  *
41  * \author
42  * Yago Fontoura do Rosario <yago.rosario@hotmail.com.br
43  */
44 
45 #include "contiki.h"
46 #include "dht11-sensor.h"
47 #include <string.h>
48 #include "dev/gpio-hal.h"
49 
50 /*---------------------------------------------------------------------------*/
51 /**
52  * @brief GPIO High
53  *
54  */
55 #define DHT11_SIGNAL_HIGH (1)
56 
57 /**
58  * @brief GPIO Low
59  *
60  */
61 #define DHT11_SIGNAL_LOW (0)
62 
63 /**
64  * @brief Duration of signal start phase 1 according to data sheet
65  *
66  */
67 #define DHT11_SIGNAL_START_PHASE1_DURATION (40)
68 
69 /**
70  * @brief Duration of signal start phase 2 according to data sheet
71  *
72  */
73 #define DHT11_SIGNAL_START_PHASE2_DURATION (80)
74 
75 /**
76  * @brief Duration of signal start phase 3 according to data sheet
77  *
78  */
79 #define DHT11_SIGNAL_START_PHASE3_DURATION (80)
80 
81 /**
82  * @brief Duration of signal response phase 1 according to data sheet
83  *
84  */
85 #define DHT11_SIGNAL_RESPONSE_PHASE1_DURATION (50)
86 
87 /**
88  * @brief Duration of signal response if bit is set to 0, according to data sheet
89  *
90  */
91 #define DHT11_SIGNAL_RESPONSE_BIT_0_DURATION (28)
92 
93 /**
94  * @brief Duration of signal response if bit is set to 1, according to data sheet
95  *
96  */
97 #define DHT11_SIGNAL_RESPONSE_BIT_1_DURATION (70)
98 
99 /**
100  * @brief Sensor timer drift in ticks
101  *
102  * DHT uses 1us granularity and rtimer granularity is higher.
103  * So, allow the reading to drift by 1 tick
104  *
105  */
106 #define DHT11_TICKS_GUARD (1)
107 
108 /**
109  * @brief Sensor timer drift in us from rtimer
110  *
111  * DHT uses 1us granularity and rtimer granularity is higher.
112  * So, allow the reading to drift by 1 tick in us
113  *
114  */
115 #define DHT11_US_GUARD RTIMERTICKS_TO_US(1)
116 
117 /**
118  * @brief Number of data requests
119  *
120  */
121 #define DHT11_DATA_SAMPLES (40)
122 
123 /**
124  * @brief Number of bytes in data
125  *
126  */
127 #define DHT11_DATA_SIZE (5)
128 
129 /**
130  * @brief DHT11 maximum sample rate is 1 Hz (1 second)
131  *
132  */
133 #define DHT11_SAMPLING_RATE_SECONDS (1)
134 /*---------------------------------------------------------------------------*/
135 /**
136  * @brief DHT struct
137  *
138  */
139 typedef struct {
140  /**
141  * @brief GPIO Port
142  *
143  */
144  gpio_hal_port_t port;
145  /**
146  * @brief GPIO Pin
147  *
148  */
149  gpio_hal_pin_t pin;
150  /**
151  * @brief DH status
152  *
153  */
154  uint8_t status;
155  /**
156  * @brief Time of last read
157  *
158  */
159  clock_time_t last_read;
160  /**
161  * @brief Data array
162  *
163  */
164  uint8_t data[DHT11_DATA_SIZE];
165 } dht_t;
166 
167 /**
168  * @brief DHT struct
169  *
170  */
171 static dht_t dht;
172 /*---------------------------------------------------------------------------*/
173 static int
174 dht11_humidity_integer(void)
175 {
176  return dht.data[0];
177 }
178 /*---------------------------------------------------------------------------*/
179 static int
180 dht11_humidity_decimal(void)
181 {
182  return dht.data[1];
183 }
184 /*---------------------------------------------------------------------------*/
185 static int
186 dht11_temperature_integer(void)
187 {
188  return dht.data[2];
189 }
190 /*---------------------------------------------------------------------------*/
191 static int
192 dht11_temperature_decimal(void)
193 {
194  return dht.data[3];
195 }
196 /*---------------------------------------------------------------------------*/
197 static int8_t
198 dht_signal_duration(uint8_t active, uint32_t max_duration)
199 {
200  rtimer_clock_t elapsed_ticks;
201  rtimer_clock_t max_wait_ticks = US_TO_RTIMERTICKS(max_duration) + DHT11_TICKS_GUARD;
202  rtimer_clock_t start_ticks = RTIMER_NOW();
203 
204  /* Wait for signal to change */
205  RTIMER_BUSYWAIT_UNTIL(gpio_hal_arch_read_pin(dht.port, dht.pin) != active, max_wait_ticks);
206 
207  elapsed_ticks = RTIMER_NOW() - start_ticks;
208 
209  if(elapsed_ticks > max_wait_ticks) {
210  return -1;
211  }
212 
213  return RTIMERTICKS_TO_US(elapsed_ticks);
214 }
215 /*---------------------------------------------------------------------------*/
216 static int8_t
217 dht_signal_transition(uint8_t active, uint32_t max_duration)
218 {
219  return dht_signal_duration(active, max_duration);
220 }
221 /*---------------------------------------------------------------------------*/
222 static uint8_t
223 dht_verify_checksum(void)
224 {
225  return ((dht.data[0] + dht.data[1] + dht.data[2] + dht.data[3]) & 0xFF) == dht.data[4];
226 }
227 /*---------------------------------------------------------------------------*/
228 static uint8_t
229 dht_read(void)
230 {
231  uint8_t j, i;
232  /* Array to store the duration of each data signal to be calculated later */
233  int8_t data_signal_duration[DHT11_DATA_SAMPLES];
234 
235  /**
236  * Data Single-bus free status is at high voltage level. When the communication
237  * between MCU and DHT11 begins, the programme of MCU will set Data Single-bus
238  * voltage level from high to low and this process must take at least 18ms to
239  * ensure DHT’s detection of MCU's signal, then MCU will pull up voltage and
240  * wait 20-40us for DHT’s response.
241  */
242  gpio_hal_arch_pin_set_output(dht.port, dht.pin);
243  gpio_hal_arch_clear_pin(dht.port, dht.pin);
244  RTIMER_BUSYWAIT(US_TO_RTIMERTICKS(18000UL));
245  gpio_hal_arch_set_pin(dht.port, dht.pin);
246  gpio_hal_arch_pin_set_input(dht.port, dht.pin);
247 
248  if(dht_signal_transition(DHT11_SIGNAL_HIGH, DHT11_SIGNAL_START_PHASE1_DURATION) == -1) {
249  return DHT11_STATUS_TIMEOUT;
250  }
251 
252  /**
253  * Once DHT detects the start signal,it will send out a low-voltage-level response
254  * signal, which lasts 80us. Then the programme of DHT sets Data Single-bus voltage
255  * level from low to high and keeps it for 80us for DHT’s preparation for sending data.
256  */
257  if(dht_signal_transition(DHT11_SIGNAL_LOW, DHT11_SIGNAL_START_PHASE2_DURATION) == -1) {
258  return DHT11_STATUS_TIMEOUT;
259  }
260 
261  if(dht_signal_transition(DHT11_SIGNAL_HIGH, DHT11_SIGNAL_START_PHASE3_DURATION) == -1) {
262  return DHT11_STATUS_TIMEOUT;
263  }
264 
265  for(i = 0; i < DHT11_DATA_SAMPLES; i++) {
266  /**
267  * When DHT is sending data to MCU, every bit of data begins with the 50us
268  * low-voltage-level and the length of the following high-voltage-level signal
269  * determines whether data bit is "0" or "1"
270  */
271  if(dht_signal_transition(DHT11_SIGNAL_LOW, DHT11_SIGNAL_RESPONSE_PHASE1_DURATION) == -1) {
272  return DHT11_STATUS_TIMEOUT;
273  }
274 
275  /*
276  * Save in array and calculate later.
277  * Should not spend time calculating in the loop else the bit bang timing gets lost
278  * Use bit 0 and bit 1 duration summed up to improve timming
279  */
280  data_signal_duration[i] = dht_signal_duration(DHT11_SIGNAL_HIGH,
283  if(data_signal_duration[i] == -1) {
284  return DHT11_STATUS_TIMEOUT;
285  }
286  }
287 
288  memset(dht.data, 0, sizeof(uint8_t) * DHT11_DATA_SIZE);
289  for(j = 0, i = 0; i < DHT11_DATA_SAMPLES; i++) {
290 
291  /**
292  * 26-28us voltage-length means data "0"
293  * 70us voltage-length means 1 bit data "1"
294  */
295  if(data_signal_duration[i] >= DHT11_SIGNAL_RESPONSE_BIT_0_DURATION + DHT11_US_GUARD) {
296  dht.data[j] = (dht.data[j] << 1) | 1;
297  } else {
298  dht.data[j] = dht.data[j] << 1;
299  }
300 
301  /* Next byte */
302  if(i % 8 == 7U) {
303  j++;
304  }
305  }
306 
307  /* Verify checksum */
308  if(!dht_verify_checksum()) {
310  } else {
311  return DHT11_STATUS_OKAY;
312  }
313 }
314 /*---------------------------------------------------------------------------*/
315 static int
316 value(int type)
317 {
318  switch(type) {
320  return dht11_humidity_integer();
322  return dht11_humidity_decimal();
324  return dht11_temperature_integer();
326  return dht11_temperature_decimal();
327  }
328 
329  return 0;
330 }
331 /*---------------------------------------------------------------------------*/
332 static int
333 status(int type)
334 {
335  (void)type;
336 
337  return dht.status;
338 }
339 /*---------------------------------------------------------------------------*/
340 static int
341 configure(int type, int c)
342 {
343  switch(type) {
345  dht.port = c;
346  break;
348  dht.pin = c;
349  break;
350  case SENSORS_HW_INIT:
351  dht.last_read = 0;
352  case SENSORS_ACTIVE:
353  if(c == 1) {
354  clock_time_t now;
355 
356  now = clock_seconds();
357  if(now - dht.last_read < DHT11_SAMPLING_RATE_SECONDS) {
358  return 0;
359  }
360  dht.last_read = now;
361  dht.status = dht_read();
362  }
363  case SENSORS_READY:
364  break;
365  default:
366  return 0;
367  }
368 
369  return 1;
370 }
371 /*---------------------------------------------------------------------------*/
372 SENSORS_SENSOR(dht11_sensor, "dht11", value, configure, status);
373 /*----------------------------------------------------------------------------*/
374 /** @} */
#define DHT11_STATUS_OKAY
DHT11 status okay.
Definition: dht11-sensor.h:93
#define DHT11_SIGNAL_RESPONSE_PHASE1_DURATION
Duration of signal response phase 1 according to data sheet.
Definition: dht11-sensor.c:85
#define DHT11_STATUS_TIMEOUT
DHT11 status timeout.
Definition: dht11-sensor.h:99
#define DHT11_SIGNAL_RESPONSE_BIT_1_DURATION
Duration of signal response if bit is set to 1, according to data sheet.
Definition: dht11-sensor.c:97
#define DHT11_VALUE_TEMPERATURE_INTEGER
DHT11 value type for temperature integer part.
Definition: dht11-sensor.h:81
#define DHT11_DATA_SIZE
Number of bytes in data.
Definition: dht11-sensor.c:127
#define RTIMER_BUSYWAIT_UNTIL(cond, max_time)
Busy-wait until a condition for at most max_time.
Definition: rtimer.h:211
DHT 11 sensor header file.
#define DHT11_SIGNAL_START_PHASE2_DURATION
Duration of signal start phase 2 according to data sheet.
Definition: dht11-sensor.c:73
#define DHT11_SIGNAL_RESPONSE_BIT_0_DURATION
Duration of signal response if bit is set to 0, according to data sheet.
Definition: dht11-sensor.c:91
uint8_t gpio_hal_port_t
A data structure that represents ports.
Definition: gpio-hal.h:110
#define DHT11_CONFIGURE_GPIO_PORT
DHT11 Configuration type for GPIO Port.
Definition: dht11-sensor.h:57
unsigned long clock_seconds(void)
Get the current value of the platform seconds.
Definition: clock.c:130
#define DHT11_CONFIGURE_GPIO_PIN
DHT11 Configuration type for GPIO Pin.
Definition: dht11-sensor.h:63
#define DHT11_US_GUARD
Sensor timer drift in us from rtimer.
Definition: dht11-sensor.c:115
#define DHT11_SIGNAL_START_PHASE1_DURATION
Duration of signal start phase 1 according to data sheet.
Definition: dht11-sensor.c:67
#define RTIMER_NOW()
Get the current clock time.
Definition: rtimer.h:185
#define DHT11_TICKS_GUARD
Sensor timer drift in ticks.
Definition: dht11-sensor.c:106
#define DHT11_STATUS_CHECKSUM_FAILED
DHT11 status checksum failed.
Definition: dht11-sensor.h:105
#define DHT11_VALUE_TEMPERATURE_DECIMAL
DHT11 value type for temperature decimal part.
Definition: dht11-sensor.h:87
static dht_t dht
DHT struct.
Definition: dht11-sensor.c:171
#define DHT11_SIGNAL_HIGH
GPIO High.
Definition: dht11-sensor.c:55
#define DHT11_VALUE_HUMIDITY_DECIMAL
DHT11 value type for humidity decimal part.
Definition: dht11-sensor.h:75
uint8_t gpio_hal_pin_t
GPIO pin number representation.
Definition: gpio-hal.h:103
#define DHT11_SIGNAL_LOW
GPIO Low.
Definition: dht11-sensor.c:61
#define DHT11_VALUE_HUMIDITY_INTEGER
DHT11 value type for humidity integer part.
Definition: dht11-sensor.h:69
#define DHT11_SIGNAL_START_PHASE3_DURATION
Duration of signal start phase 3 according to data sheet.
Definition: dht11-sensor.c:79
#define DHT11_DATA_SAMPLES
Number of data requests.
Definition: dht11-sensor.c:121
#define DHT11_SAMPLING_RATE_SECONDS
DHT11 maximum sample rate is 1 Hz (1 second)
Definition: dht11-sensor.c:133
Header file for the GPIO HAL.
#define RTIMER_BUSYWAIT(duration)
Busy-wait for a fixed duration.
Definition: rtimer.h:218
static uint8_t dht_read(void)
Definition: dht11-sensor.c:229