Simple working code for DHT sensors

I noticed a lot of people had issues with the DHT sensors and I could not get the Adafruit library working myself. The Adafruit code https://github.com/adafruit/DHT-sensor-library is pretty iffy, requires higher timing resolution than necessary and doesn’t support the internal pullup resistor. Here is my simple version that uses the internal pullup resistor:

int dht = D0;
int rh = 0;
int temp = 0;

void setup() 
{
  Spark.variable("rh", &rh, INT);
  Spark.variable("temp", &temp, INT);
  pinMode(dht, INPUT_PULLUP);
}

void loop() 
{
  delay(10000);
  rh = read_dht(dht, &temp);
}

int read_dht(int pin, int *temperature)
{
    uint8_t data[5] = {0, 0, 0, 0, 0};
    
    noInterrupts();
    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);
    delay(20);
    pinMode(pin, INPUT_PULLUP);
    while (digitalRead(pin) == HIGH) {
        delayMicroseconds(10);
    }
    while (digitalRead(pin) == LOW) {
        delayMicroseconds(10);
    }
    while (digitalRead(pin) == HIGH) {
        delayMicroseconds(10);
    }
    for (uint8_t i = 0; i < 40; i++) {
        uint8_t counter = 0;
        while (digitalRead(pin) == LOW) {
            delayMicroseconds(10);
        }
        while (digitalRead(pin) == HIGH) {
            delayMicroseconds(10);
            counter++;
        }
    
        data[i/8] <<= 1;
        if (counter > 4) {
            data[i/8] |= 1;
        }
    }
    interrupts();
    
    if (data[4] != ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
        *temperature = -254;
        return -1;
    }
        
    *temperature = data[2];
    return data[0];
}
3 Likes

rigtort, great work! If you would like a “formal” library for the DHT22 which includes CRC checking, you can get from my github. :smile:

2 Likes

There’s no CRC on DHT, just a simple sum is used as the checksum. Your library will have intermittent failures since you don’t disable interrupts.

rigtorp, you are right about the CRC! I had my mind on the 18B20 temperature sensor. However, disabling interrupts was specifically left out due to the sensitive nature of interrupts on the Spark, especially for the duration of the DHT22 read. I worked on an interrupt-based version but after consideration of the Spark “backend”, I decided that DHT22 readings are not critical. Besides, I tend to sample slowly (every 1 to 5 mins) and average them anyway. :smile:

I have issues with the Spark connection to the backend. It often looses the connection and then re-establishes it after a while.

That may be due to the interrupt disabling. Things are improving daily though and if you compile locally, you get the benefits faster.

I think part of your problem with it losing connection is the delay in the loop. A delay of 10000 is right at the edge of what the spark will allow.

1 Like

Hmm, but interrupts are not disabled in the loop except when reading from the DHT so the ISRs will be able to handle the SPI communication.

This is a nice a simple way to read DHT sensors… nice work!

Agreed with the delay(10000); is a no-no.

If you want some super simple bad code that will let you hard delay that long…

delay(5000);
SPARK_WLAN_Loop();
delay(5000);

A more proper way:

uint32_t lastRead = 0; // last known read time
bool s = true;
int dht = D0;
int rh = 0;
int temp = 0;

void setup()
{
  lastRead = millis(); // We just powered up
  pinMode(D7, OUTPUT);
  Spark.variable("rh", &rh, INT);
  Spark.variable("temp", &temp, INT);
  pinMode(dht, INPUT_PULLUP);
}

void loop() {
  // Run some test code so we know the core is running!
  digitalWrite(D7,s);
  s = !s; // toggle the state
  delay(100); // makes it blinky
  
  // Is it time to read our sensor?
  if( tenSecondsElapsed() ) {
    // Read sensor
    rh = read_dht(dht, &temp);
  }
} // End main loop (currently runs every 5-6 ms)

uint8_t tenSecondsElapsed() {
  if( (millis()-lastRead) > (10*1000) ) {
    lastRead = millis();
    return 1; // 10 seconds has elapsed
  } else {
    return 0; // nope, not yet be patient!
  }
}

int read_dht(int pin, int *temperature)
{
    uint8_t data[5] = {0, 0, 0, 0, 0};

    __disable_irq();
    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);
    delay(20);
    pinMode(pin, INPUT_PULLUP);
    while (digitalRead(pin) == HIGH) {
        delayMicroseconds(10);
    }
    while (digitalRead(pin) == LOW) {
        delayMicroseconds(10);
    }
    while (digitalRead(pin) == HIGH) {
        delayMicroseconds(10);
    }
    for (uint8_t i = 0; i < 40; i++) {
        uint8_t counter = 0;
        while (digitalRead(pin) == LOW) {
            delayMicroseconds(10);
        }
        while (digitalRead(pin) == HIGH) {
            delayMicroseconds(10);
            counter++;
        }

        data[i/8] <<= 1;
        if (counter > 4) {
            data[i/8] |= 1;
        }
    }
    __enable_irq();

    if (data[4] != ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
        *temperature = -254;
        return -1;
    }

    *temperature = data[2];
    return data[0];
}

FYI: That use of nointerrupts()/interrupts() will only disable user created interrupts such as attachInterrupt(); To really disable the background ones you need to use __disable_irq(); and __enable_irq();.

1 Like

Let’s not forget the [elapsedMillis()][1] either!

elapsedMillis  tenSecondTimer;
const unsigned long TenSeconds = 10000L;

in loop():

  // Is it time to read our sensor?
  if (tenSecondTimer >= TenSeconds) {
    tenSecondTimer = 0;
    // Read sensor
    rh = read_dht(dht, &temp);
  }

:smile:
[1]: https://github.com/pkourany/elapsedMillis

1 Like

Shh… I’m trying to forget!!! lol

I’ve implemented these changes. Let’s see if it improves stability.

Thanks for all the input.