2018-07-04 18:56:39 +02:00

425 lines
12 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************
DHT Temperature & Humidity Sensor library for Arduino & ESP32.
Features:
- Support for DHT11 and DHT22/AM2302/RHT03
- Auto detect sensor model
- Very low memory footprint
- Very small code
https://github.com/beegee-tokyo/arduino-DHTesp
Written by Mark Ruys, mark@paracas.nl.
Updated to work with ESP32 by Bernd Giesecke, bernd@giesecke.tk
GNU General Public License, check LICENSE for more information.
All text above must be included in any redistribution.
Datasheets:
- http://www.micro4you.com/files/sensor/DHT11.pdf
- http://www.adafruit.com/datasheets/DHT22.pdf
- http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Weather/RHT03.pdf
- http://meteobox.tk/files/AM2302.pdf
Changelog:
2013-06-10: Initial version
2013-06-12: Refactored code
2013-07-01: Add a resetTimer method
2017-12-12: Added task switch disable
Added computeHeatIndex function from Adafruit DNT library
2017-12-14: Added computeDewPoint function from idDHTLib Library
Added getComfortRatio function from libDHT Library
2017-12-15: Added computePerception function
2018-01-02: Added example for multiple sensors usage.
2018-01-03: Added function getTempAndHumidity which returns temperature and humidity in one call.
2018-01-03: Added retry in case the reading from the sensor fails with a timeout.
2018-01-08: Added ESP8266 (and probably AVR) compatibility.
2018-03-11: Updated DHT example
******************************************************************/
#include "DHTesp.h"
void DHTesp::setup(uint8_t pin, DHT_MODEL_t model)
{
DHTesp::pin = pin;
DHTesp::model = model;
DHTesp::resetTimer(); // Make sure we do read the sensor in the next readSensor()
if ( model == AUTO_DETECT) {
DHTesp::model = DHT22;
readSensor();
if ( error == ERROR_TIMEOUT ) {
DHTesp::model = DHT11;
// Warning: in case we auto detect a DHT11, you should wait at least 1000 msec
// before your first read request. Otherwise you will get a time out error.
}
}
//Set default comfort profile.
//In computing these constants the following reference was used
//http://epb.apogee.net/res/refcomf.asp
//It was simplified as 4 straight lines and added very little skew on
//the vertical lines (+0.1 on x for C,D)
//The for points used are(from top left, clock wise)
//A(30%, 30*C) B(70%, 26.2*C) C(70.1%, 20.55*C) D(30.1%, 22.22*C)
//On the X axis we have the rel humidity in % and on the Y axis the temperature in *C
//Too hot line AB
m_comfort.m_tooHot_m = -0.095;
m_comfort.m_tooHot_b = 32.85;
//Too humid line BC
m_comfort.m_tooHumid_m = -56.5;
m_comfort.m_tooHumid_b = 3981.2;
//Too cold line DC
m_comfort.m_tooCold_m = -0.04175;
m_comfort.m_tooHCold_b = 23.476675;
//Too dry line AD
m_comfort.m_tooDry_m = -77.8;
m_comfort.m_tooDry_b = 2364;
}
void DHTesp::resetTimer()
{
DHTesp::lastReadTime = millis() - 3000;
}
float DHTesp::getHumidity()
{
readSensor();
if ( error == ERROR_TIMEOUT ) { // Try a second time to read
readSensor();
}
return humidity;
}
float DHTesp::getTemperature()
{
readSensor();
if ( error == ERROR_TIMEOUT ) { // Try a second time to read
readSensor();
}
return temperature;
}
TempAndHumidity DHTesp::getTempAndHumidity()
{
readSensor();
if ( error == ERROR_TIMEOUT ) { // Try a second time to read
readSensor();
}
values.temperature = temperature;
values.humidity = humidity;
return values;
}
#ifndef OPTIMIZE_SRAM_SIZE
const char* DHTesp::getStatusString()
{
switch ( error ) {
case DHTesp::ERROR_TIMEOUT:
return "TIMEOUT";
case DHTesp::ERROR_CHECKSUM:
return "CHECKSUM";
default:
return "OK";
}
}
#else
// At the expense of 26 bytes of extra PROGMEM, we save 11 bytes of
// SRAM by using the following method:
prog_char P_OK[] PROGMEM = "OK";
prog_char P_TIMEOUT[] PROGMEM = "TIMEOUT";
prog_char P_CHECKSUM[] PROGMEM = "CHECKSUM";
const char *DHTesp::getStatusString() {
prog_char *c;
switch ( error ) {
case DHTesp::ERROR_CHECKSUM:
c = P_CHECKSUM; break;
case DHTesp::ERROR_TIMEOUT:
c = P_TIMEOUT; break;
default:
c = P_OK; break;
}
static char buffer[9];
strcpy_P(buffer, c);
return buffer;
}
#endif
void DHTesp::readSensor()
{
// Make sure we don't poll the sensor too often
// - Max sample rate DHT11 is 1 Hz (duty cicle 1000 ms)
// - Max sample rate DHT22 is 0.5 Hz (duty cicle 2000 ms)
unsigned long startTime = millis();
if ( (unsigned long)(startTime - lastReadTime) < (model == DHT11 ? 999L : 1999L) ) {
return;
}
lastReadTime = startTime;
temperature = NAN;
humidity = NAN;
uint16_t rawHumidity = 0;
uint16_t rawTemperature = 0;
uint16_t data = 0;
// Request sample
digitalWrite(pin, LOW); // Send start signal
pinMode(pin, OUTPUT);
if ( model == DHT11 ) {
delay(18);
}
else {
// This will fail for a DHT11 - that's how we can detect such a device
delayMicroseconds(800);
}
pinMode(pin, INPUT);
digitalWrite(pin, HIGH); // Switch bus to receive data
// We're going to read 83 edges:
// - First a FALLING, RISING, and FALLING edge for the start bit
// - Then 40 bits: RISING and then a FALLING edge per bit
// To keep our code simple, we accept any HIGH or LOW reading if it's max 85 usecs long
#ifdef ESP32
// ESP32 is a multi core / multi processing chip
// It is necessary to disable task switches during the readings
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&mux);
#else
cli();
#endif
for ( int8_t i = -3 ; i < 2 * 40; i++ ) {
byte age;
startTime = micros();
do {
age = (unsigned long)(micros() - startTime);
if ( age > 90 ) {
error = ERROR_TIMEOUT;
#ifdef ESP32
portEXIT_CRITICAL(&mux);
#else
sei();
#endif
return;
}
}
while ( digitalRead(pin) == (i & 1) ? HIGH : LOW );
if ( i >= 0 && (i & 1) ) {
// Now we are being fed our 40 bits
data <<= 1;
// A zero max 30 usecs, a one at least 68 usecs.
if ( age > 30 ) {
data |= 1; // we got a one
}
}
switch ( i ) {
case 31:
rawHumidity = data;
break;
case 63:
rawTemperature = data;
data = 0;
break;
}
}
#ifdef ESP32
portEXIT_CRITICAL(&mux);
#else
sei();
#endif
// Verify checksum
if ( (byte)(((byte)rawHumidity) + (rawHumidity >> 8) + ((byte)rawTemperature) + (rawTemperature >> 8)) != data ) {
error = ERROR_CHECKSUM;
return;
}
// Store readings
if ( model == DHT11 ) {
humidity = rawHumidity >> 8;
temperature = rawTemperature >> 8;
}
else {
humidity = rawHumidity * 0.1;
if ( rawTemperature & 0x8000 ) {
rawTemperature = -(int16_t)(rawTemperature & 0x7FFF);
}
temperature = ((int16_t)rawTemperature) * 0.1;
}
error = ERROR_NONE;
}
//boolean isFahrenheit: True == Fahrenheit; False == Celcius
float DHTesp::computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit) {
// Using both Rothfusz and Steadman's equations
// http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
float hi;
if (!isFahrenheit) {
temperature = toFahrenheit(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 : toCelsius(hi);
}
//boolean isFahrenheit: True == Fahrenheit; False == Celcius
float DHTesp::computeDewPoint(float temperature, float percentHumidity, bool isFahrenheit) {
// reference: http://wahiduddin.net/calc/density_algorithms.htm
if (isFahrenheit) {
temperature = toCelsius(temperature);
}
double A0 = 373.15 / (273.15 + (double) temperature);
double SUM = -7.90298 * (A0 - 1);
SUM += 5.02808 * log10(A0);
SUM += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / A0))) - 1) ;
SUM += 8.1328e-3 * (pow(10, (-3.49149 * (A0 - 1))) - 1) ;
SUM += log10(1013.246);
double VP = pow(10, SUM - 3) * (double) percentHumidity;
double Td = log(VP / 0.61078); // temp var
Td = (241.88 * Td) / (17.558 - Td);
return isFahrenheit ? toFahrenheit(Td) : Td;
}
//boolean isFahrenheit: True == Fahrenheit; False == Celcius
byte DHTesp::computePerception(float temperature, float percentHumidity, bool isFahrenheit) {
// Computing human perception from dew point
// reference: https://en.wikipedia.org/wiki/Dew_point ==> Relationship to human comfort
// reference: Horstmeyer, Steve (2006-08-15). "Relative Humidity....Relative to What? The Dew Point Temperature...a better approach". Steve Horstmeyer, Meteorologist, WKRC TV, Cincinnati, Ohio, USA. Retrieved 2009-08-20.
// Using table
// Return value Dew point Human perception[6]
// 7 Over 26 °C Severely high, even deadly for asthma related illnesses
// 6 2426 °C Extremely uncomfortable, oppressive
// 5 2124 °C Very humid, quite uncomfortable
// 4 1821 °C Somewhat uncomfortable for most people at upper edge
// 3 1618 °C OK for most, but all perceive the humidity at upper edge
// 2 1316 °C Comfortable
// 1 1012 °C Very comfortable
// 0 Under 10 °C A bit dry for some
if (isFahrenheit) {
temperature = toCelsius(temperature);
}
float dewPoint = computeDewPoint(temperature, percentHumidity);
if (dewPoint < 10.0f) {
return Perception_Dry;
} else if (dewPoint < 13.0f) {
return Perception_VeryComfy;
} else if (dewPoint < 16.0f) {
return Perception_Comfy;
} else if (dewPoint < 18.0f) {
return Perception_Ok;
} else if (dewPoint < 21.0f) {
return Perception_UnComfy;
} else if (dewPoint < 24.0f) {
return Perception_QuiteUnComfy;
} else if (dewPoint < 26.0f) {
return Perception_VeryUnComfy;
}
// else dew >= 26.0
return Perception_SevereUncomfy;
}
//boolean isFahrenheit: True == Fahrenheit; False == Celcius
float DHTesp::getComfortRatio(ComfortState& destComfortStatus, float temperature, float percentHumidity, bool isFahrenheit) {
float ratio = 100; //100%
float distance = 0;
float kTempFactor = 3; //take into account the slope of the lines
float kHumidFactor = 0.1; //take into account the slope of the lines
uint8_t tempComfort = 0;
if (isFahrenheit) {
temperature = toCelsius(temperature);
}
destComfortStatus = Comfort_OK;
distance = m_comfort.distanceTooHot(temperature, percentHumidity);
if(distance > 0)
{
//update the comfort descriptor
tempComfort += (uint8_t)Comfort_TooHot;
//decrease the comfot ratio taking the distance into account
ratio -= distance * kTempFactor;
}
distance = m_comfort.distanceTooHumid(temperature, percentHumidity);
if(distance > 0)
{
//update the comfort descriptor
tempComfort += (uint8_t)Comfort_TooHumid;
//decrease the comfot ratio taking the distance into account
ratio -= distance * kHumidFactor;
}
distance = m_comfort.distanceTooCold(temperature, percentHumidity);
if(distance > 0)
{
//update the comfort descriptor
tempComfort += (uint8_t)Comfort_TooCold;
//decrease the comfot ratio taking the distance into account
ratio -= distance * kTempFactor;
}
distance = m_comfort.distanceTooDry(temperature, percentHumidity);
if(distance > 0)
{
//update the comfort descriptor
tempComfort += (uint8_t)Comfort_TooDry;
//decrease the comfot ratio taking the distance into account
ratio -= distance * kHumidFactor;
}
destComfortStatus = (ComfortState)tempComfort;
if(ratio < 0)
ratio = 0;
return ratio;
}