You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
12 KiB
428 lines
12 KiB
/* DHT12 library
|
|
|
|
MIT license
|
|
written by Renzo Mischianti
|
|
*/
|
|
|
|
#include "DHT12.h"
|
|
#include "Wire.h"
|
|
|
|
// Default is i2c on default pin with default DHT12 adress
|
|
DHT12::DHT12(void) {
|
|
|
|
}
|
|
|
|
DHT12::DHT12(uint8_t addressOrPin, bool oneWire) {
|
|
_isOneWire = oneWire;
|
|
if (oneWire) {
|
|
_pin = addressOrPin;
|
|
#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
|
|
// basd on the speed of the processor.
|
|
|
|
DEBUG_PRINTLN("PIN MODE");
|
|
} else {
|
|
_address = addressOrPin;
|
|
DEBUG_PRINTLN("I2C MODE");
|
|
}
|
|
}
|
|
|
|
// Is not good idea use other pin for i2c as standard on Arduino you can get lag.
|
|
// The lag happens when you choose "different pins", because you are then using a
|
|
// slow software emulation of the I2C hardware. The built in I2C hardware has fixed pin assignments.
|
|
#ifndef __AVR
|
|
DHT12::DHT12(uint8_t sda, uint8_t scl) {
|
|
_isOneWire = false;
|
|
_sda = sda;
|
|
_scl = scl;
|
|
}
|
|
|
|
DHT12::DHT12(uint8_t sda, uint8_t scl, uint8_t address) {
|
|
_isOneWire = false;
|
|
_sda = sda;
|
|
_scl = scl;
|
|
_address = address;
|
|
}
|
|
#endif
|
|
|
|
void DHT12::begin() {
|
|
_lastreadtime = -(MIN_ELAPSED_TIME + 1);
|
|
if (_isOneWire) {
|
|
// 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.
|
|
DEBUG_PRINT("Max clock cycles: ");
|
|
DEBUG_PRINTLN(_maxcycles, DEC);
|
|
} else {
|
|
#ifndef __AVR
|
|
Wire.begin(_sda, _scl);
|
|
#else
|
|
// Default pin for AVR some problem on software emulation
|
|
// #define SCL_PIN _scl
|
|
// #define SDA_PIN _sda
|
|
Wire.begin();
|
|
#endif
|
|
DEBUG_PRINT("I2C Inizialization: sda, scl: ");
|
|
DEBUG_PRINT(_sda);
|
|
DEBUG_PRINT(",");
|
|
DEBUG_PRINTLN(_scl);
|
|
}
|
|
}
|
|
|
|
DHT12::ReadStatus DHT12::readStatus(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_ELAPSED_TIME)) {
|
|
return _lastresult; // return last correct measurement
|
|
}
|
|
_lastreadtime = currenttime;
|
|
|
|
if (_isOneWire) {
|
|
// Reset 40 bits of received data to zero.
|
|
data[0] = data[1] = data[2] = data[3] = data[4] = 0;
|
|
|
|
// 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);
|
|
|
|
// First set data line low for 20 milliseconds.
|
|
pinMode(_pin, OUTPUT);
|
|
digitalWrite(_pin, LOW);
|
|
delay(20);
|
|
|
|
uint32_t cycles[80];
|
|
{
|
|
// Turn off interrupts temporarily because the next sections are timing critical
|
|
// and we don't want any interruptions.
|
|
InterruptLock lock;
|
|
|
|
// 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_PULLUP);
|
|
delayMicroseconds(10); // Delay a bit to let sensor pull data line low.
|
|
|
|
// 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 = ERROR_TIMEOUT_LOW;
|
|
return _lastresult;
|
|
}
|
|
if (expectPulse(HIGH) == 0) {
|
|
DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse."));
|
|
_lastresult = ERROR_TIMEOUT_HIGH;
|
|
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);
|
|
}
|
|
|
|
// 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 == 0) || (highCycles == 0)) {
|
|
DEBUG_PRINTLN(F("Timeout waiting for pulse."));
|
|
_lastresult = ERROR_TIMEOUT;
|
|
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:"));
|
|
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);
|
|
|
|
DHT12::ReadStatus cks = DHT12::_checksum();
|
|
if (cks != OK) {
|
|
DEBUG_PRINTLN("CHECKSUM ERROR!");
|
|
_lastresult = cks;
|
|
return cks;
|
|
}
|
|
|
|
_lastresult = OK;
|
|
return OK;
|
|
}
|
|
// return DHT12::_readSensor(DHTLIB_DHT_WAKEUP, DHTLIB_DHT_LEADING_ZEROS);
|
|
// return DHT12::_readSensor(DHTLIB_DHT11_WAKEUP, DHTLIB_DHT11_LEADING_ZEROS);
|
|
|
|
} else {
|
|
DEBUG_PRINT("I2C START READING..");
|
|
Wire.beginTransmission(_address);
|
|
Wire.write(0);
|
|
if (Wire.endTransmission() != 0) {
|
|
DEBUG_PRINTLN("CONNECTION ERROR!");
|
|
_lastresult = ERROR_CONNECT;
|
|
return _lastresult;
|
|
}
|
|
Wire.requestFrom(_address, (uint8_t) 5);
|
|
for (uint8_t i = 0; i < 5; ++i) {
|
|
data[i] = Wire.read();
|
|
DEBUG_PRINTLN(data[i]);
|
|
}
|
|
|
|
delay(1);
|
|
if (Wire.available() != 0) {
|
|
DEBUG_PRINTLN("TIMEOUT ERROR!");
|
|
_lastresult = ERROR_TIMEOUT;
|
|
return _lastresult;
|
|
}
|
|
|
|
DHT12::ReadStatus cks = DHT12::_checksum();
|
|
if (cks != OK) {
|
|
DEBUG_PRINTLN("CHECKSUM ERROR!");
|
|
_lastresult = cks;
|
|
return cks;
|
|
}
|
|
|
|
DEBUG_PRINTLN("...READING OK");
|
|
_lastresult = OK;
|
|
return _lastresult;
|
|
}
|
|
}
|
|
|
|
bool DHT12::read(bool force) {
|
|
ReadStatus chk = DHT12::readStatus(force);
|
|
DEBUG_PRINT(F("\nRead sensor: "));
|
|
DEBUG_PRINT((chk != DHT12::OK));
|
|
switch (chk) {
|
|
case DHT12::OK:
|
|
DEBUG_PRINTLN(F("OK"));
|
|
break;
|
|
case DHT12::ERROR_CHECKSUM:
|
|
DEBUG_PRINTLN(F("Checksum error"))
|
|
;
|
|
break;
|
|
case DHT12::ERROR_TIMEOUT:
|
|
DEBUG_PRINTLN(F("Timeout error"))
|
|
;
|
|
break;
|
|
case DHT12::ERROR_TIMEOUT_HIGH:
|
|
DEBUG_PRINTLN(F("Timeout error high"))
|
|
;
|
|
break;
|
|
case DHT12::ERROR_TIMEOUT_LOW:
|
|
DEBUG_PRINTLN(F("Timeout error low"))
|
|
;
|
|
break;
|
|
case DHT12::ERROR_CONNECT:
|
|
DEBUG_PRINTLN(F("Connect error"))
|
|
;
|
|
break;
|
|
case DHT12::ERROR_ACK_L:
|
|
DEBUG_PRINTLN(F("AckL error"))
|
|
;
|
|
break;
|
|
case DHT12::ERROR_ACK_H:
|
|
DEBUG_PRINTLN(F("AckH error"))
|
|
;
|
|
break;
|
|
case DHT12::ERROR_UNKNOWN:
|
|
DEBUG_PRINTLN(F("Unknown error DETECTED"))
|
|
;
|
|
break;
|
|
case DHT12::NONE:
|
|
DEBUG_PRINTLN(F("No result"))
|
|
;
|
|
break;
|
|
default:
|
|
DEBUG_PRINTLN(F("Unknown error"))
|
|
;
|
|
break;
|
|
}
|
|
return (chk == DHT12::OK);
|
|
}
|
|
|
|
float DHT12::convertCtoF(float c) {
|
|
return c * 1.8 + 32;
|
|
}
|
|
|
|
float DHT12::convertFtoC(float f) {
|
|
return (f - 32) * 0.55555;
|
|
}
|
|
|
|
//boolean isFahrenheit: True == Fahrenheit; False == Celcius
|
|
float DHT12::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 = 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);
|
|
}
|
|
|
|
float DHT12::readHumidity(bool force) {
|
|
DEBUG_PRINTLN("----------------------------");
|
|
float humidity = NAN;
|
|
|
|
if (_isOneWire) {
|
|
if (DHT12::read(force)) {
|
|
DEBUG_PRINT(data[0]);
|
|
humidity = data[0];
|
|
}
|
|
} else {
|
|
if (DHT12::read(force)) {
|
|
humidity = (data[0] + (float) data[1] / 10);
|
|
}
|
|
}
|
|
return humidity;
|
|
}
|
|
|
|
//boolean S == Scale. True == Fahrenheit; False == Celcius
|
|
float DHT12::readTemperature(bool scale, bool force) {
|
|
float temperature = NAN;
|
|
if (_isOneWire) {
|
|
if (DHT12::read(force)) {
|
|
temperature = data[2];
|
|
if (scale) {
|
|
temperature = convertCtoF(temperature);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
bool r = DHT12::read(force);
|
|
DEBUG_PRINT("READ ---> ");
|
|
DEBUG_PRINTLN(r);
|
|
if (r) {
|
|
temperature = (data[2] + (float) data[3] / 10); //((data[2] & 0x7F)*256 + data[3]);
|
|
// if (data[2] & 0x80) // negative temperature
|
|
// temperature = -temperature;
|
|
|
|
if (scale) {
|
|
temperature = convertCtoF(temperature);
|
|
}
|
|
}
|
|
}
|
|
return temperature;
|
|
|
|
}
|
|
|
|
#include <math.h>
|
|
// dewPoint function NOAA
|
|
// reference (1) : http://wahiduddin.net/calc/density_algorithms.htm
|
|
// reference (2) : http://www.colorado.edu/geography/weather_station/Geog_site/about.htm
|
|
//
|
|
//boolean S == Scale. True == Fahrenheit; False == Celcius
|
|
float DHT12::dewPoint(float temperature, float humidity, bool isFahrenheit) {
|
|
// sloppy but good approximation for 0 ... +70 °C with max. deviation less than 0.25 °C
|
|
|
|
float temp;
|
|
|
|
if(!isFahrenheit){
|
|
temp = temperature;
|
|
} else {
|
|
temp = convertFtoC(temperature);
|
|
}
|
|
|
|
float humi = humidity;
|
|
float ans = (temp - (14.55 + 0.114 * temp) * (1 - (0.01 * humi)) - pow(((2.5 + 0.007 * temp) * (1 - (0.01 * humi))),3) - (15.9 + 0.117 * temp) * pow((1 - (0.01 * humi)), 14));
|
|
|
|
if(!isFahrenheit){
|
|
return ans; // returns dew Point in Celsius
|
|
}
|
|
|
|
return convertCtoF(ans); // returns dew Point in Fahrenheit
|
|
}
|
|
|
|
//////// PRIVATE
|
|
DHT12::ReadStatus DHT12::_checksum() {
|
|
uint8_t sum = data[0] + data[1] + data[2] + data[3];
|
|
if (data[4] != sum)
|
|
return ERROR_CHECKSUM;
|
|
return OK;
|
|
}
|
|
|
|
// 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.c
|
|
uint32_t DHT12::expectPulse(bool level) {
|
|
uint32_t count = 0;
|
|
// 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 0; // 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 0; // Exceeded timeout, fail.
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return count;
|
|
}
|
|
|