mirror of
https://git.mirrors.martin98.com/https://github.com/luc-github/ESP3D.git
synced 2025-06-06 02:36:49 +08:00
425 lines
12 KiB
C++
425 lines
12 KiB
C++
/******************************************************************
|
||
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 24–26 °C Extremely uncomfortable, oppressive
|
||
// 5 21–24 °C Very humid, quite uncomfortable
|
||
// 4 18–21 °C Somewhat uncomfortable for most people at upper edge
|
||
// 3 16–18 °C OK for most, but all perceive the humidity at upper edge
|
||
// 2 13–16 °C Comfortable
|
||
// 1 10–12 °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;
|
||
}
|