IoT AI at the Edge, grappling with a DHT11 Sensor

Or how I had to go low level

This is slow

One of the first sensors I hooked up to my IoT board was a DHT 11 combined temperature and humidity sensor. This is quite a popular low cost sensor widely used in the Arduino and Makers community. As you’d expect, there are a number of DHT sensor libraries around and, of course, not one to reinvent the wheel, I deployed one to test it out. It worked, great. However, I noticed it was quite slow on response reading the data back on the bus.

I take this as a challenge

A quick google later and the forums are full of others having the same problem with the DHT11. The vast majority of these posts either (i) put up with the DHT11 slow response or more likely (ii) abandon the DHT11 and purchase the superior, but more costly DHT 22.

I grabbed the DHT11 OEM Datasheet to get the technical low down on exactly how it worked and, from an initial read, it appeared that there should be no reason why the DHT11 should be as slow as everyone appears to be experiencing. Never the one to turn down a technical challenge I’d thought I’d write my own DHT11 library from scratch to see if I could get a better response.

So how does this sensor work?

The DHT11 has both a thermistor and a resistance based moisture sensor for humidity. A bespoke IC provides sampling and a custom digital bus interface. The DHT11 sample rate is fixed at 1Hz, however the Datasheet states that data can be read on the digital bus in ~100ms. Obviously the IC does not sample the sensor at that rate, however, as per the forum posts, people have experienced unstable read behaviour.

How do you talk to this thing?

The core end to end interaction with the DHT11 bus is shown in the figure below. Communication between the MCU and the Sensor is via a single wire digital bus allowing only one device to “talk” at any one time. Hence you have this elaborate latch up/down sequence before transmitting the data.

Here’s the basic sequence:

  • Initialise and reset the DHT11 ensuring the bus is high.
  • Bring the bus low.
  • Wait for 18ms
  • Read the bus and wait for it to go high.
  • Wait for the bus to go low.
  • Wait for the bus to high - signals start of data transmission

Here’s the code for this bit.

void dht11_init(int pin)
{
  // Initialise and reset
  digitalWrite(pin, HIGH);
  pinMode(pin, OUTPUT);
}

void dht11_start(int pin)
{
  // Bring the bus low
  digitalWrite(pin, LOW);
  // Wait for 18ms
  delay(18);  
  // Read the bus and wait for it to go high
  pinMode(pin, INPUT);
  while (!digitalRead(pin));  
  // Wait for the bus to go low
  while (digitalRead(pin));  
  // Wait for the bus to high - signals start of data transmission
  while (!digitalRead(pin));
}

So now the getting the data from the IC. Temperature and humidity are both sent in a single 40-bit data stream. This includes a checksum. The bit timings and signals for 0 and 1 are shown below.

The code for this is a bit more tricky.

byte dht11_read_byte(int pin)
{
  byte data=0;
  // Read a byte from an 8-bit stream  
  for (int i = 0; i < 8; i++) {
    // Wait for the bus to go low
    while (digitalRead(pin));
    // Wait for the bus to go high
    while (!digitalRead(pin));
    // Start the clock
    unsigned long start=micros();
    // Wait for the bus to go low
    while (digitalRead(pin));
    // Stop the clock
    unsigned long finish=micros();
    // If elapsed time is greater than 50us then read the bit
    // Update data with a bit shift to assemble the byte
    if ((unsigned long)(finish-start)>50) data |= 1 << (7-i);
  }
  // return the data as byte
  return data;
}

The last bit is to release the bus to signal the end of the transmission.

void dht11_finish(int pin)
{
  // Releases the bus
  while (!digitalRead(pin)) ;
  digitalWrite(pin, HIGH);
  pinMode(pin, OUTPUT);
}

So how do you get the temperature and humdity out of that 40-bit data stream? Well the Datasheet states that the data format is, and I quote:

“8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit check sum. If the data transmission is right, the check-sum should be the last 8bit of 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data”. Where RH is relative humdity and T is temperature. Got that? Anyway, took me a bit to figure this out, here’s the code:

humidity = (float)data[0] + (float)data[1] / 10.0f;
temperature = (float)data[2] + (float)data[3] / 10.0f;

Did it work?

Basically, yes. Also it appears to perform quite well, I’ve not yet experienced any of the unstable reads. I guess only time will tell!

Anyway, at least now I’ve got some useful sensor data coming into my IoT device. The next challenge is GNSS.