mirror of
https://git.mirrors.martin98.com/https://github.com/luc-github/ESP3D.git
synced 2025-08-04 19:40:39 +08:00

Add Time Support (server + manual setup), only used for ESP32 FS currently as ESP8266 SPIFFS does not support Time, need to wait for LittleFS may be ? Add DHT support Add Pin reset support Add Base for Display Add libraries for new supported features Add /config handle as shortcut for [ESP420]plain to be used in embedded page Code refactoring for defines, use less Define as switches but more define as values for switches Clean warnings Lot of small bug fixes Add docs for [ESPXXX] commands
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;
|
||
}
|