Skip to content

Commit

Permalink
DHT11 sampling fix (tested on ESP8266 and Mega2560 r3) (#22)
Browse files Browse the repository at this point in the history
* Fixing issue #2 (timing problems Mega2560 r3 + DHT11).
Added method for more precise time measurement of the level states.
Simplified interface dropping 'pin' from most methods (keeping overloaded methods for interface compatibility).

* Adding constructors without parameters (for backward interface compatibility), #2

* Cleaning-up old and debug code, corrected comments, #2

* Default value defined already in .h, #2

* Correcting timing in DHT11:sample(), error threshold value lowered to work properly on ESP2866, #3

* Simplifying the code of the time measurement methods (#2, #3).

* Preventing potential inf. loop in levelTime() and levelTimePrecise(), #5
Adjusting pin level times for DHT11, needed after the fix above (-> need to adjust times after any change in the time measurement code...), #3

* Adjusting timing for mega2560_r3 after changes for #5 (also: cont. #2).

* Correcing comments / api doc. (#2, #3).
  • Loading branch information
t-w authored and winlinvip committed Aug 18, 2018
1 parent adaa544 commit 69ceff9
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 59 deletions.
154 changes: 109 additions & 45 deletions SimpleDHT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ SOFTWARE.

#include "SimpleDHT.h"

int SimpleDHT::read(int pin, byte* ptemperature, byte* phumidity, byte pdata[40]) {
int SimpleDHT::read(byte* ptemperature, byte* phumidity, byte pdata[40]) {
int ret = SimpleDHTErrSuccess;

float temperature = 0;
float humidity = 0;
if ((ret = read2(pin, &temperature, &humidity, pdata)) != SimpleDHTErrSuccess) {
if ((ret = read2(&temperature, &humidity, pdata)) != SimpleDHTErrSuccess) {
return ret;
}

Expand All @@ -44,7 +44,7 @@ int SimpleDHT::read(int pin, byte* ptemperature, byte* phumidity, byte pdata[40]
return ret;
}

int SimpleDHT::confirm(int pin, int us, byte level) {
int SimpleDHT::confirm(int us, byte level) {
int cnt = us / 10;
if ((us % 10) > 0) {
cnt++;
Expand All @@ -66,6 +66,62 @@ int SimpleDHT::confirm(int pin, int us, byte level) {
return SimpleDHTErrSuccess;
}


int SimpleDHT::levelTime( byte level, int interval )
{
unsigned long time_start = micros(),
time;

#ifdef __AVR
uint8_t portState = level ? bitmask : 0;
#endif
do
{
delayMicroseconds( interval );

// for an unsigned int type, the difference have a correct value
// even if overflow, explanation here:
// https://arduino.stackexchange.com/questions/33572/arduino-countdown-without-using-delay
time = micros() - time_start;
}
#ifdef __AVR
while ( ( *portInputRegister( port ) & bitmask ) == portState &&
( time < maxLevelTime ) );
#else
while ( digitalRead( pin ) == level &&
( time < maxLevelTime ) );
#endif

return time;
}

int SimpleDHT::levelTimePrecise( byte level )
{
unsigned long time_start = micros(),
time;

#ifdef __AVR
uint8_t portState = level ? bitmask : 0;
#endif
do
{
// for an unsigned int type, the difference have a correct value
// even if overflow, explanation here:
// https://arduino.stackexchange.com/questions/33572/arduino-countdown-without-using-delay
time = micros() - time_start;
}
#ifdef __AVR
while ( ( *portInputRegister( port ) & bitmask ) == portState &&
( time < maxLevelTime ) );
#else
while ( digitalRead( pin ) == level &&
( time < maxLevelTime ) );
#endif

return time;
}


byte SimpleDHT::bits2byte(byte data[8]) {
byte v = 0;
for (int i = 0; i < 8; i++) {
Expand All @@ -91,11 +147,11 @@ int SimpleDHT::parse(byte data[40], short* ptemperature, short* phumidity) {
return SimpleDHTErrSuccess;
}

int SimpleDHT11::read2(int pin, float* ptemperature, float* phumidity, byte pdata[40]) {
int SimpleDHT11::read2(float* ptemperature, float* phumidity, byte pdata[40]) {
int ret = SimpleDHTErrSuccess;

byte data[40] = {0};
if ((ret = sample(pin, data)) != SimpleDHTErrSuccess) {
if ((ret = sample(data)) != SimpleDHTErrSuccess) {
return ret;
}

Expand Down Expand Up @@ -123,72 +179,81 @@ int SimpleDHT11::read2(int pin, float* ptemperature, float* phumidity, byte pdat
return ret;
}

int SimpleDHT11::sample(int pin, byte data[40]) {

int SimpleDHT11::sample(byte data[40]) {
// empty output data.
memset(data, 0, 40);

// According to protocol: https://akizukidenshi.com/download/ds/aosong/DHT11.pdf
// According to protocol: [1] https://akizukidenshi.com/download/ds/aosong/DHT11.pdf
// notify DHT11 to start:
// 1. PULL LOW 20ms.
// 2. PULL HIGH 20-40us.
// 3. SET TO INPUT.
// Changes in timing done according to:
// [2] https://www.mouser.com/ds/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
// - original values specified in code
// - since they were not working (MCU-dependent timing?), replace in code with
// _working_ values based on measurements done with levelTimePrecise()
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
delay(20);
digitalWrite(pin, HIGH);
delay(500);

digitalWrite(pin, LOW); // 1.
delay(20); // specs [2]: 18us

digitalWrite(pin, HIGH); // 2.
delayMicroseconds(25); // specs [2]: 20-40us

pinMode(pin, INPUT);
delayMicroseconds(30);

// DHT11 starting:
// 1. PULL LOW 80us
// 2. PULL HIGH 80us
if (confirm(pin, 80, LOW)) {
int t = levelTime( LOW ); // 1.
if ( t < 36 ) { // specs [2]: 80us
return SimpleDHTErrStartLow;
}
if (confirm(pin, 80, HIGH)) {

t = levelTime( HIGH ); // 2.
if ( t < 68 ) { // specs [2]: 80us
return SimpleDHTErrStartHigh;
}

// DHT11 data transmite:
// 1. 1bit start, PULL LOW 50us
// 2. PULL HIGH 26-28us, bit(0)
// 3. PULL HIGH 70us, bit(1)
for (int j = 0; j < 40; j++) {
if (confirm(pin, 50, LOW)) {
return SimpleDHTErrDataLow;
}

// read a bit, should never call method,
// for the method call use more than 20us,
// so it maybe failed to detect the bit0.
bool ok = false;
int tick = 0;
for (int i = 0; i < 8; i++, tick++) {
if (digitalRead(pin) != HIGH) {
ok = true;
break;
}
delayMicroseconds(10);
}
if (!ok) {
return SimpleDHTErrDataRead;
}
data[j] = (tick > 3? 1:0);
// 2. PULL HIGH:
// - 26-28us, bit(0)
// - 70us, bit(1)
for (int j = 0; j < 40; j++)
{
t = levelTime( LOW, 10 ); // 1.
if ( t < 24 ) { // specs says: 50us
return SimpleDHTErrDataLow;
}

// read a bit
t = levelTime( HIGH ); // 2.
if ( t < 11 ) { // specs say: 20us
return SimpleDHTErrDataRead;
}
data[ j ] = ( t > 40 ? 1 : 0 ); // specs: 26-28us -> 0, 70us -> 1
}

// DHT11 EOF:
// 1. PULL LOW 50us.
if (confirm(pin, 50, LOW)) {
t = levelTime( LOW ); // 1.
if ( t < 24 ) { // specs say: 50us
return SimpleDHTErrDataEOF;
}

return SimpleDHTErrSuccess;
}

int SimpleDHT22::read2(int pin, float* ptemperature, float* phumidity, byte pdata[40]) {
int SimpleDHT22::read2(float* ptemperature, float* phumidity, byte pdata[40]) {
int ret = SimpleDHTErrSuccess;

byte data[40] = {0};
if ((ret = sample(pin, data)) != SimpleDHTErrSuccess) {
if ((ret = sample(data)) != SimpleDHTErrSuccess) {
return ret;
}

Expand All @@ -211,12 +276,12 @@ int SimpleDHT22::read2(int pin, float* ptemperature, float* phumidity, byte pdat
return ret;
}

int SimpleDHT22::sample(int pin, byte data[40]) {
int SimpleDHT22::sample(byte data[40]) {
// empty output data.
memset(data, 0, 40);

// According to protocol: http://akizukidenshi.com/download/ds/aosong/AM2302.pdf
// notify DHT11 to start:
// notify DHT11 to start:
// 1. T(be), PULL LOW 1ms(0.8-20ms).
// 2. T(go), PULL HIGH 30us(20-200us), use 40us.
// 3. SET TO INPUT.
Expand All @@ -230,10 +295,10 @@ int SimpleDHT22::sample(int pin, byte data[40]) {
// DHT11 starting:
// 1. T(rel), PULL LOW 80us(75-85us), use 90us.
// 2. T(reh), PULL HIGH 80us(75-85us), use 90us.
if (confirm(pin, 90, LOW)) {
if (confirm(90, LOW)) {
return SimpleDHTErrStartLow;
}
if (confirm(pin, 90, HIGH)) {
if (confirm(90, HIGH)) {
return SimpleDHTErrStartHigh;
}

Expand All @@ -242,7 +307,7 @@ int SimpleDHT22::sample(int pin, byte data[40]) {
// 2. T(H0), PULL HIGH 26us(22-30us), bit(0)
// 3. T(H1), PULL HIGH 70us(68-75us), bit(1)
for (int j = 0; j < 40; j++) {
if (confirm(pin, 60, LOW)) {
if (confirm(60, LOW)) {
return SimpleDHTErrDataLow;
}

Expand All @@ -266,10 +331,9 @@ int SimpleDHT22::sample(int pin, byte data[40]) {

// DHT11 EOF:
// 1. T(en), PULL LOW 50us(45-55us), use 60us.
if (confirm(pin, 60, LOW)) {
if (confirm(60, LOW)) {
return SimpleDHTErrDataEOF;
}

return SimpleDHTErrSuccess;
}

Loading

0 comments on commit 69ceff9

Please sign in to comment.