DHT11 + Adafruit Library, are these delays normal?

I have read through the DHT11 datasheet and currently have my code setup to only do a reading every 2.5s or so (recommended max calls to once every 2s).

I have this block of code,

  /**************************
     *                        *
     *  CALCULATE TEMPERATURE *
     *                        *
     **************************/
     float operationStart = millis();
     float h = dht.readHumidity();
     float t = dht.readTemperature();

    if(!isnan(h) && !isnan(t)){
    // Calcualte the current temperature so we can math the brightness
    current_temp = dht.computeHeatIndex(t, h, false);
    Serial.println("Temperature Read Successful");
//    Serial.print(current_temp);
//    Serial.println("C");
    }
    Serial.println(millis() - operationStart);

So between the start of the block and the end, I have done a simple timer to figure out how long the operation is taking.
It’s taking a whopping 275ms. Now that’s not really an issue if it were not for the fact I am also sending this data to a 7 segment 4 digit display right after.

I was originally just using a NTC Thermistor, and that ran much faster, it was using the Stein-hart Model and I only done a reading every 1s, difference is it calculated in microseconds.

Is this just due to the fact it’s a pretty basic and cheap sensor?


EDIT: So I found the solution, well at least a solution that works for my purpose. You can find what was causing the problems by looking through the thread. The long and short of it:

Adafruit DHT sensor library has a conservative timer of 250ms + 20ms for pulling the data pin high, then low in preparation for data transmission. This is causing my 7 segment 4 digit display to flicker since it can not update quick enough to create the illusion of all 4 digits being lit.

My work around for this is to remove calls to delay(t) and use an incremental timer using millis().

To do this I hacked up the .cpp/.h library files a little, I added this method:

bool DHT::sendSignalAndWait(uint8_t waitTime, bool force){
 _signalWaitTime = waitTime;
 if(force){
 digitalWrite(_pin, HIGH);
 delay(waitTime);
 pinMode(_pin, OUTPUT);
 digitalWrite(_pin, LOW);
 delay(waitTime);

 return true;
 }
 if(!_signalBegin){
 _signalBegin = true;
 _signalTimer = millis();
 
 // Set data pin high
 digitalWrite(_pin, HIGH); 
 return false;
 }
 if(!(millis() - _signalTimer > _signalWaitTime)) return false; 
 if(!_signalReady){
 _signalReady = true;
 _signalTimer = millis();
 pinMode(_pin, OUTPUT);
 digitalWrite(_pin, LOW);
 return false;
 }
 if(!(millis() - _signalTimer > _signalWaitTime)) return false;
 _signalBegin = false;
 _signalReady = false;
 _signalTimer = NAN;
 _signalComplete = true;
 return true;
}

As well as modified the read() function, it no longer requires the pin operations. So this

boolean DHT::read(bool force) {
  // Check if sensor was read less than two seconds ago and return early
  // to use last reading.
  uint32_t currenttime = millis();
  if (!force && ((currenttime - _lastreadtime) < 2000)) {
    return _lastresult; // return last correct measurement
  }
  _lastreadtime = currenttime;

  // Reset 40 bits of received data to zero.
  data[0] = data[1] = data[2] = data[3] = data[4] = 0;

  // Send start signal.  See DHT datasheet for full signal diagram:
  //   http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf

  // Go into high impedence state to let pull-up raise data line level and
  // start the reading process.
  digitalWrite(_pin, HIGH);
  delay(250);

  // First set data line low for 20 milliseconds.
  pinMode(_pin, OUTPUT);
  digitalWrite(_pin, LOW);
  delay(20);

 - omitted

became this:

boolean DHT::read(bool force) {
  // Check if sensor was read less than two seconds ago and return early
  // to use last reading.
  uint32_t currenttime = millis();
  if (!force && ((currenttime - _lastreadtime) < 2000)) {
    return _lastresult; // return last correct measurement
  }
  _lastreadtime = currenttime;

  // Reset 40 bits of received data to zero.
  data[0] = data[1] = data[2] = data[3] = data[4] = 0;
  
   // Signal code moved to own function

 - omitted

So now I can make continious calls to sendSignalAndWait() which will hold the data pin high for X amount of time (default 20ms) and then low for X amount of time (default 20ms). Once sendSignalAndWait() returns true, I can then make the usual calls to the dht class to get humidity, temp and heat index. The only thing else I done was make it so you can select the DHT delay between checks, it’s default is 2000ms but the sensor can update twice as fast as that.

Gibbo3771:
I have read through the DHT11 datasheet and currently have my code setup to only do a reading every 2.5s or so (recommended max calls to once every 2s).

Interesting. The datasheet I found:
https://www.mouser.com/ds/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
says:

Note: Sampling period at intervals should be no less than 1 second.

but I found that not to work and had to increase the interval to 1.2 seconds.

Gibbo3771:
So between the start of the block and the end, I have done a simple timer to figure out how long the operation is taking.
It's taking a whopping 275ms.

I just did a project with DHT11 and had some problems with how slow reading from it was, but I'm sure it wasn't that slow.

Post a link to where you got the DHT11 library from. Please use the chain links icon on the toolbar to make it clickable. Or if you installed it using Library Manger (Sketch > Include Library > Manage Libraries) then say so and state the full name of the library.

Gibbo3771:
Now that's not really an issue if it were not for the fact I am also sending this data to a 7 segment 4 digit display right after.

It might be helpful if you explained why that makes it an issue.

Interesting. The datasheet I found:
https://www.mouser.com/ds/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
says:
Quote
Note: Sampling period at intervals should be no less than 1 second.
but I found that not to work and had to increase the interval to 1.2 seconds.

Oh, where the hell did I pull that from then! Regardless, it does not actually effect anything, reading it once every 1.5s, 5s, 10s, it’s all the same. The latency is coming from when you read it, as that code executes.

Or if you installed it using Library Manger (Sketch > Include Library > Manage Libraries) then say so and state the full name of the library.

From library manager:
DHT sensor library by Adafruit Version 1.3.0

It might be helpful if you explained why that makes it an issue.

Ah sorry, the delay causes my display to basically turn off while the sensor is being read.

Perhaps it’s another part of the code causing that problem? Could be a simple case of looking in the wrong place :D.

 // Set the pin, ensure brightness does not go over 255
  if(brightness >= MAX_BRIGHTNESS)
    brightness = 255;
  else if(brightness < 0)
    brightness = MIN_BRIGHTNESS;

  // Send the signal to the pin
  analogWrite(LED_PIN, brightness);

  /**************************
   *                        *
   *  DISPLAY TEMPERATURE   *
   *                        *
   **************************/

  // Write the temp into the array
  int tt = current_temp * 10;
  digital_temp_read[3] = ((int)tt % 1000) / 100;
  digital_temp_read[2] = ((int)tt % 100) / 10;
  digital_temp_read[1] = ((int)tt % 10) % 10;
  digital_temp_read[0] = 12;


  // Update the display
  for(int x = 0; x < NUM_DIGITS; x++){
    PORTB = 0b00111100;
            
    // Grab the digit pattern relative to the temp read
    byte pattern = digit_pattern[digital_temp_read[x]];

    // Add a decimal point to the digit
    if(x == 2)
      pattern |= B10000000;

    
    PORTD &= ~_BV(PD6);    
    shiftOut(SER_PIN, SRCLK_PIN, MSBFIRST, pattern);
    // Display the test number on a digit
    PORTD = _BV(PD6);
    // Turn on the digit at the position
    PORTB ^= control_pins[x];
    delay(2);
  }

I don’t know if you can see anything there, this block is right after the temperature read. It looks fine to me and was working OK with a thermistor (which let out some magic smoke during an unrelated incident).

So, I decided to have another good look at it and found some interesting stuff.

I narrowed the 275ms~ delay down to one part of the code:

     float operationStart = millis();
     float h = dht.readHumidity();
     Serial.println(millis() - operationStart);

The delay happens in the humidity read function, so I decided to take a look into the library and see what is going on.

This piece of code to check what sensor you have, I am only using the DH11 so I done some modifications.

float DHT::readHumidity(bool force) {
  float f = NAN;
  if (read()) {
    switch (_type) {
    case DHT11:
      f = data[0];
      break;
    case DHT22:
    case DHT21:
      f = data[0];
      f *= 256;
      f += data[1];
      f *= 0.1;
      break;
    }
  }
  return f;
}

I changed it to:

float DHT::readHumidity(bool force) {
  return data[0];
}

Interestingly, this actually removed the delay. I don’t see why, it’s a simple logic block with what seems to be the simplest outcome. Just grabbing data from an array.

I ran it again, oops, delay is back but else where. I checked the code one line at a time again and found that the delay had shifted from the humidity read to the temperature read. Wtf?

Again, it’s just a simple logic block with basically an variable assignment.

//boolean S == Scale.  True == Fahrenheit; False == Celcius
float DHT::readTemperature(bool S, bool force) {
  float f = NAN;

  if (read(force)) {
    switch (_type) {
    case DHT11:
      f = data[2];
      if(S) {
        f = convertCtoF(f);
      }
      break;
    case DHT22:
    case DHT21:
      f = data[2] & 0x7F;
      f *= 256;
      f += data[3];
      f *= 0.1;
      if (data[2] & 0x80) {
        f *= -1;
      }
      if(S) {
        f = convertCtoF(f);
      }
      break;
    }
  }
  return f;
}

Added a new method instead of re-writting:

float DHT::readTemperatureDHT11(){
	return data[2];
}

Behold, the delay is gone.

Oh wait no it’s not because I never payed attention to the crucial thing, the read() function.

So the delay is in this monstrous block of code that I have no idea currently what it does, how it works, how much a load it puts on the chip etc.

boolean DHT::read(bool force) {
  // Check if sensor was read less than two seconds ago and return early
  // to use last reading.
  uint32_t currenttime = millis();
  if (!force && ((currenttime - _lastreadtime) < 2000)) {
    return _lastresult; // return last correct measurement
  }
  _lastreadtime = currenttime;

  // Reset 40 bits of received data to zero.
  data[0] = data[1] = data[2] = data[3] = data[4] = 0;

  // Send start signal.  See DHT datasheet for full signal diagram:
  //   http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf

  // Go into high impedence state to let pull-up raise data line level and
  // start the reading process.
  digitalWrite(_pin, HIGH);
  delay(250);

  // First set data line low for 20 milliseconds.
  pinMode(_pin, OUTPUT);
  digitalWrite(_pin, LOW);
  delay(20);

  uint32_t cycles[80];
  {
    // Turn off interrupts temporarily because the next sections are timing critical
    // and we don't want any interruptions.
    InterruptLock lock;

    // End the start signal by setting data line high for 40 microseconds.
    digitalWrite(_pin, HIGH);
    delayMicroseconds(40);

    // Now start reading the data line to get the value from the DHT sensor.
    pinMode(_pin, INPUT_PULLUP);
    delayMicroseconds(10);  // Delay a bit to let sensor pull data line low.

    // First expect a low signal for ~80 microseconds followed by a high signal
    // for ~80 microseconds again.
    if (expectPulse(LOW) == 0) {
      DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse."));
      _lastresult = false;
      return _lastresult;
    }
    if (expectPulse(HIGH) == 0) {
      DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse."));
      _lastresult = false;
      return _lastresult;
    }

    // Now read the 40 bits sent by the sensor.  Each bit is sent as a 50
    // microsecond low pulse followed by a variable length high pulse.  If the
    // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds
    // then it's a 1.  We measure the cycle count of the initial 50us low pulse
    // and use that to compare to the cycle count of the high pulse to determine
    // if the bit is a 0 (high state cycle count < low state cycle count), or a
    // 1 (high state cycle count > low state cycle count). Note that for speed all
    // the pulses are read into a array and then examined in a later step.
    for (int i=0; i<80; i+=2) {
      cycles[i]   = expectPulse(LOW);
      cycles[i+1] = expectPulse(HIGH);
    }
  } // Timing critical code is now complete.

  // Inspect pulses and determine which ones are 0 (high state cycle count < low
  // state cycle count), or 1 (high state cycle count > low state cycle count).
  for (int i=0; i<40; ++i) {
    uint32_t lowCycles  = cycles[2*i];
    uint32_t highCycles = cycles[2*i+1];
    if ((lowCycles == 0) || (highCycles == 0)) {
      DEBUG_PRINTLN(F("Timeout waiting for pulse."));
      _lastresult = false;
      return _lastresult;
    }
    data[i/8] <<= 1;
    // Now compare the low and high cycle times to see if the bit is a 0 or 1.
    if (highCycles > lowCycles) {
      // High cycles are greater than 50us low cycle count, must be a 1.
      data[i/8] |= 1;
    }
    // Else high cycles are less than (or equal to, a weird case) the 50us low
    // cycle count so this must be a zero.  Nothing needs to be changed in the
    // stored data.
  }

  DEBUG_PRINTLN(F("Received:"));
  DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(F(", "));
  DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(F(", "));
  DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(F(", "));
  DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(F(", "));
  DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(F(" =? "));
  DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX);

  // Check we read 40 bits and that the checksum matches.
  if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
    _lastresult = true;
    return _lastresult;
  }
  else {
    DEBUG_PRINTLN(F("Checksum failure!"));
    _lastresult = false;
    return _lastresult;
  }
}

So I guess I need to find what is causing the delay in there then.

So I may as well post my results because I don't think I can actually resolve this issue due to hardware limitations.

I had a good read of the datasheet + the library code and found where the 270ms delay was coming from. In the read() function there was these two calls:

  // Go into high impedence state to let pull-up raise data line level and
  // start the reading process.
  digitalWrite(_pin, HIGH);
  delay(250);

  // First set data line low for 20 milliseconds.
  pinMode(_pin, OUTPUT);
  digitalWrite(_pin, LOW);
  delay(20);

A 250ms delay and 20ms delay clumped together. So I have literally no idea what the first delay does. Is that the signal start pullup? According to the datasheet it should take at least 18ms (figure 3 datasheet) which falls more in line with the second delay. Playing around with these numbers does not give me that result, however.

If I set the first delay to 19+ms all is good, anything below 19 and obviously the signal is not reaching the sensor. So if that's the start signal, what the hell is second delay? Is that just turn the signal off because we already established the MCU and sensor is communicating? Why two delays?

Regardless of all that jazz, the minimum delay of 18ms in order to communicate with the sensor is a killer. This is due to me writing the temp data to a 7 segment 4 digit display that relies on little to no delays in the code to give the illusion of a seamless update. So can I write this off as the hardware is not appropriate? :frowning:

I haven't had a chance to look at your code yet, but this is the library I used:
Arduino/libraries/DHTlib at master · RobTillaart/Arduino · GitHub. I compared it to the library that was originally used in the project:
GitHub - winlinvip/SimpleDHT: Simple, Stable and Fast Arduino Temp & Humidity Sensors for DHT11 an
and found it to be a little faster but I haven't tried the Adafruit library.

It may be that Adafruit's library is just being extremely conservative with timing. Their top priority is going to be that the parts bought from them work right out of the box so that they can avoid expensive customer support. Performance would be a secondary concern.

pert:
I haven't had a chance to look at your code yet, but this is the library I used:
Arduino/libraries/DHTlib at master · RobTillaart/Arduino · GitHub. I compared it to the library that was originally used in the project:
GitHub - winlinvip/SimpleDHT: Simple, Stable and Fast Arduino Temp & Humidity Sensors for DHT11 an
and found it to be a little faster but I haven't tried the Adafruit library.

It may be that Adafruit's library is just being extremely conservative with timing. Their top priority is going to be that the parts bought from them work right out of the box so that they can avoid expensive customer support. Performance would be a secondary concern.

Thanks for the links, I had a look at it and yeah, it seems the Adafruit library is more conservative with their timing so signals are 100% sent with no risk. They are quite plug and play based so I understand, however the DHT11 still needs that 18ms~ start signal before you can pull the data line high so it's going to mess with my 7 segment 4 digit display.

Gibbo3771:
Ah sorry, the delay causes my display to basically turn off while the sensor is being read.

I don't understand why that would happen. The display should continue to show the previous reading while the sensor is being read and only then should the display be updated. It sounds like you might be clearing the display, reading the sensor, and then printing the reading to the display. Even if there is a blocking delay while the sensor is read, there should be no reason why the display needs to be blank during that time.

Please post your full code. If it exceeds the 9000 character limit allowed by the forum, you can add it as an attachment. If you click the "Reply" button, you will see a link "Attachments and other options".

pert:
this is the library I used:
Arduino/libraries/DHTlib at master · RobTillaart/Arduino · GitHub. I compared it to the library that was originally used in the project:
GitHub - winlinvip/SimpleDHT: Simple, Stable and Fast Arduino Temp & Humidity Sensors for DHT11 an
and found it to be a little faster

Reading that now, it seems a bit ambiguous. I meant that DHTlib is faster than SimpleDHT.

Yes these DHT series are slow sensors, These sensors takes a while to read the data, its better to create a delay or a timer of around 1.5 to 2.5 sec before reading the values.

unsigned long  DHTTimer = 0;
unsigned long  DHTInterval = 2340;
void setup{
dht.begin();
}

void loop{

if(millis()-DHTTimer>=DHTInterval){

float temp = dht.readTemperature();
float humid = dht.readHumidity();

if(isnan(temp)&&isnan(humid)){
   return;
}
}
}

if you directly access the value without some delay it will show not a number of values.

or either you can use other sensors like SHT31, HIH6130, SHT25 etc these have I2C compatibility and use only 2 wires.

Yeah, we already know about the required minimum interval between reads. That's not the issue. The slowness we're talking about is the time the library's sensor read function takes to return.

I did mention the interval in passing before, but only because I was curious that the number my datasheet specified was to short for my DHT11 and the one Gibbo3771 had was much more conservative. But really that was off topic for this thread.

pert:
Yeah, we already know about the required minimum interval between reads. That's not the issue. The slowness we're talking about is the time the library's sensor read function takes to return.

Pretty much.

pert:
I don't understand why that would happen. The display should continue to show the previous reading while the sensor is being read and only then should the display be updated.

It is showing the last value, the problem is because it's a 12 pin 4 digit display, so it displays the first number correctly but the remaining 3 are unlit because the illusion of it updating so quick does not exist. I actually didn't make this clear lol.

I've only used the 7 segment displays with the TM1637 driver. Those maintain whatever you've printed to them last on all 4 digits. So a workaround for your problem would be to change your display hardware.

I see there are multiple function calls to the DHT11 library. If each of those causes a significant delay, you could update the display between each function call.

In my project, I had the problem of needing to read from a DHT11 and a different sensor that was also slow, update an LCD, etc., all while keeping a stepper motor turning steadily. In the end, I used a timer interrupt to update the stepper since no matter what I did the blocking delays were too long otherwise.

I see there are multiple function calls to the DHT11 library. If each of those causes a significant delay, you could update the display between each function call.

Why on earth did I not think of that? Unfortunately it is only one function that is causing the delay, however is it possible to do the following, some pseudo code:

loop

if signal is not sent
    send signal, pull data low
    set timer
    signal sent

if signal sent and timer expired (20ms)
    pull data high
    read temperature, store data

update display

Would this work? So rather than using a hardware timer, I am using a software timer? I can even set the timer to 100ms, or use the clock speed of the chip to ensure that no matter how fast/slow it is running, it still times 20ms. This allows me to keep updating the display.

Or does the line have to be pulled low with a hardware timer to work? I am planning on giving it a bash anyway, while I await your reply :p.

I think you're on the right track. If I was to write a DHT library I would do something like this to avoid long blocking delays (but note I don't actually know anything about the communication protocol so this is just a vague guess):

void DHT::startRead() {
  readStep = startReadStep;
  readTimestamp = millis();
}

bool DHT::update() {
  if ((readStep != finishedReadStep) && (millis() - readTimestamp > interval)) {
    readTimestamp = millis();
    switch (readStep) {
      case startReadStep:
        // start the read
        readStep = secondReadStep;
        break;
      case secondReadStep:
        // do whatever comes next
        readStep = finishReadStep;
        break;
      case finishReadStep:
        // set temperature and humidity variables according to the sensor reading
        readStep = finishedReadStep;
        break;
    }
    return false;
  }
  return true;
}

byte DHT::getTemperature() {
  return temperature;
}

byte DHT::getHumidity() {
  return humidity;
}

Then the sketch code would look something like this:

DHT dht;
void setup() {
  Serial.begin(9600);
  dht.startRead();
}

void loop() {
  if(dht.update()) {
    Serial.println(dht.getTemperature());
    Serial.println(dht.getHumidity());
  }
}

The trouble that might arise is if the DHT signal interval timing can't exceed a maximum duration and the user is not calling DHT::update() frequently enough from their sketch. That is where interrupts can be useful, because they will fire no matter what sort of blocking code the user is running (as long as interrupts aren't disabled at the time). But interrupts complicate things greatly and if the user is trying to use multiple libraries that use the same interrupt vector it's a big problem.

In my project, I didn't even look into the DHT library code. Instead I just used the hardware timer for the servo so that it could continue to be updated even while the DHT library was taking its sweet time blocking my loop(). But I happened to get lucky that nothing else in the project was using Timer1. Even then, I had a problem with an atomicity bug as the servo code I found was not written correctly. I usually try to avoid using interrupts due to this sort of added complexity but when there is no other alternative they are incredibly useful.

Thanks for all the help, I got it working with only a very very slight flicker during reads, almost unnoticeable but I am going to clean up the code as it's a bit rough :p.

I'll update the OP with my outcome.