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  * \file
35  * DHT 11 sensor implementation
36  *
37  * \see https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
38  *
39  * \author
40  * Yago Fontoura do Rosario <yago.rosario@hotmail.com.br
41  */
42 
43 #include "contiki.h"
44 #include "dht11-sensor.h"
45 #include <string.h>
46 #include "dev/gpio-hal.h"
47 
48 /*---------------------------------------------------------------------------*/
49 /**
50  * @brief GPIO High
51  *
52  */
53 #define DHT11_SIGNAL_HIGH (1)
54 
55 /**
56  * @brief GPIO Low
57  *
58  */
59 #define DHT11_SIGNAL_LOW (0)
60 
61 /**
62  * @brief Duration of signal start phase 1 according to data sheet
63  *
64  */
65 #define DHT11_SIGNAL_START_PHASE1_DURATION (40)
66 
67 /**
68  * @brief Duration of signal start phase 2 according to data sheet
69  *
70  */
71 #define DHT11_SIGNAL_START_PHASE2_DURATION (80)
72 
73 /**
74  * @brief Duration of signal start phase 3 according to data sheet
75  *
76  */
77 #define DHT11_SIGNAL_START_PHASE3_DURATION (80)
78 
79 /**
80  * @brief Duration of signal response phase 1 according to data sheet
81  *
82  */
83 #define DHT11_SIGNAL_RESPONSE_PHASE1_DURATION (50)
84 
85 /**
86  * @brief Duration of signal response if bit is set to 0, according to data sheet
87  *
88  */
89 #define DHT11_SIGNAL_RESPONSE_BIT_0_DURATION (28)
90 
91 /**
92  * @brief Duration of signal response if bit is set to 1, according to data sheet
93  *
94  */
95 #define DHT11_SIGNAL_RESPONSE_BIT_1_DURATION (70)
96 
97 /**
98  * @brief Sensor timer drift in ticks
99  *
100  * DHT uses 1us granularity and rtimer granularity is higher.
101  * So, allow the reading to drift by 1 tick
102  *
103  */
104 #define DHT11_TICKS_GUARD (1)
105 
106 /**
107  * @brief Sensor timer drift in us from rtimer
108  *
109  * DHT uses 1us granularity and rtimer granularity is higher.
110  * So, allow the reading to drift by 1 tick in us
111  *
112  */
113 #define DHT11_US_GUARD RTIMERTICKS_TO_US(1)
114 
115 /**
116  * @brief Number of data requests
117  *
118  */
119 #define DHT11_DATA_SAMPLES (40)
120 
121 /**
122  * @brief Number of bytes in data
123  *
124  */
125 #define DHT11_DATA_SIZE (5)
126 
127 /**
128  * @brief DHT11 maximum sample rate is 1 Hz (1 second)
129  *
130  */
131 #define DHT11_SAMPLING_RATE_SECONDS (1)
132 /*---------------------------------------------------------------------------*/
133 /**
134  * @brief DHT struct
135  *
136  */
137 typedef struct {
138  /**
139  * @brief GPIO Port
140  *
141  */
142  gpio_hal_port_t port;
143  /**
144  * @brief GPIO Pin
145  *
146  */
147  gpio_hal_pin_t pin;
148  /**
149  * @brief DH status
150  *
151  */
152  uint8_t status;
153  /**
154  * @brief Time of last read
155  *
156  */
157  clock_time_t last_read;
158  /**
159  * @brief Data array
160  *
161  */
162  uint8_t data[DHT11_DATA_SIZE];
163 } dht_t;
164 
165 /**
166  * @brief DHT struct
167  *
168  */
169 static dht_t dht;
170 /*---------------------------------------------------------------------------*/
171 static int
172 dht11_humidity_integer(void)
173 {
174  return dht.data[0];
175 }
176 /*---------------------------------------------------------------------------*/
177 static int
178 dht11_humidity_decimal(void)
179 {
180  return dht.data[1];
181 }
182 /*---------------------------------------------------------------------------*/
183 static int
184 dht11_temperature_integer(void)
185 {
186  return dht.data[2];
187 }
188 /*---------------------------------------------------------------------------*/
189 static int
190 dht11_temperature_decimal(void)
191 {
192  return dht.data[3];
193 }
194 /*---------------------------------------------------------------------------*/
195 static int8_t
196 dht_signal_duration(uint8_t active, uint32_t max_duration)
197 {
198  rtimer_clock_t elapsed_ticks;
199  rtimer_clock_t max_wait_ticks = US_TO_RTIMERTICKS(max_duration) + DHT11_TICKS_GUARD;
200  rtimer_clock_t start_ticks = RTIMER_NOW();
201 
202  /* Wait for signal to change */
203  RTIMER_BUSYWAIT_UNTIL(gpio_hal_arch_read_pin(dht.port, dht.pin) != active, max_wait_ticks);
204 
205  elapsed_ticks = RTIMER_NOW() - start_ticks;
206 
207  if(elapsed_ticks > max_wait_ticks) {
208  return -1;
209  }
210 
211  return RTIMERTICKS_TO_US(elapsed_ticks);
212 }
213 /*---------------------------------------------------------------------------*/
214 static int8_t
215 dht_signal_transition(uint8_t active, uint32_t max_duration)
216 {
217  return dht_signal_duration(active, max_duration);
218 }
219 /*---------------------------------------------------------------------------*/
220 static uint8_t
221 dht_verify_checksum(void)
222 {
223  return ((dht.data[0] + dht.data[1] + dht.data[2] + dht.data[3]) & 0xFF) == dht.data[4];
224 }
225 /*---------------------------------------------------------------------------*/
226 static uint8_t
227 dht_read(void)
228 {
229  uint8_t j, i;
230  /* Array to store the duration of each data signal to be calculated later */
231  int8_t data_signal_duration[DHT11_DATA_SAMPLES];
232 
233  /**
234  * Data Single-bus free status is at high voltage level. When the communication
235  * between MCU and DHT11 begins, the programme of MCU will set Data Single-bus
236  * voltage level from high to low and this process must take at least 18ms to
237  * ensure DHT’s detection of MCU's signal, then MCU will pull up voltage and
238  * wait 20-40us for DHT’s response.
239  */
240  gpio_hal_arch_pin_set_output(dht.port, dht.pin);
241  gpio_hal_arch_clear_pin(dht.port, dht.pin);
242  RTIMER_BUSYWAIT(US_TO_RTIMERTICKS(18000UL));
243  gpio_hal_arch_set_pin(dht.port, dht.pin);
244  gpio_hal_arch_pin_set_input(dht.port, dht.pin);
245 
246  if(dht_signal_transition(DHT11_SIGNAL_HIGH, DHT11_SIGNAL_START_PHASE1_DURATION) == -1) {
247  return DHT11_STATUS_TIMEOUT;
248  }
249 
250  /**
251  * Once DHT detects the start signal,it will send out a low-voltage-level response
252  * signal, which lasts 80us. Then the programme of DHT sets Data Single-bus voltage
253  * level from low to high and keeps it for 80us for DHT’s preparation for sending data.
254  */
255  if(dht_signal_transition(DHT11_SIGNAL_LOW, DHT11_SIGNAL_START_PHASE2_DURATION) == -1) {
256  return DHT11_STATUS_TIMEOUT;
257  }
258 
259  if(dht_signal_transition(DHT11_SIGNAL_HIGH, DHT11_SIGNAL_START_PHASE3_DURATION) == -1) {
260  return DHT11_STATUS_TIMEOUT;
261  }
262 
263  for(i = 0; i < DHT11_DATA_SAMPLES; i++) {
264  /**
265  * When DHT is sending data to MCU, every bit of data begins with the 50us
266  * low-voltage-level and the length of the following high-voltage-level signal
267  * determines whether data bit is "0" or "1"
268  */
269  if(dht_signal_transition(DHT11_SIGNAL_LOW, DHT11_SIGNAL_RESPONSE_PHASE1_DURATION) == -1) {
270  return DHT11_STATUS_TIMEOUT;
271  }
272 
273  /*
274  * Save in array and calculate later.
275  * Should not spend time calculating in the loop else the bit bang timing gets lost
276  * Use bit 0 and bit 1 duration summed up to improve timming
277  */
278  data_signal_duration[i] = dht_signal_duration(DHT11_SIGNAL_HIGH,
281  if(data_signal_duration[i] == -1) {
282  return DHT11_STATUS_TIMEOUT;
283  }
284  }
285 
286  memset(dht.data, 0, sizeof(uint8_t) * DHT11_DATA_SIZE);
287  for(j = 0, i = 0; i < DHT11_DATA_SAMPLES; i++) {
288 
289  /**
290  * 26-28us voltage-length means data "0"
291  * 70us voltage-length means 1 bit data "1"
292  */
293  if(data_signal_duration[i] >= DHT11_SIGNAL_RESPONSE_BIT_0_DURATION + DHT11_US_GUARD) {
294  dht.data[j] = (dht.data[j] << 1) | 1;
295  } else {
296  dht.data[j] = dht.data[j] << 1;
297  }
298 
299  /* Next byte */
300  if(i % 8 == 7U) {
301  j++;
302  }
303  }
304 
305  /* Verify checksum */
306  if(!dht_verify_checksum()) {
308  } else {
309  return DHT11_STATUS_OKAY;
310  }
311 }
312 /*---------------------------------------------------------------------------*/
313 static int
314 value(int type)
315 {
316  switch(type) {
318  return dht11_humidity_integer();
320  return dht11_humidity_decimal();
322  return dht11_temperature_integer();
324  return dht11_temperature_decimal();
325  }
326 
327  return 0;
328 }
329 /*---------------------------------------------------------------------------*/
330 static int
331 status(int type)
332 {
333  (void)type;
334 
335  return dht.status;
336 }
337 /*---------------------------------------------------------------------------*/
338 static int
339 configure(int type, int c)
340 {
341  switch(type) {
343  dht.port = c;
344  break;
346  dht.pin = c;
347  break;
348  case SENSORS_HW_INIT:
349  dht.last_read = 0;
350  case SENSORS_ACTIVE:
351  if(c == 1) {
352  clock_time_t now;
353 
354  now = clock_seconds();
355  if(now - dht.last_read < DHT11_SAMPLING_RATE_SECONDS) {
356  return 0;
357  }
358  dht.last_read = now;
359  dht.status = dht_read();
360  }
361  case SENSORS_READY:
362  break;
363  default:
364  return 0;
365  }
366 
367  return 1;
368 }
369 /*---------------------------------------------------------------------------*/
370 SENSORS_SENSOR(dht11_sensor, "dht11", value, configure, status);
#define DHT11_DATA_SAMPLES
Number of data requests.
Definition: dht11-sensor.c:119
#define DHT11_STATUS_OKAY
DHT11 status okay.
Definition: dht11-sensor.h:94
#define DHT11_STATUS_TIMEOUT
DHT11 status timeout.
Definition: dht11-sensor.h:100
#define DHT11_VALUE_TEMPERATURE_INTEGER
DHT11 value type for temperature integer part.
Definition: dht11-sensor.h:82
#define DHT11_DATA_SIZE
Number of bytes in data.
Definition: dht11-sensor.c:125
#define DHT11_SIGNAL_START_PHASE2_DURATION
Duration of signal start phase 2 according to data sheet.
Definition: dht11-sensor.c:71
#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
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:58
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:64
#define DHT11_US_GUARD
Sensor timer drift in us from rtimer.
Definition: dht11-sensor.c:113
#define DHT11_SAMPLING_RATE_SECONDS
DHT11 maximum sample rate is 1 Hz (1 second)
Definition: dht11-sensor.c:131
#define RTIMER_NOW()
Get the current clock time.
Definition: rtimer.h:185
#define DHT11_SIGNAL_START_PHASE1_DURATION
Duration of signal start phase 1 according to data sheet.
Definition: dht11-sensor.c:65
#define DHT11_STATUS_CHECKSUM_FAILED
DHT11 status checksum failed.
Definition: dht11-sensor.h:106
#define DHT11_VALUE_TEMPERATURE_DECIMAL
DHT11 value type for temperature decimal part.
Definition: dht11-sensor.h:88
#define DHT11_SIGNAL_START_PHASE3_DURATION
Duration of signal start phase 3 according to data sheet.
Definition: dht11-sensor.c:77
#define DHT11_SIGNAL_HIGH
GPIO High.
Definition: dht11-sensor.c:53
#define DHT11_VALUE_HUMIDITY_DECIMAL
DHT11 value type for humidity decimal part.
Definition: dht11-sensor.h:76
uint8_t gpio_hal_pin_t
GPIO pin number representation.
Definition: gpio-hal.h:103
#define DHT11_VALUE_HUMIDITY_INTEGER
DHT11 value type for humidity integer part.
Definition: dht11-sensor.h:70
static dht_t dht
DHT struct.
Definition: dht11-sensor.c:169
#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:89
#define DHT11_SIGNAL_RESPONSE_PHASE1_DURATION
Duration of signal response phase 1 according to data sheet.
Definition: dht11-sensor.c:83
#define DHT11_SIGNAL_LOW
GPIO Low.
Definition: dht11-sensor.c:59
#define DHT11_TICKS_GUARD
Sensor timer drift in ticks.
Definition: dht11-sensor.c:104
static uint8_t dht_read(void)
Definition: dht11-sensor.c:227
#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:95
Header file for the GPIO HAL.
#define RTIMER_BUSYWAIT(duration)
Busy-wait for a fixed duration.
Definition: rtimer.h:218