From 9d7a9da8ae6cc5f6b6dd5be62d787f779e640a2a Mon Sep 17 00:00:00 2001 From: Tony DiCola Date: Fri, 26 Jun 2015 15:14:17 -0700 Subject: [PATCH 1/2] Refactor algorithm to dynamically adjust to faster/slower processors. Fix #35 #30. --- DHT.cpp | 209 ++++++++++++++++++------------- DHT.h | 101 +++++++++------ examples/DHTtester/DHTtester.ino | 25 ++-- library.properties | 2 +- 4 files changed, 196 insertions(+), 141 deletions(-) diff --git a/DHT.cpp b/DHT.cpp index 2ef244c..92ebcf8 100644 --- a/DHT.cpp +++ b/DHT.cpp @@ -1,4 +1,4 @@ -/* DHT library +/* DHT library MIT license written by Adafruit Industries @@ -9,8 +9,9 @@ written by Adafruit Industries DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) { _pin = pin; _type = type; - _count = count; - firstreading = true; + _firstreading = true; + // Note that count is now ignored as the DHT reading algorithm adjusts itself + // basd on the speed of the processor. } void DHT::begin(void) { @@ -22,158 +23,198 @@ void DHT::begin(void) { //boolean S == Scale. True == Farenheit; False == Celcius float DHT::readTemperature(bool S) { - float f; + float f = NAN; if (read()) { switch (_type) { case DHT11: f = data[2]; - if(S) - f = convertCtoF(f); - - return f; + if(S) { + f = convertCtoF(f); + } + break; case DHT22: case DHT21: f = data[2] & 0x7F; f *= 256; f += data[3]; f /= 10; - if (data[2] & 0x80) - f *= -1; - if(S) - f = convertCtoF(f); - - return f; + if (data[2] & 0x80) { + f *= -1; + } + if(S) { + f = convertCtoF(f); + } + break; } } - return NAN; + return f; } float DHT::convertCtoF(float c) { - return c * 9 / 5 + 32; + return c * 9 / 5 + 32; } float DHT::convertFtoC(float f) { - return (f - 32) * 5 / 9; + return (f - 32) * 5 / 9; } float DHT::readHumidity(void) { - float f; + float f = NAN; if (read()) { switch (_type) { case DHT11: f = data[0]; - return f; + break; case DHT22: case DHT21: f = data[0]; f *= 256; f += data[1]; f /= 10; - return f; + break; } } - return NAN; + return f; } float DHT::computeHeatIndex(float tempFahrenheit, float percentHumidity) { // Adapted from equation at: https://github.com/adafruit/DHT-sensor-library/issues/9 and // Wikipedia: http://en.wikipedia.org/wiki/Heat_index - return -42.379 + - 2.04901523 * tempFahrenheit + + return -42.379 + + 2.04901523 * tempFahrenheit + 10.14333127 * percentHumidity + -0.22475541 * tempFahrenheit*percentHumidity + -0.00683783 * pow(tempFahrenheit, 2) + - -0.05481717 * pow(percentHumidity, 2) + - 0.00122874 * pow(tempFahrenheit, 2) * percentHumidity + + -0.05481717 * pow(percentHumidity, 2) + + 0.00122874 * pow(tempFahrenheit, 2) * percentHumidity + 0.00085282 * tempFahrenheit*pow(percentHumidity, 2) + -0.00000199 * pow(tempFahrenheit, 2) * pow(percentHumidity, 2); } - boolean DHT::read(void) { - uint8_t laststate = HIGH; - uint8_t counter = 0; - uint8_t j = 0, i; - unsigned long currenttime; - // Check if sensor was read less than two seconds ago and return early // to use last reading. - currenttime = millis(); + uint32_t currenttime = millis(); if (currenttime < _lastreadtime) { // ie there was a rollover _lastreadtime = 0; } - if (!firstreading && ((currenttime - _lastreadtime) < 2000)) { - return true; // return last correct measurement - //delay(2000 - (currenttime - _lastreadtime)); + if (!_firstreading && ((currenttime - _lastreadtime) < 2000)) { + return _lastresult; // return last correct measurement } - firstreading = false; - /* - Serial.print("Currtime: "); Serial.print(currenttime); - Serial.print(" Lasttime: "); Serial.print(_lastreadtime); - */ + _firstreading = false; _lastreadtime = millis(); + // Reset 40 bits of received data to zero. data[0] = data[1] = data[2] = data[3] = data[4] = 0; - - // pull the pin high and wait 250 milliseconds + + // 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. digitalWrite(_pin, HIGH); delay(250); - // now pull it low for ~20 milliseconds + // First set data line low for 20 milliseconds. pinMode(_pin, OUTPUT); digitalWrite(_pin, LOW); delay(20); + + // Turn off interrupts temporarily because the next sections are timing critical + // and we don't want any interruptions. noInterrupts(); + + // End the start signal by setting data line high for 40 microseconds. digitalWrite(_pin, HIGH); delayMicroseconds(40); + + // Now start reading the data line to get the value from the DHT sensor. pinMode(_pin, INPUT); + delayMicroseconds(10); // Delay a bit to let sensor pull data line low. - // read in timings - for ( i=0; i< MAXTIMINGS; i++) { - counter = 0; - while (digitalRead(_pin) == laststate) { - counter++; - delayMicroseconds(1); - if (counter == 255) { - break; - } - } - laststate = digitalRead(_pin); - - if (counter == 255) break; - - // ignore first 3 transitions - if ((i >= 4) && (i%2 == 0)) { - // shove each bit into the storage bytes - data[j/8] <<= 1; - if (counter > _count) - data[j/8] |= 1; - j++; - } - + // First expect a low signal for ~80 microseconds followed by a high signal + // for ~80 microseconds again. + if (expectPulse(LOW) == 0) { + DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse.")); + _lastresult = false; + return _lastresult; + } + if (expectPulse(HIGH) == 0) { + DEBUG_PRINTLN(F("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). + for (int i=0; i<40; ++i) { + uint32_t lowCycles = expectPulse(LOW); + if (lowCycles == 0) { + DEBUG_PRINTLN(F("Timeout waiting for bit low pulse.")); + _lastresult = false; + return _lastresult; + } + uint32_t highCycles = expectPulse(HIGH); + if (highCycles == 0) { + DEBUG_PRINTLN(F("Timeout waiting for bit high 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. + } + + // Re-enable interrupts, timing critical code is complete. interrupts(); - - /* - Serial.println(j, DEC); - Serial.print(data[0], HEX); Serial.print(", "); - Serial.print(data[1], HEX); Serial.print(", "); - Serial.print(data[2], HEX); Serial.print(", "); - Serial.print(data[3], HEX); Serial.print(", "); - Serial.print(data[4], HEX); Serial.print(" =? "); - Serial.println(data[0] + data[1] + data[2] + data[3], HEX); - */ - // check we read 40 bits and that the checksum matches - if ((j >= 40) && - (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) ) { - return true; + DEBUG_PRINTLN(F("Received:")); + 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], 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("Checksum failure!")); + _lastresult = false; + return _lastresult; } - - - return false; - +} + +// 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. +uint32_t DHT::expectPulse(bool level) { + uint32_t count = 0; + uint32_t end = micros() + 1000; + // Loop while counting cycles until the level changes. + while (digitalRead(_pin) == level) { + count++; + if (micros() >= end) { + // Exceeded timeout waiting for level to change, fail. + return 0; + } + } + return count; } diff --git a/DHT.h b/DHT.h index 5280f9c..9cf381d 100644 --- a/DHT.h +++ b/DHT.h @@ -1,41 +1,60 @@ -#ifndef DHT_H -#define DHT_H -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -/* DHT library - -MIT license -written by Adafruit Industries -*/ - -// how many timing transitions we need to keep track of. 2 * number bits + extra -#define MAXTIMINGS 85 - -#define DHT11 11 -#define DHT22 22 -#define DHT21 21 -#define AM2301 21 - -class DHT { - private: - uint8_t data[6]; - uint8_t _pin, _type, _count; - unsigned long _lastreadtime; - boolean firstreading; - - public: - DHT(uint8_t pin, uint8_t type, uint8_t count=6); - void begin(void); - float readTemperature(bool S=false); - float convertCtoF(float); - float convertFtoC(float); - float computeHeatIndex(float tempFahrenheit, float percentHumidity); - float readHumidity(void); - boolean read(void); - -}; -#endif +/* DHT library + +MIT license +written by Adafruit Industries +*/ +#ifndef DHT_H +#define DHT_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + + +// Uncomment to enable printing out nice debug messages. +//#define DHT_DEBUG + +// Define where debug output will be printed. +#define DEBUG_PRINTER Serial + +// Setup debug printing macros. +#ifdef DHT_DEBUG + #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +// Define types of sensors. +#define DHT11 11 +#define DHT22 22 +#define DHT21 21 +#define AM2301 21 + + +class DHT { + public: + DHT(uint8_t pin, uint8_t type, uint8_t count=6); + void begin(void); + float readTemperature(bool S=false); + float convertCtoF(float); + float convertFtoC(float); + float computeHeatIndex(float tempFahrenheit, float percentHumidity); + float readHumidity(void); + boolean read(void); + + private: + uint8_t data[6]; + uint8_t _pin, _type; + uint32_t _lastreadtime; + bool _firstreading; + bool _lastresult; + + uint32_t expectPulse(bool level); + +}; + +#endif diff --git a/examples/DHTtester/DHTtester.ino b/examples/DHTtester/DHTtester.ino index 021107f..ba70cf5 100644 --- a/examples/DHTtester/DHTtester.ino +++ b/examples/DHTtester/DHTtester.ino @@ -6,7 +6,7 @@ #define DHTPIN 2 // what pin we're connected to // Uncomment whatever type you're using! -//#define DHTTYPE DHT11 // DHT 11 +//#define DHTTYPE DHT11 // DHT 11 #define DHTTYPE DHT22 // DHT 22 (AM2302) //#define DHTTYPE DHT21 // DHT 21 (AM2301) @@ -17,21 +17,16 @@ // Connect pin 4 (on the right) of the sensor to GROUND // Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor -// Initialize DHT sensor for normal 16mhz Arduino +// Initialize DHT sensor. +// Note that older versions of this library took an optional third parameter to +// tweak the timings for faster processors. This parameter is no longer needed +// as the current DHT reading algorithm adjusts itself to work on faster procs. DHT dht(DHTPIN, DHTTYPE); -// NOTE: For working with a faster chip, like an Arduino Due or Teensy, you -// might need to increase the threshold for cycle counts considered a 1 or 0. -// You can do this by passing a 3rd parameter for this threshold. It's a bit -// of fiddling to find the right value, but in general the faster the CPU the -// higher the value. The default for a 16mhz AVR is a value of 6. For an -// Arduino Due that runs at 84mhz a value of 30 works. -// Example to initialize DHT sensor for Arduino Due: -//DHT dht(DHTPIN, DHTTYPE, 30); void setup() { - Serial.begin(9600); + Serial.begin(9600); Serial.println("DHTxx test!"); - + dht.begin(); } @@ -46,7 +41,7 @@ void loop() { float t = dht.readTemperature(); // Read temperature as Fahrenheit float f = dht.readTemperature(true); - + // Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t) || isnan(f)) { Serial.println("Failed to read from DHT sensor!"); @@ -57,10 +52,10 @@ void loop() { // Must send in temp in Fahrenheit! float hi = dht.computeHeatIndex(f, h); - Serial.print("Humidity: "); + Serial.print("Humidity: "); Serial.print(h); Serial.print(" %\t"); - Serial.print("Temperature: "); + Serial.print("Temperature: "); Serial.print(t); Serial.print(" *C "); Serial.print(f); diff --git a/library.properties b/library.properties index f0a3314..ab647c4 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DHT sensor library -version=1.0.0 +version=1.1.0 author=Adafruit maintainer=Adafruit sentence=Arduino library for DHT11, DHT22, etc Temp & Humidity Sensors From 729895339cc7bc05f5df9c553631055b049ab567 Mon Sep 17 00:00:00 2001 From: Tony DiCola Date: Fri, 26 Jun 2015 15:15:45 -0700 Subject: [PATCH 2/2] Integrate keywords.txt from pull #31 --- keywords.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 keywords.txt diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..146d4fa --- /dev/null +++ b/keywords.txt @@ -0,0 +1,22 @@ +########################################### +# Syntax Coloring Map For DHT-sensor-library +########################################### + +########################################### +# Datatypes (KEYWORD1) +########################################### + +DHT KEYWORD1 + +########################################### +# Methods and Functions (KEYWORD2) +########################################### + +begin KEYWORD2 +readTemperature KEYWORD2 +convertCtoF KEYWORD2 +convertFtoC KEYWORD2 +computeHeatIndex KEYWORD2 +readHumidity KEYWORD2 +read KEYWORD2 +