#include "Arduino.h"
#define TIMEOUT \
UINT32_MAX /**< Used programmatically for timeout. \
Not a timeout duration. Type: uint32_t. */
#define DHTPIN 4
#define ESP_OUT_REF 2
uint8_t pullTime = 55; // Time (in usec) to pull up data line before reading
uint32_t _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
// based on the speed of the processor.
bool _lastresult;
uint8_t data[5];
class InterruptLock {
public:
InterruptLock() {
}
~InterruptLock() {
}
};
void setup() {
Serial.begin(115200);
Serial.print("Max Timeout: ");
Serial.println(UINT32_MAX);
Serial.print("pullTime: ");
Serial.println(pullTime);
Serial.print("maxCycles: ");
Serial.println(_maxcycles);
pinMode(ESP_OUT_REF, OUTPUT);
// Mark End of Startup
delay(100);
digitalWrite(ESP_OUT_REF, LOW);
delay(100);
digitalWrite(ESP_OUT_REF, HIGH);
}
void loop() {
// 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.
pinMode(DHTPIN, INPUT_PULLUP);
delay(1);
// First set data line low for a period according to sensor type
pinMode(DHTPIN, OUTPUT);
digitalWrite(DHTPIN, LOW);
delayMicroseconds(1100); // data sheet says "at least 1ms"
uint32_t cycles[80];
{
// End the start signal by setting data line high for 40 microseconds.
pinMode(DHTPIN, INPUT_PULLUP);
// Delay a moment to let sensor pull data line low.
delayMicroseconds(pullTime);
// UNCOMMENT DO DUMP RAW SIGNAL
/*
for(int i = 0; i < TIMEOUT; i++) {
int val = digitalRead(DHTPIN);
Serial.print(val);
}*/
// Mark start pulse
digitalWrite(ESP_OUT_REF, LOW);
digitalWrite(ESP_OUT_REF, HIGH);
digitalWrite(ESP_OUT_REF, LOW);
// Turn off interrupts temporarily because the next sections
// are timing critical and we don't want any interruptions.
//InterruptLock lock;
// First expect a low signal for ~80 microseconds followed by a high signal
// for ~80 microseconds again.
if (expectPulse(LOW) == TIMEOUT) {
Serial.println(F("1 DHT timeout waiting for start signal low pulse."));
_lastresult = false;
return ;//_lastresult;
}
if (expectPulse(HIGH) == TIMEOUT) {
Serial.println(F("2 DHT 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). 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);
if (cycles[i] == TIMEOUT) {
// Mark timeout
digitalWrite(ESP_OUT_REF, HIGH);
Serial.print("\n\nFailed: cycles[");
Serial.print(i);
Serial.println("]");
Serial.println("\nCycles Dump\n");
for (int j = 0; j< i; j++) {
Serial.print(" 0x");
Serial.print(cycles[j], HEX);
}
delay(2000);
digitalWrite(ESP_OUT_REF, HIGH);
return;
}
cycles[i + 1] = expectPulse(HIGH);
if (cycles[i + 1] == TIMEOUT) {
Serial.print("Failed: cycles[");
Serial.print(i);
Serial.println("]");
Serial.println("\nCycles Dump\n");
for (int j = 0; j< i; j++) {
Serial.print(" 0x");
Serial.print(cycles[j], HEX);
}
return;
}
}
} // Timing critical code is now complete.
// 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 == TIMEOUT) || (highCycles == TIMEOUT)) {
Serial.println(F("3 DHT timeout waiting for 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.
}
}
// 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 expectPulse(bool level) {
uint32_t count = 0;
digitalWrite(ESP_OUT_REF, HIGH);
digitalWrite(ESP_OUT_REF, LOW);
// Otherwise fall back to using digitalRead (this seems to be necessary on
// ESP8266 right now, perhaps bugs in direct port access functions?).
while (digitalRead(DHTPIN) == level) {
if (count++ >= _maxcycles) {
return TIMEOUT; // Exceeded timeout, fail.
}
}
digitalWrite(ESP_OUT_REF, HIGH);
digitalWrite(ESP_OUT_REF, LOW);
return count;
}