Get DHT22 Pulse Timing with Micros()

I’ve gotten the DHT22 temperature-humidity sensor working using a modified version of the code posted by @wgbartley (https://community.spark.io/t/dht22-dht11-and-similar-blocking-version/998 ). I’ve also looked at the pulse lengths using a variation of that program that uses a 1 microsecond delay and a counter inside the while loop (rather than using micros() as below). I was trying to do a similar thing using micros() to get a more accurate view of the pulse lengths, but I get bizarre readings that I can’t make sense of. Here is the code I’m using,

int dataPin = D4;

void setup() {
    Serial.begin(19200);
    pinMode(dataPin, OUTPUT);
    digitalWrite(dataPin, HIGH);
    read();
}

void loop() {
}

void read(void) {
    noInterrupts();
    for (int k = 0; k<4; k++) {
        delay(3000);
        
        uint8_t lastState = HIGH;
        uint8_t j = 0, i;
        unsigned long results[85];
        uint8_t state[85];
        unsigned long t0 = 0;
        unsigned long t1 = 0;
        
        Serial.println("");
        Serial.println("started!");
    
        pinMode(dataPin, OUTPUT);
        digitalWrite(dataPin, HIGH);
        delay(250);
        digitalWrite(dataPin, LOW);
        delay(20);
        
        digitalWrite(dataPin, HIGH);
        delayMicroseconds(40);
        pinMode(dataPin, INPUT);
    
        // read in timings
        for (i=0; i< 85; i++) {
            t0 = micros();
            while (digitalRead(dataPin) == lastState) {
                t1 = micros();
                if ((t1 - t0) > 1000)
                    break;
            }
    
            lastState = digitalRead(dataPin);
            results[i] = t1 - t0;
            state[i] = lastState;
    
            if ((t1 - t0) > 1000)
                break;
    
        }
    
        for (j=0; j< 85; j++) {
            delay(2);
            Serial.print(j);
            delay(2);
            Serial.print("   ");
            delay(2);
            Serial.print(results[j]);
            delay(2);
            Serial.print("  ");
            delay(2);
            Serial.print(state[j]);
            delay(2);
            Serial.print("  ");
            delay(2);
            Serial.println("");
            delay(2);
        }
    }
    Serial.println("");
    Serial.println("");
    Serial.println("Ending serial");
    interrupts();
    Serial.end();
}

I print out the index of the loop, the time difference in microseconds (t1 - t0), and the state of the pin. Here is a sample of the results,

started!
0   4279527169  0  
1   0  0  
2   16  0  
3   536871520  0  
4   134257971  0  
5   536879020  0  
6   1073756172  0  
7   536871520  0  
8   16  124  
9   16  78  
10   0  0  
11   128  32  
12   0  3  
13   0  16  
14   0  0  
15   12288  0  
16   0  177  
17   16  31  
18   536879030  0  
19   16  32  
20   536877892  124  
21   536879020  78  
22   1  0  
23   536891300  32  
24   134248479  77  
25   536877892  122 

Given the code, the time difference should never be bigger than 1000, and the pin state should be either 0 or 1. As you can see, the time difference values are sometimes as large as 9 figures, and the pin state, while sometimes 0, is never 1, and frequently some other number. Interestingly, the numbers are not random – I run the loop 4 times, and I get the same printout each time. Is there something wrong wight he way I’m using micros()?

You had me at “Timing”… I’ve been working on solving this freaking crap all damn night. It’s not you, it’s this damn micros() and code… really REALLY not cool.

I don’t have a DHT22 so I first set off to write a timing emulator. This just bangs away on D4 output low for 50us, high for 27us, low for 50us, high for 70us (repeat 5000 times).

// http://www.adafruit.com/datasheets/DHT22.pdf
// DHT22 Emulator
bool s,b = false;
void setup() {
  pinMode(D4,OUTPUT);
}

void loop() {
  for(int x=0; x<10000; x++) {
    s = !s;                  // toggle bit vs start transmit
    if(s) {
      digitalWrite(D4,HIGH); //  2us
      delayMicroseconds(25); // 25us (27us total HIGH = Bit 0)
      if(b) delayMicroseconds(43); // 43us (70us total HIGH = Bit 1)
      digitalWrite(D4,LOW);  //  2us
      b = !b;                // toggle bit value
    }
    else {
      delayMicroseconds(48); // 48us (50us total LOW = start transmit)
    }
  } 
}

I started with your code, and just commented out the parts that output on D4… and I did see some valid numbers very briefly at the start of the night. But then I kept seeing the weird patterns.

I went digging all over and see problems everywhere.

  • digitalRead() and analogRead() return type int32_t (signed 32 bit!) why? Could this contribute to the problem, perhaps. I tried all kinds of typecasting… not sure if it helped.
  • noInterrupts() and interrupts() will do nothing for you because there are no arduino style interrupts used.
  • it’s bad to do a digitalRead() twice when you are trying to track edges.
  • you were saving lastState as a result after you read the “newState”
  • I’m really starting to wondering if 'break;" works in this compiler!
  • In the process of trying to find an answer, I re-wrote most of your code to make it easier for me to test. Now you get the Decoding side up and running, open your serial monitor, and then press the Mode button to perform a read(). If you hold down Mode it read()'s over and over. Once in a blue moon you’ll see a 2000 number, that’s when the DHT22 emulator is done with it’s 10000 loops, and takes 5-6ms to run the background tasks.
  • micros() wraps at 60,000,000 which is every minute -_-
  • delay() needs to be re-written slightly

Here’s the decoder. It works pretty good… honestly I don’t know what bits started to make it work properly. This one really frustrated me! Probably 4 hours screwing with it. Just imagine how many times you could re-write this code and flash it over the air in 4 hours… yeah, that’s about how many times I did. :smile:

// DHT22 Decoder
uint32_t startTime = 0;
uint16_t dataPin = D4;
uint8_t lastState = HIGH;
uint8_t newState = HIGH;
uint32_t results[86];
uint8_t state[86];
uint32_t t0 = 0;
uint32_t t1 = 0;
uint32_t tim0[86];
uint32_t tim1[86];
bool timeOut = false;
        
void setup() {
    startTime = millis(); // capture the time that our app starts
    pinMode(dataPin, INPUT);
    Serial.begin(115200);
}

inline void read(void) {
    Serial.println("");
    Serial.println("started!");

    // read in timings
    int i,j;
    timeOut = false;
    for (i=0; (i<85) && !timeOut; i++) {
        t0 = (uint32_t)micros();
        while ((newState == lastState) && !timeOut) {
            t1 = (uint32_t)micros();
            newState = digitalRead(dataPin);
            if ((uint32_t)(t1 - t0) >= 2000) {
                timeOut = true; //break;
            }
        }
        // save the elapsed time with the correct state (lastState)
        state[i] = lastState;
        // at this point in time, newState is the new lastState ;-)
        lastState = newState;
        results[i] = (uint32_t)(t1 - t0);
        tim0[i] = (uint32_t)t0;
        tim1[i] = (uint32_t)t1;
    }
    // Only print up until a timeOut, or end of array
    for (j=0; j<i; j++) {
        delay(2);
        Serial.print(j);
        delay(2);
        Serial.print("   ");
        delay(2);
        Serial.print(results[j]);
        delay(2);
        Serial.print("  ");
        delay(2);
        Serial.print(state[j]);
        delay(2);
        Serial.print("  ");
        delay(2);
        Serial.print(tim1[j]);
        delay(2);
        Serial.print("  ");
        delay(2);
        Serial.print(tim0[j]);
        delay(2);
        Serial.print("  ");
        delay(2);
        Serial.print(tim1[j] - tim0[j]);
        delay(2);
        Serial.print("  ");
        delay(2);
        Serial.println("");
        delay(2);
    }
    Serial.println("");
    Serial.println("Ending serial");
}

void loop() {
    if(ModeBtnPressed()) {
        read();
        BUTTON_ResetDebouncedState(BUTTON1);
    }
}

bool ModeBtnPressed() {
    if(millis() - startTime > 3000) { // wait at least 3s for smart config
        if(BUTTON_GetDebouncedTime(BUTTON1) >= 250) {
            return 1;
        }
    }
    return 0;
}

And some sample output:

//index, pulse time, pulse state, t1, t0, t1-t0
started!
0   51  0  56275255  56275204  51
1   24  1  56275283  56275259  24
2   48  0  56275335  56275287  48
3   68  1  56275406  56275338  68
4   47  0  56275457  56275410  47
5   25  1  56275486  56275461  25
6   47  0  56275537  56275490  47
7   64  1  56275605  56275541  64
8   48  0  56275660  56275612  48
9   24  1  56275688  56275664  24
10   47  0  56275739  56275692  47
11   68  1  56275811  56275743  68
12   47  0  56275862  56275815  47
13   24  1  56275890  56275866  24
14   48  0  56275942  56275894  48
15   67  1  56276013  56275946  67
16   48  0  56276065  56276017  48
17   24  1  56276093  56276069  24
18   47  0  56276144  56276097  47
19   68  1  56276216  56276148  68
20   47  0  56276267  56276220  47
21   24  1  56276295  56276271  24
22   48  0  56276347  56276299  48
23   67  1  56276418  56276351  67
24   48  0  56276470  56276422  48
25   27  1  56276501  56276474  27
26   48  0  56276553  56276505  48
27   70  1  56276627  56276557  70
28   48  0  56276679  56276631  48
29   24  1  56276707  56276683  24
30   47  0  56276758  56276711  47
31   68  1  56276830  56276762  68
32   47  0  56276881  56276834  47
33   24  1  56276909  56276885  24
34   48  0  56276961  56276913  48
35   67  1  56277032  56276965  67
36   48  0  56277084  56277036  48
37   24  1  56277112  56277088  24
38   47  0  56277163  56277116  47
39   68  1  56277235  56277167  68
40   47  0  56277286  56277239  47
41   25  1  56277315  56277290  25
42   48  0  56277366  56277318  48
43   68  1  56277438  56277370  68
44   48  0  56277489  56277441  48
45   24  1  56277517  56277493  24
46   48  0  56277569  56277521  48
47   67  1  56277640  56277573  67
48   47  0  56277691  56277644  47
49   24  1  56277719  56277695  24
50   48  0  56277771  56277723  48
51   67  1  56277842  56277775  67
52   48  0  56277894  56277846  48
53   24  1  56277922  56277898  24
54   47  0  56277973  56277926  47
55   68  1  56278045  56277977  68
56   47  0  56278096  56278049  47
57   24  1  56278124  56278100  24
58   48  0  56278176  56278128  48
59   67  1  56278247  56278180  67
60   48  0  56278299  56278251  48
61   24  1  56278327  56278303  24
62   47  0  56278378  56278331  47
63   68  1  56278450  56278382  68
64   51  0  56278505  56278454  51
65   24  1  56278533  56278509  24
66   47  0  56278584  56278537  47
67   67  1  56278655  56278588  67
68   47  0  56278706  56278659  47
69   24  1  56278734  56278710  24
70   48  0  56278786  56278738  48
71   67  1  56278857  56278790  67
72   48  0  56278909  56278861  48
73   24  1  56278937  56278913  24
74   47  0  56278988  56278941  47
75   68  1  56279060  56278992  68
76   51  0  56279115  56279064  51
77   21  1  56279139  56279118  21
78   51  0  56279194  56279143  51
79   68  1  56279266  56279198  68
80   47  0  56279317  56279270  47
81   24  1  56279345  56279321  24
82   48  0  56279397  56279349  48
83   67  1  56279468  56279401  67
84   48  0  56279520  56279472  48

Ending serial

:sleeping: :zzz: g’night

@BDub, thanks for all your hard work, I appreciate it. I too worked for several days on this – having to flash the core over and over definitely slows things down (I’m used to the Xcode build cycle using the iPhone simulator which is very fast). After trying your code (which worked), I started removing things to see if I could find out what made the difference. I didn’t see any problems related to micros(), data types or the break command. I’m pretty sure the thing that made the difference was not doing the double read of the pin. This is the code, in that block that worked after sifting things out,

for (uint8_t i=0; i<85; i++) {
        t0 = micros();
        while (newState == lastState) {
            t1 = micros();
            newState = digitalRead(dataPin);
            if (t1 - t0 >= 500) break;
        }
    
        lastState = newState;
        state[i] = lastState;
        results[i] = t1 - t0;
        if (t1 - t0 >= 500) break;

The thing that still baffles me, is why I was getting values other than 0 or 1 for the pin state. Anyway, here is my modified code for reading the DHT22 using micros() instead of a delay and a counter. I changed the output to a single int so I can get both values (as well as a failed read indication) with one GET, and I parse that in an iOS app (DHTTempAndHumidityReader) to present the data on my iPhone.

#define MAXTIMINGS 85

uint8_t data[5];
int tAndH;
int dataPin = D4;

void setup() {
    Spark.variable("tAndH", &tAndH, INT);
    pinMode(dataPin, INPUT);
} 


void loop() {
    delay(2000);
    tAndH = readValues();
}



int readValues(void) {
    int _h, _t, _tAndH;
    if (read()) {
        _h = data[0];
        _h *= 256;
        _h += data[1];
        
        _t = data[2] & 0x7F;
        _t *= 256;
        _t += data[3];

        if (data[2] & 0x80)
         _t *= -1;
         _t <<= 16;    
         _tAndH = _t + _h; // combine values to get both with one GET
        return _tAndH;
    }

    return -1; // return -1 if the read fails
}



bool read(void) {
    uint8_t i, j=0;
    uint8_t lastState = HIGH;
    uint8_t newState = HIGH;
    unsigned long results[85];
    uint8_t state[85];
    unsigned long t0 = 0;
    unsigned long t1 = 0;

    data[0] = data[1] = data[2] = data[3] = data[4] = 0;

    // send start sequence to the DHT22
    pinMode(dataPin, OUTPUT);
    digitalWrite(dataPin, LOW);
    delay(20);
    digitalWrite(dataPin, HIGH);
    delayMicroseconds(40);
    pinMode(dataPin, INPUT); // switch to input to read from the DHT22


    for (i=0; i< MAXTIMINGS; i++) {
        
        t0 = micros();
        while (newState == lastState) {
            t1 = micros();
            newState = digitalRead(dataPin);
            if (t1 - t0 >= 500) break;
        }

        lastState = newState;

        if ((t1 - t0) > 500) break;

        // ignore first 4 transitions
        if (i >= 4  &&  lastState == 0) {
            // shove each bit into the storage bytes
            data[j/8] <<= 1;

            if ((t1 - t0) > 40) // in tests, low values were 22 to 26, and high values were 67 to 73
                data[j/8] |= 1;

            j++;
        }
    }

    // check we read 40 bits and that the checksum matches
    if ((j >= 40) &&  (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)))
        return true;

    return false;
}
1 Like

Very cool @Ric. You should put that all up on a github repo :wink:

EDIT: and by the way… the state saves were weird, and so were the really large micros() values that should never be seen. Still trying to forget about this nightmare :smile:

Hello Guys, Does anyone have a way on how to expose the sensor outside without damage it? I being using the sparkcore inside a nema box with the temp36 out on the bottom of the box and it works fine, But I like to try one of this sensors, but it looks that they it can not get wet? Or am I wrong? or Yes can they get wet? or we have to use something like this?
http://shop.boxtec.ch/digital-humidity-temperature-sensor-dht44-rht05-p-40846.html?language=en
Thank You.