forked from FrostbyteGR/check_dht22
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdht22.c
258 lines (208 loc) · 6.83 KB
/
dht22.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
* dht22.c
* DHT22 Sensor nagios plugin for Single Board Computers
* Copyright (c) 2017 Frostbyte <frostbytegr@gmail.com>
*
* Check environment temperature and humidity with DHT22 via GPIO
*
* Credits:
* <projects@drogon.net> - wiringPi library and rht03 code
* <devel@nagios-plugins.org> - plugin development guidelines
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sched.h>
#include <sys/time.h>
// wiringPi library
#include "ThirdParty/wiringPi/wiringPi.h"
// Sensor library
#include "dht22.h"
// Boolean definitions
#ifndef TRUE
# define TRUE (1==1)
# define FALSE (!TRUE)
#endif
// Sensor definitions
#define QUERYRETRIES 4
// Function to set the scheduling policy with maximum priority
static void setMaximumPriority() {
struct sched_param sched;
// Utilize the FIFO scheduler and assign the highest possible priority so context switching is avoided
memset(&sched, 0, sizeof(sched));
sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &sched);
}
// Function to set the scheduling policy with default priority
static void setDefaultPriority() {
struct sched_param sched;
// Revert back to the standard round-robin scheduler
memset(&sched, 0, sizeof(sched));
sched.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &sched);
}
// Function to enforce delay until the GPIO transitions from LOW to HIGH state
static int sensorLowHighWait(int GPIO) {
struct timeval now, timeOut, timeUp;
// Set a timer of 1 ms
gettimeofday(&now, NULL);
timerclear(&timeOut);
timeOut.tv_usec=1000;
timeradd(&now, &timeOut, &timeUp);
// If the GPIO is already in a HIGH state
// Wait until it transitions to a LOW state
while (digitalRead(GPIO)==HIGH) {
gettimeofday(&now, NULL);
// If the timer runs out
if (timercmp(&now, &timeUp, >)) {
return FALSE;
}
}
// Set another time of 1 ms
gettimeofday(&now, NULL);
timerclear(&timeOut);
timeOut.tv_usec=1000;
timeradd(&now, &timeOut, &timeUp);
// Wait until the GPIO transitions to a HIGH state
while (digitalRead(GPIO)==LOW) {
gettimeofday(&now, NULL);
// If the timer runs out
if (timercmp(&now, &timeUp, >)) {
return FALSE;
}
}
// Upon successful transition
return TRUE;
}
// Function to retrieve a byte of data from the sensor
static uint8_t retrieveByte(int GPIO) {
uint8_t result=0x00;
// For every bit of the byte
for (int bit=0; bit<8; ++bit) {
// If the sensor transition fails
if (!sensorLowHighWait(GPIO)) {
return 0;
}
// Data retrieval needs to be timed
delayMicroseconds(30);
// Insert bits into the result by shifting them to the left
result<<=1;
// If the GPIO is in a HIGH state
if (digitalRead(GPIO)==HIGH) {
// Turn the bit that was just inserted to 1
result|=1;
}
}
// Return the processed byte
return result;
}
// Function to query the sensor for information
static int querySensor(int GPIO, uint8_t results[4]) {
struct timeval now, then, took;
uint8_t queryChecksum=0x00, retrievedBytes[5];
// Set priority to maximum
setMaximumPriority();
// Take a timestamp before the operation begins
gettimeofday(&then, NULL);
// Set the GPIO into OUTPUT mode so it's state can be manipulated
pinMode(GPIO, OUTPUT);
// Wake up the sensor by setting the GPIO to a LOW state for 10ms
digitalWrite(GPIO, LOW);
delay(10);
// And then a HIGH state for 40ìs
digitalWrite(GPIO, HIGH);
delayMicroseconds(40);
// Set the GPIO into INPUT mode so data can be read from it
pinMode(GPIO, INPUT);
// If the sensor transition fails
if (!sensorLowHighWait(GPIO)) {
return FALSE;
}
// Retrieve 5 bytes (40 bits) of information from the sensor
for (int byte=0; byte<5; ++byte) {
retrievedBytes[byte]=retrieveByte(GPIO);
// For the first 4 bytes of information
if (byte<4) {
// Copy them to the results
results[byte]=retrievedBytes[byte];
// Add them to the query checksum
queryChecksum+=retrievedBytes[byte];
}
}
// Mask the query checksum
queryChecksum&=0xFF;
// Take another timestamp once the operation ends
gettimeofday(&now, NULL);
// Find out how long the operation took
timersub(&now, &then, &took);
// Set priority back to default
setDefaultPriority();
// The time it should take to complete the operation should be:
// 10ms + 40µs - for sensor to reset
// + 80µs + 80µs - for the sensor to transition from a LOW to a HIGH state
// + 40 * ( 50µs + 27µs [0] or 70µs [1] ) - for the data to be retrieved from the sensor
// = 15010µs in total
// If it took more than that, there has been a scheduling
// interruption and the reading is probably invalid
if ((took.tv_sec!=0) || (took.tv_usec>16000)) {
return FALSE;
}
// Return the checksum validation result of the query
return queryChecksum==retrievedBytes[4];
}
// Main query function for the DHT22 sensor
struct sensorOutput parseSensorOutput(int GPIO) {
struct sensorOutput result;
uint8_t sensorData[4];
// If wiringPi fails to initialize
if (wiringPiSetup()==-1) {
// Throw an error and exit
fprintf(stderr, "wiringPi failed to initialize.\n");
fflush(stderr);
exit(EXIT_FAILURE);
}
// Set the sensor query retry count
int queryRetries=QUERYRETRIES+1;
// While there are still retries remaining
while (queryRetries--) {
// Clean up any retrieved sensor data
memset(sensorData, 0, sizeof(sensorData));
// If the sensor query was successful
if (querySensor(GPIO, sensorData)) {
// Parse the temperature and humidity data
result.temperature=(((sensorData[2]*256+sensorData[3])/10)*9/5)+32;
result.humidity=(sensorData[0]*256+sensorData[1])/10;
// Check and adjust for negative temperatures
if ((sensorData[2]&0x80)!=0) {
result.temperature*=-1;
}
// If the retrieved data are within the sensor's documented capabilities
if (result.temperature>=SENSOR_TMP_MIN && result.temperature<=SENSOR_TMP_MAX && result.humidity>=SENSOR_HUM_MIN && result.humidity<=SENSOR_HUM_MAX) {
// Return the processed output
return result;
}
}
// Wait for 2 seconds before retrying
delay(2000);
}
// If this part is reached, no measurement was valid
// Set the output values to N/A
result.temperature=SENSOR_NA;
result.humidity=SENSOR_NA;
// Return the processed output
return result;
}