So finally after some thinking and testing I think I've got a solution avoiding most checksum errors with DHT sensors. DHT11 as well as DHT22.
I think that a mix of a "loop counting timeout" and "delayMicroseconds" is the best solution, avoiding checksum errors as well as multiple function calls of the micros() function which would lead to big variations in timing.
A loop such like this executed on a 16 MHz Arduino board:
loopCnt = 120;
while(digitalRead(pin) == LOW) if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
will end about 6 to 8µs after the pin has gone HIGH.
This is caused, because the digitalRead() function call will take 4...6µs and the loop overhead takes about 2µs .
Followed by:
delayMicroseconds(32);
boolean dhtBit=digitalRead(pin);
which will take 32µs for the delay and 4...6µs for the digitalRead, we get a sample of the pin after a total time of 6+32+4 to 8+32+6 Microseconds, this is 42 to 46 Microseconds after the pin went HIGH.
If the DHT sensor sends a 0-bit with a 27µs duration, or a 1-bit with a 70µs duration, we are in the middle of it in any case with a sample timing of 42 to 46µs.
In case we are using a DUE board, timing is a bit different. The digitalRead function is much quicker as well as the loop that detects the HIGH state. So the sample is taken after about 1+32+1= 34µs. But also 34µs is surely longer than 27µs for a 0-bit, so even with the DUE and the same code it is possible to detect 0-bits from 1-bits safely.
Total demo code, tested with 16 MHz MEGA and with 84 MHz DUE boards as well as with DHT11 and DHT22 sensors (configurable):
// DHT11 and DHT22 sensor reading demo
// by 'jurs' for German Arduino Forum
// Code should be fine for all Arduino boards from 16 MHz (i.e. "UNO") to 84 MHz ("DUE")
// DHT functions enumerated
enum {DHT11_SAMPLE, DHT22_SAMPLE, DHT_TEMPERATURE, DHT_HUMIDITY, DHT_DATAPTR};
// Begin of user configuration area
#define DHT_SAMPLE DHT11_SAMPLE // must be DHT11_SAMPLE or DHT22_SAMPLE
#define DHT_PIN 2
// End of user configuration area
// DHT error codes enumerated
enum {DHT_OK=0, DHT_ERROR_TIMEOUT=-1, DHT_ERROR_CRC=-2, DHT_ERROR_UNKNOWN=-3};
void setup()
{
Serial.begin(9600);
Serial.println("DHT Temperature and Huminity Measurement");
}
// DHT sensor pinout from left to right looking at the gridded side
// 1-VCC 2-DATA 3-NC 4-GND
int dhtCall(byte pin, byte function)
// input parameters are the data pin and one of the DHT functions
// return value is DHT error code with function DHT11_SAMPLE or DHT22_SAMPLE
// alsways do sampling with DHT_OK result before calling other functions
// return value is temperature with function DHT_TEMPERATURE
// return value is humidity with function DHT_HUMIDITY
// return value is pointer to byte array containing raw data with function DHT_DATAPTR
{
static int temperature=-999;
static int humidity=-999;
static byte data[5]; // 5 bytes to receive 40 data bits
unsigned int loopCnt; // loop counter
byte sum; // checksum
#define DHT_LOOPS 120
int triggerTime;
switch (function)
{
case DHT11_SAMPLE: // REQUEST DHT11 SAMPLE
case DHT22_SAMPLE: // REQUEST DHT22 SAMPLE
if (function==DHT11_SAMPLE) triggerTime=20000; // 20000µs trigger time for DHT11
else triggerTime=1000; // 1000µs trigger time for DHT22
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
delayMicroseconds(triggerTime);
digitalWrite(pin,HIGH);
pinMode(pin,INPUT);
loopCnt = DHT_LOOPS;
while(digitalRead(pin) == HIGH) if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
loopCnt = DHT_LOOPS;
while(digitalRead(pin) == LOW) if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
loopCnt = DHT_LOOPS;
while(digitalRead(pin) == HIGH) if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
for (byte bitNum=0;bitNum<40;bitNum++) // try reading 40 bits
{
loopCnt = DHT_LOOPS;
while(digitalRead(pin) == LOW) if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
delayMicroseconds(32);
boolean dhtBit=digitalRead(pin);
bitWrite(data[bitNum/8],7-bitNum%8,dhtBit);
loopCnt = DHT_LOOPS;
while(digitalRead(pin) == HIGH) if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
}
sum = data[0] + data[1] + data[2] + data[3];
if (data[4] != sum) return DHT_ERROR_CRC;
if (function==DHT11_SAMPLE)
{
humidity=data[0];
temperature=data[2];
}
else
{
humidity=data[0]*256+data[1];
temperature= (data[2] & 0x7F) * 256 + data[3];
if (data[2] & 0x80) temperature= -temperature;
}
return DHT_OK;
case DHT_TEMPERATURE:
return temperature;
case DHT_HUMIDITY:
return humidity;
case DHT_DATAPTR:
return (int)data;
default:
return DHT_ERROR_UNKNOWN;
}
}
void loop()
{
byte* b; // byte pointer for showing raw data
switch (dhtCall(DHT_PIN, DHT_SAMPLE)) // always request a sample first
{
case DHT_OK: // only if DHT_OK is true, get temperature, humidity and possibly raw data
Serial.print("Humidity: ");
Serial.print(dhtCall(DHT_PIN, DHT_HUMIDITY));
Serial.print(" Temp: ");
Serial.print(dhtCall(DHT_PIN, DHT_TEMPERATURE));
Serial.print(" raw data: ");
b=(byte*)dhtCall(DHT_PIN, DHT_DATAPTR);
for (int i=0;i<5;i++)
{
Serial.print(b[i]);Serial.print('\t');
}
Serial.println();
break;
case DHT_ERROR_TIMEOUT:
Serial.println("Timeout Error");
break;
case DHT_ERROR_CRC:
Serial.println("CRC Error");
break;
default:
Serial.println("Unknown Error");
}
delay(2000); // minimum time between two DHT samples is two seconds
}
The temperature and humidity values returned from my function are degrees and percent in case of a DHT11 and they are tenths of degrees and tenths of percent in case of a DHT22. Always integer numbers. So in case you want do display decimals with DHT22, either use a special formatting routine or divide the values by 10.0 to make float values before doing float formatting.
As a goodie which is not available with any DHT-library I've seen before, I have made the raw data bytes readable after sampling. So it would be possible to send out the raw data just as sampled from the sensor via a RF sender over an wireless connection.
Not tested with 8 MHz Arduinos, I only tested with 16 MHz and 84 MHz DUE boards.
Works fine without checksum errors for me.