DHT.cpp (12000B)
1 /*! 2 * @file DHT.cpp 3 * 4 * @mainpage DHT series of low cost temperature/humidity sensors. 5 * 6 * @section intro_sec Introduction 7 * 8 * This is a library for DHT series of low cost temperature/humidity sensors. 9 * 10 * You must have Adafruit Unified Sensor Library library installed to use this 11 * class. 12 * 13 * Adafruit invests time and resources providing this open source code, 14 * please support Adafruit andopen-source hardware by purchasing products 15 * from Adafruit! 16 * 17 * @section author Author 18 * 19 * Written by Adafruit Industries. 20 * 21 * @section license License 22 * 23 * MIT license, all text above must be included in any redistribution 24 */ 25 26 #include "DHT.h" 27 28 #define MIN_INTERVAL 2000 /**< min interval value */ 29 #define TIMEOUT \ 30 UINT32_MAX /**< Used programmatically for timeout. \ 31 Not a timeout duration. Type: uint32_t. */ 32 33 /*! 34 * @brief Instantiates a new DHT class 35 * @param pin 36 * pin number that sensor is connected 37 * @param type 38 * type of sensor 39 * @param count 40 * number of sensors 41 */ 42 DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) { 43 (void)count; // Workaround to avoid compiler warning. 44 _pin = pin; 45 _type = type; 46 #ifdef __AVR 47 _bit = digitalPinToBitMask(pin); 48 _port = digitalPinToPort(pin); 49 #endif 50 _maxcycles = 51 microsecondsToClockCycles(1000); // 1 millisecond timeout for 52 // reading pulses from DHT sensor. 53 // Note that count is now ignored as the DHT reading algorithm adjusts itself 54 // based on the speed of the processor. 55 } 56 57 /*! 58 * @brief Setup sensor pins and set pull timings 59 * @param usec 60 * Optionally pass pull-up time (in microseconds) before DHT reading 61 *starts. Default is 55 (see function declaration in DHT.h). 62 */ 63 void DHT::begin(uint8_t usec) { 64 // set up the pins! 65 pinMode(_pin, INPUT_PULLUP); 66 // Using this value makes sure that millis() - lastreadtime will be 67 // >= MIN_INTERVAL right away. Note that this assignment wraps around, 68 // but so will the subtraction. 69 _lastreadtime = millis() - MIN_INTERVAL; 70 DEBUG_PRINT("DHT max clock cycles: "); 71 DEBUG_PRINTLN(_maxcycles, DEC); 72 pullTime = usec; 73 } 74 75 /*! 76 * @brief Read temperature 77 * @param S 78 * Scale. Boolean value: 79 * - true = Fahrenheit 80 * - false = Celcius 81 * @param force 82 * true if in force mode 83 * @return Temperature value in selected scale 84 */ 85 float DHT::readTemperature(bool S, bool force) { 86 float f = NAN; 87 88 if (read(force)) { 89 switch (_type) { 90 case DHT11: 91 f = data[2]; 92 if (data[3] & 0x80) { 93 f = -1 - f; 94 } 95 f += (data[3] & 0x0f) * 0.1; 96 if (S) { 97 f = convertCtoF(f); 98 } 99 break; 100 case DHT12: 101 f = data[2]; 102 f += (data[3] & 0x0f) * 0.1; 103 if (data[2] & 0x80) { 104 f *= -1; 105 } 106 if (S) { 107 f = convertCtoF(f); 108 } 109 break; 110 case DHT22: 111 case DHT21: 112 f = ((word)(data[2] & 0x7F)) << 8 | data[3]; 113 f *= 0.1; 114 if (data[2] & 0x80) { 115 f *= -1; 116 } 117 if (S) { 118 f = convertCtoF(f); 119 } 120 break; 121 } 122 } 123 return f; 124 } 125 126 /*! 127 * @brief Converts Celcius to Fahrenheit 128 * @param c 129 * value in Celcius 130 * @return float value in Fahrenheit 131 */ 132 float DHT::convertCtoF(float c) { return c * 1.8 + 32; } 133 134 /*! 135 * @brief Converts Fahrenheit to Celcius 136 * @param f 137 * value in Fahrenheit 138 * @return float value in Celcius 139 */ 140 float DHT::convertFtoC(float f) { return (f - 32) * 0.55555; } 141 142 /*! 143 * @brief Read Humidity 144 * @param force 145 * force read mode 146 * @return float value - humidity in percent 147 */ 148 float DHT::readHumidity(bool force) { 149 float f = NAN; 150 if (read(force)) { 151 switch (_type) { 152 case DHT11: 153 case DHT12: 154 f = data[0] + data[1] * 0.1; 155 break; 156 case DHT22: 157 case DHT21: 158 f = ((word)data[0]) << 8 | data[1]; 159 f *= 0.1; 160 break; 161 } 162 } 163 return f; 164 } 165 166 /*! 167 * @brief Compute Heat Index 168 * Simplified version that reads temp and humidity from sensor 169 * @param isFahrenheit 170 * true if fahrenheit, false if celcius 171 *(default true) 172 * @return float heat index 173 */ 174 float DHT::computeHeatIndex(bool isFahrenheit) { 175 float hi = computeHeatIndex(readTemperature(isFahrenheit), readHumidity(), 176 isFahrenheit); 177 return hi; 178 } 179 180 /*! 181 * @brief Compute Heat Index 182 * Using both Rothfusz and Steadman's equations 183 * (http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml) 184 * @param temperature 185 * temperature in selected scale 186 * @param percentHumidity 187 * humidity in percent 188 * @param isFahrenheit 189 * true if fahrenheit, false if celcius 190 * @return float heat index 191 */ 192 float DHT::computeHeatIndex(float temperature, float percentHumidity, 193 bool isFahrenheit) { 194 float hi; 195 196 if (!isFahrenheit) 197 temperature = convertCtoF(temperature); 198 199 hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + 200 (percentHumidity * 0.094)); 201 202 if (hi > 79) { 203 hi = -42.379 + 2.04901523 * temperature + 10.14333127 * percentHumidity + 204 -0.22475541 * temperature * percentHumidity + 205 -0.00683783 * pow(temperature, 2) + 206 -0.05481717 * pow(percentHumidity, 2) + 207 0.00122874 * pow(temperature, 2) * percentHumidity + 208 0.00085282 * temperature * pow(percentHumidity, 2) + 209 -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2); 210 211 if ((percentHumidity < 13) && (temperature >= 80.0) && 212 (temperature <= 112.0)) 213 hi -= ((13.0 - percentHumidity) * 0.25) * 214 sqrt((17.0 - abs(temperature - 95.0)) * 0.05882); 215 216 else if ((percentHumidity > 85.0) && (temperature >= 80.0) && 217 (temperature <= 87.0)) 218 hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2); 219 } 220 221 return isFahrenheit ? hi : convertFtoC(hi); 222 } 223 224 /*! 225 * @brief Read value from sensor or return last one from less than two 226 *seconds. 227 * @param force 228 * true if using force mode 229 * @return float value 230 */ 231 bool DHT::read(bool force) { 232 // Check if sensor was read less than two seconds ago and return early 233 // to use last reading. 234 uint32_t currenttime = millis(); 235 if (!force && ((currenttime - _lastreadtime) < MIN_INTERVAL)) { 236 return _lastresult; // return last correct measurement 237 } 238 _lastreadtime = currenttime; 239 240 // Reset 40 bits of received data to zero. 241 data[0] = data[1] = data[2] = data[3] = data[4] = 0; 242 243 #if defined(ESP8266) 244 yield(); // Handle WiFi / reset software watchdog 245 #endif 246 247 // Send start signal. See DHT datasheet for full signal diagram: 248 // http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf 249 250 // Go into high impedence state to let pull-up raise data line level and 251 // start the reading process. 252 pinMode(_pin, INPUT_PULLUP); 253 delay(1); 254 255 // First set data line low for a period according to sensor type 256 pinMode(_pin, OUTPUT); 257 digitalWrite(_pin, LOW); 258 switch (_type) { 259 case DHT22: 260 case DHT21: 261 delayMicroseconds(1100); // data sheet says "at least 1ms" 262 break; 263 case DHT11: 264 default: 265 delay(20); // data sheet says at least 18ms, 20ms just to be safe 266 break; 267 } 268 269 uint32_t cycles[80]; 270 { 271 // End the start signal by setting data line high for 40 microseconds. 272 pinMode(_pin, INPUT_PULLUP); 273 274 // Delay a moment to let sensor pull data line low. 275 delayMicroseconds(pullTime); 276 277 // Now start reading the data line to get the value from the DHT sensor. 278 279 // Turn off interrupts temporarily because the next sections 280 // are timing critical and we don't want any interruptions. 281 InterruptLock lock; 282 283 // First expect a low signal for ~80 microseconds followed by a high signal 284 // for ~80 microseconds again. 285 if (expectPulse(LOW) == TIMEOUT) { 286 DEBUG_PRINTLN(F("DHT timeout waiting for start signal low pulse.")); 287 _lastresult = false; 288 return _lastresult; 289 } 290 if (expectPulse(HIGH) == TIMEOUT) { 291 DEBUG_PRINTLN(F("DHT timeout waiting for start signal high pulse.")); 292 _lastresult = false; 293 return _lastresult; 294 } 295 296 // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 297 // microsecond low pulse followed by a variable length high pulse. If the 298 // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds 299 // then it's a 1. We measure the cycle count of the initial 50us low pulse 300 // and use that to compare to the cycle count of the high pulse to determine 301 // if the bit is a 0 (high state cycle count < low state cycle count), or a 302 // 1 (high state cycle count > low state cycle count). Note that for speed 303 // all the pulses are read into a array and then examined in a later step. 304 for (int i = 0; i < 80; i += 2) { 305 cycles[i] = expectPulse(LOW); 306 cycles[i + 1] = expectPulse(HIGH); 307 } 308 } // Timing critical code is now complete. 309 310 // Inspect pulses and determine which ones are 0 (high state cycle count < low 311 // state cycle count), or 1 (high state cycle count > low state cycle count). 312 for (int i = 0; i < 40; ++i) { 313 uint32_t lowCycles = cycles[2 * i]; 314 uint32_t highCycles = cycles[2 * i + 1]; 315 if ((lowCycles == TIMEOUT) || (highCycles == TIMEOUT)) { 316 DEBUG_PRINTLN(F("DHT timeout waiting for pulse.")); 317 _lastresult = false; 318 return _lastresult; 319 } 320 data[i / 8] <<= 1; 321 // Now compare the low and high cycle times to see if the bit is a 0 or 1. 322 if (highCycles > lowCycles) { 323 // High cycles are greater than 50us low cycle count, must be a 1. 324 data[i / 8] |= 1; 325 } 326 // Else high cycles are less than (or equal to, a weird case) the 50us low 327 // cycle count so this must be a zero. Nothing needs to be changed in the 328 // stored data. 329 } 330 331 DEBUG_PRINTLN(F("Received from DHT:")); 332 DEBUG_PRINT(data[0], HEX); 333 DEBUG_PRINT(F(", ")); 334 DEBUG_PRINT(data[1], HEX); 335 DEBUG_PRINT(F(", ")); 336 DEBUG_PRINT(data[2], HEX); 337 DEBUG_PRINT(F(", ")); 338 DEBUG_PRINT(data[3], HEX); 339 DEBUG_PRINT(F(", ")); 340 DEBUG_PRINT(data[4], HEX); 341 DEBUG_PRINT(F(" =? ")); 342 DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); 343 344 // Check we read 40 bits and that the checksum matches. 345 if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { 346 _lastresult = true; 347 return _lastresult; 348 } else { 349 DEBUG_PRINTLN(F("DHT checksum failure!")); 350 _lastresult = false; 351 return _lastresult; 352 } 353 } 354 355 // Expect the signal line to be at the specified level for a period of time and 356 // return a count of loop cycles spent at that level (this cycle count can be 357 // used to compare the relative time of two pulses). If more than a millisecond 358 // ellapses without the level changing then the call fails with a 0 response. 359 // This is adapted from Arduino's pulseInLong function (which is only available 360 // in the very latest IDE versions): 361 // https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.c 362 uint32_t DHT::expectPulse(bool level) { 363 #if (F_CPU > 16000000L) 364 uint32_t count = 0; 365 #else 366 uint16_t count = 0; // To work fast enough on slower AVR boards 367 #endif 368 // On AVR platforms use direct GPIO port access as it's much faster and better 369 // for catching pulses that are 10's of microseconds in length: 370 #ifdef __AVR 371 uint8_t portState = level ? _bit : 0; 372 while ((*portInputRegister(_port) & _bit) == portState) { 373 if (count++ >= _maxcycles) { 374 return TIMEOUT; // Exceeded timeout, fail. 375 } 376 } 377 // Otherwise fall back to using digitalRead (this seems to be necessary on 378 // ESP8266 right now, perhaps bugs in direct port access functions?). 379 #else 380 while (digitalRead(_pin) == level) { 381 if (count++ >= _maxcycles) { 382 return TIMEOUT; // Exceeded timeout, fail. 383 } 384 } 385 #endif 386 387 return count; 388 }