| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 | /*! *  @file DHT.cpp * *  @mainpage DHT series of low cost temperature/humidity sensors. * *  @section intro_sec Introduction * *  This is a library for DHT series of low cost temperature/humidity sensors. * *  You must have Adafruit Unified Sensor Library library installed to use this * class. * *  Adafruit invests time and resources providing this open source code, *  please support Adafruit andopen-source hardware by purchasing products *  from Adafruit! * *  @section author Author * *  Written by Adafruit Industries. * *  @section license License * *  MIT license, all text above must be included in any redistribution */#include "DHT.h"#define MIN_INTERVAL 2000 /**< min interval value */#define TIMEOUT -1        /**< timeout on *//*! *  @brief  Instantiates a new DHT class *  @param  pin *          pin number that sensor is connected *  @param  type *          type of sensor *  @param  count *          number of sensors */DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) {  _pin = pin;  _type = type;#ifdef __AVR  _bit = digitalPinToBitMask(pin);  _port = digitalPinToPort(pin);#endif  _maxcycles =      microsecondsToClockCycles(1000); // 1 millisecond timeout for                                       // reading pulses from DHT sensor.  // Note that count is now ignored as the DHT reading algorithm adjusts itself  // based on the speed of the processor.}/*! *  @brief  Setup sensor pins and set pull timings *  @param  usec *          Optionally pass pull-up time (in microseconds) before DHT reading *starts. Default is 55 (see function declaration in DHT.h). */void DHT::begin(uint8_t usec) {  // set up the pins!  pinMode(_pin, INPUT_PULLUP);  // Using this value makes sure that millis() - lastreadtime will be  // >= MIN_INTERVAL right away. Note that this assignment wraps around,  // but so will the subtraction.  _lastreadtime = millis() - MIN_INTERVAL;  DEBUG_PRINT("DHT max clock cycles: ");  DEBUG_PRINTLN(_maxcycles, DEC);  pullTime = usec;}/*! *  @brief  Read temperature *  @param  S *          Scale. Boolean value: *					- true = Fahrenheit *					- false = Celcius *  @param  force *          true if in force mode *	@return Temperature value in selected scale */float DHT::readTemperature(bool S, bool force) {  float f = NAN;  if (read(force)) {    switch (_type) {    case DHT11:      f = data[2];      if (data[3] & 0x80) {        f = -1 - f;      }      f += (data[3] & 0x0f) * 0.1;      if (S) {        f = convertCtoF(f);      }      break;    case DHT12:      f = data[2];      f += (data[3] & 0x0f) * 0.1;      if (data[2] & 0x80) {        f *= -1;      }      if (S) {        f = convertCtoF(f);      }      break;    case DHT22:    case DHT21:      f = ((word)(data[2] & 0x7F)) << 8 | data[3];      f *= 0.1;      if (data[2] & 0x80) {        f *= -1;      }      if (S) {        f = convertCtoF(f);      }      break;    }  }  return f;}/*! *  @brief  Converts Celcius to Fahrenheit *  @param  c *					value in Celcius *	@return float value in Fahrenheit */float DHT::convertCtoF(float c) { return c * 1.8 + 32; }/*! *  @brief  Converts Fahrenheit to Celcius *  @param  f *					value in Fahrenheit *	@return float value in Celcius */float DHT::convertFtoC(float f) { return (f - 32) * 0.55555; }/*! *  @brief  Read Humidity *  @param  force *					force read mode *	@return float value - humidity in percent */float DHT::readHumidity(bool force) {  float f = NAN;  if (read(force)) {    switch (_type) {    case DHT11:    case DHT12:      f = data[0] + data[1] * 0.1;      break;    case DHT22:    case DHT21:      f = ((word)data[0]) << 8 | data[1];      f *= 0.1;      break;    }  }  return f;}/*! *  @brief  Compute Heat Index *          Simplified version that reads temp and humidity from sensor *  @param  isFahrenheit * 					true if fahrenheit, false if celcius (default *true) *	@return float heat index */float DHT::computeHeatIndex(bool isFahrenheit) {  float hi = computeHeatIndex(readTemperature(isFahrenheit), readHumidity(),                              isFahrenheit);  return hi;}/*! *  @brief  Compute Heat Index *  				Using both Rothfusz and Steadman's equations *					(http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml) *  @param  temperature *          temperature in selected scale *  @param  percentHumidity *          humidity in percent *  @param  isFahrenheit * 					true if fahrenheit, false if celcius *	@return float heat index */float DHT::computeHeatIndex(float temperature, float percentHumidity,                            bool isFahrenheit) {  float hi;  if (!isFahrenheit)    temperature = convertCtoF(temperature);  hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) +              (percentHumidity * 0.094));  if (hi > 79) {    hi = -42.379 + 2.04901523 * temperature + 10.14333127 * percentHumidity +         -0.22475541 * temperature * percentHumidity +         -0.00683783 * pow(temperature, 2) +         -0.05481717 * pow(percentHumidity, 2) +         0.00122874 * pow(temperature, 2) * percentHumidity +         0.00085282 * temperature * pow(percentHumidity, 2) +         -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2);    if ((percentHumidity < 13) && (temperature >= 80.0) &&        (temperature <= 112.0))      hi -= ((13.0 - percentHumidity) * 0.25) *            sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);    else if ((percentHumidity > 85.0) && (temperature >= 80.0) &&             (temperature <= 87.0))      hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);  }  return isFahrenheit ? hi : convertFtoC(hi);}/*! *  @brief  Read value from sensor or return last one from less than two *seconds. *  @param  force *          true if using force mode *	@return float value */bool DHT::read(bool force) {  // Check if sensor was read less than two seconds ago and return early  // to use last reading.  uint32_t currenttime = millis();  if (!force && ((currenttime - _lastreadtime) < MIN_INTERVAL)) {    return _lastresult; // return last correct measurement  }  _lastreadtime = currenttime;  // Reset 40 bits of received data to zero.  data[0] = data[1] = data[2] = data[3] = data[4] = 0;#if defined(ESP8266)  yield(); // Handle WiFi / reset software watchdog#endif  // Send start signal.  See DHT datasheet for full signal diagram:  //   http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf  // Go into high impedence state to let pull-up raise data line level and  // start the reading process.  pinMode(_pin, INPUT_PULLUP);  delay(1);  // First set data line low for a period according to sensor type  pinMode(_pin, OUTPUT);  digitalWrite(_pin, LOW);  switch (_type) {  case DHT22:  case DHT21:    delayMicroseconds(1100); // data sheet says "at least 1ms"    break;  case DHT11:  default:    delay(20); // data sheet says at least 18ms, 20ms just to be safe    break;  }  uint32_t cycles[80];  {    // End the start signal by setting data line high for 40 microseconds.    pinMode(_pin, INPUT_PULLUP);    // Delay a moment to let sensor pull data line low.    delayMicroseconds(pullTime);    // Now start reading the data line to get the value from the DHT sensor.    // Turn off interrupts temporarily because the next sections    // are timing critical and we don't want any interruptions.    InterruptLock lock;    // First expect a low signal for ~80 microseconds followed by a high signal    // for ~80 microseconds again.    if (expectPulse(LOW) == TIMEOUT) {      DEBUG_PRINTLN(F("DHT timeout waiting for start signal low pulse."));      _lastresult = false;      return _lastresult;    }    if (expectPulse(HIGH) == TIMEOUT) {      DEBUG_PRINTLN(F("DHT timeout waiting for start signal high pulse."));      _lastresult = false;      return _lastresult;    }    // Now read the 40 bits sent by the sensor.  Each bit is sent as a 50    // microsecond low pulse followed by a variable length high pulse.  If the    // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds    // then it's a 1.  We measure the cycle count of the initial 50us low pulse    // and use that to compare to the cycle count of the high pulse to determine    // if the bit is a 0 (high state cycle count < low state cycle count), or a    // 1 (high state cycle count > low state cycle count). Note that for speed    // all the pulses are read into a array and then examined in a later step.    for (int i = 0; i < 80; i += 2) {      cycles[i] = expectPulse(LOW);      cycles[i + 1] = expectPulse(HIGH);    }  } // Timing critical code is now complete.  // Inspect pulses and determine which ones are 0 (high state cycle count < low  // state cycle count), or 1 (high state cycle count > low state cycle count).  for (int i = 0; i < 40; ++i) {    uint32_t lowCycles = cycles[2 * i];    uint32_t highCycles = cycles[2 * i + 1];    if ((lowCycles == TIMEOUT) || (highCycles == TIMEOUT)) {      DEBUG_PRINTLN(F("DHT timeout waiting for pulse."));      _lastresult = false;      return _lastresult;    }    data[i / 8] <<= 1;    // Now compare the low and high cycle times to see if the bit is a 0 or 1.    if (highCycles > lowCycles) {      // High cycles are greater than 50us low cycle count, must be a 1.      data[i / 8] |= 1;    }    // Else high cycles are less than (or equal to, a weird case) the 50us low    // cycle count so this must be a zero.  Nothing needs to be changed in the    // stored data.  }  DEBUG_PRINTLN(F("Received from DHT:"));  DEBUG_PRINT(data[0], HEX);  DEBUG_PRINT(F(", "));  DEBUG_PRINT(data[1], HEX);  DEBUG_PRINT(F(", "));  DEBUG_PRINT(data[2], HEX);  DEBUG_PRINT(F(", "));  DEBUG_PRINT(data[3], HEX);  DEBUG_PRINT(F(", "));  DEBUG_PRINT(data[4], HEX);  DEBUG_PRINT(F(" =? "));  DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX);  // Check we read 40 bits and that the checksum matches.  if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {    _lastresult = true;    return _lastresult;  } else {    DEBUG_PRINTLN(F("DHT checksum failure!"));    _lastresult = false;    return _lastresult;  }}// Expect the signal line to be at the specified level for a period of time and// return a count of loop cycles spent at that level (this cycle count can be// used to compare the relative time of two pulses).  If more than a millisecond// ellapses without the level changing then the call fails with a 0 response.// This is adapted from Arduino's pulseInLong function (which is only available// in the very latest IDE versions)://   https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.cuint32_t DHT::expectPulse(bool level) {#if (F_CPU > 16000000L)  uint32_t count = 0;#else  uint16_t count = 0; // To work fast enough on slower AVR boards#endif// On AVR platforms use direct GPIO port access as it's much faster and better// for catching pulses that are 10's of microseconds in length:#ifdef __AVR  uint8_t portState = level ? _bit : 0;  while ((*portInputRegister(_port) & _bit) == portState) {    if (count++ >= _maxcycles) {      return TIMEOUT; // Exceeded timeout, fail.    }  }// Otherwise fall back to using digitalRead (this seems to be necessary on// ESP8266 right now, perhaps bugs in direct port access functions?).#else  while (digitalRead(_pin) == level) {    if (count++ >= _maxcycles) {      return TIMEOUT; // Exceeded timeout, fail.    }  }#endif  return count;}
 |