DHT11 and DHT22 bus should not be set output and high.

I really think that the DHT11 and DHT22 bus is a open collector bus, and the Arduino should never make the pin output and high.

Most libraries however set the datapin as output and high in the beginning of the data sequence. I think that is wrong.

The datasheets can be read both ways, but the pictures show a slow rising of the signal when it must turn high. That indicates that the pullup resistor makes the signal high, and not the microcontroller. Other datasheet says "releases the bus" for that or "pullup" or "RL pullup".
If the datasheet can be read both ways, then I assume that the bus is a open-collector bus. Therefor the Arduino should not set the signal output and high.

I'm hoping to find someone who can see through a bad translated chinees datasheet and agrees with me.

ADDED: I got the feeling that I was the only one who saw it this way, and I wrote: "This is a: "I'm right and everyone else is wrong"". But it turned out to be a known for years, and others who spend time to look into it already know this.

cycle

  • MCU (set pin to input with pullup) - then waits for high.
  • MCU set pin to output and pulls it low for at least 1ms
  • MCU set pin to input an ‘listen’ for startbit + 40 databits

repeat

So you agree that the resistor should pull it high, and the MCU should only listen. So the MCU should not set the data pin output and high.

That makes two of us :slight_smile:

That indicates that the pullup resistor makes the signal high, and not the microcontroller.

Yes it is principle of open drain.
It is clear: when the micro controller wants to talk to DHTxx he must put the line to 0V.

I think you do not see that the logic is reversed:
Non active state = boolean false = logical “0”, but the voltage = + 5V
Work = boolean true = logical “1”, but the voltage = 0V

There is no obligation to choose the level 0 volts for logic 0 and nothing prohibits to choose the level 5 Volts as logic 0. The only thing that is required is to announce the choice has been done.

It’s like this that all IC using open drain works,
It is because of this principle that you can do simple wired OR function that are so important in I2C busies.

I disagree the term “pull up” : for me a pull up is used to fix a potential, for example on input of a logic gate.
Without this pull up resistor the gate can works. It can be unstable without input signal, but in normal use with an input signal it works perfectly.

With open collector this is absolutely not the case, it is a misnomer because without this resistor the circuit can not works.
This resistor is not optional, it is mandatory : it is a load resistor.

For Aosong datasheet I totaly agree, there are raw Chinese/English Google translation without any correction.
The worse I found is for AM2320 : shitty POWER(shitty)
An attachment the best the least worse that I found for DHT22(AM2302)

For your information you can have a look :
http://sigrok.org/wiki/Protocol_decoder:Am230x

There is internal analysis of some DHT → they incorporate a 8 bits ST micro-controler.

AM2302.pdf (570 KB)

Of course, the data pin is active low.
You also agree, that the MCU should never set the data pin output and high ? That makes three of us.
Thanks for that link.

That means that the most used DHT libraries are wrong:

It seems that it is already known for years.
There are even pictures of the data signal during the "more or less a short circuit" !

+1
It would not make sense to have a device pull the line high with an active gate when there are multiple objects on the Data line. Since the boot-up sequence is not common nor synchronized among multiple devices, the Data line state must be assumed unknown and possibly dangerous.

Checked the lib. You're right, there is an error. (all my DHT11/22 still live)

Opened an issue on my repo so I will check / fix when time permits.

I have difficulty in understanding, not technical, but to well understand English. :fearful:

You seem to fear that we can have :
case 1:
Simultaneously :

  • Output of the atmega to High
  • DHT that draws the line to 0
    Which would effectively do a short-cut.

case 2 :
You also seem to fear that when there are multiple modules on the line there could have some which draw to 0 and the other to Vcc, doing short-cut.

If I well understand I replied :

Case 1:
You agree that this is not a normal case.
This would means that the I/o is in output mode and to a high level and DHT draws the line to 0.
If the timing is respected it is impossible except if someone take control of the pin out of the library, for example with an interrupt : it would be foolish,
Be default during wake up phase all I/O of microcontrolers are in INPUT mode.

Case 2:
It is impossible: The DHT protocol supports only one module on a line. That can not work with multiple module
It is Aosong one-wire bus, it is not Dallas one-wire bus .

In I2C mode there may be several modules but A4 and A5 are in this case true open-drain, and there are no risks.

Hi guys,

I just randomly saw this thread because I use DHT22 sensor in my small project for a while.

I don't have too much knowledge about electronics, but I am a native Chinese speaker. So I may help translate or interpret some documents.

On the Chinese version data sheet of DHT11 provided by Adafruit, it reads

After sending "start" signal, change MCU to input mode. The bus will be pulled up to high by the resistor. Output mode with high level is OK too.

So it makes clear that you can output high, instead of setting it to input mode and let the pull-up resistor do the work. I think that's why some popular libraries have that implementation. It does sound like a second choice though.

However this document is very old. Aosong (the manufacturer) published all the newest version of Chinese documents here: http://pan.baidu.com/share/home?uk=2119638989#category/type=0

I checked the newest datasheets for DHT11 and ASOM2312 (DHT22) (what 68tjs posted above should be translated from this) now they only say

Set MCU's I/O to output and output low level, for at least 18ms. Then set the I/O of MCU to input mode. Because of pull-up resistor, the I/O of MCU (a.k.a. DATA bus) will be high. Wait for DHT11 to response.

So my guess is, both should work normally but apparently input mode work better. So they updated their datasheet. Hardware revision may be also involved, but it's kinda unlikely.

I wish it helps. If you have any other questions about the datasheet feel free to ask me.

Thank you ! Mystery solved.
Perhaps it was possible in the past to set the bus high, perhaps it was a mistake in the datasheet. Perhaps the DHT11 did have electronics inside that allowed it. But the DHT22 today has a microcontroller, which does not allow to make the bus high.

long ago in a land far from here there was a bug to solve …

knut_ny:
cycle

  • MCU (set pin to input with pullup) - then waits for high.
  • MCU set pin to output and pulls it low for at least 1ms
  • MCU set pin to input an ‘listen’ for startbit + 40 databits

repeat

I have changed my DHT-stable code to not actively pull it HIGH and it works.

Not published on GITHUB yet (need to check some other things too)
but you can verify with the version below.

Comments and remarks are as always welcome

//
//    FILE: dht.h
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.13b
// PURPOSE: DHT Temperature & Humidity Sensor library for Arduino
//     URL: --
//
// HISTORY:
// see dht.cpp file
//

#ifndef dht_h
#define dht_h

#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

#define DHT_LIB_VERSION "0.1.13b"

#define DHTLIB_OK                0
#define DHTLIB_ERROR_CHECKSUM   -1
#define DHTLIB_ERROR_TIMEOUT    -2
#define DHTLIB_INVALID_VALUE    -999

#define DHTLIB_DHT11_WAKEUP     18
#define DHTLIB_DHT_WAKEUP       1

// max timeout is 100usec.
// For a 16Mhz proc that is max 1600 clock cycles
// loops using TIMEOUT use at least 4 clock cycli
// so 100 us takes max 400 loops
// so by dividing F_CPU by 40000 we "fail" as fast as possible
#define DHTLIB_TIMEOUT (F_CPU/40000)

class dht
{
public:
    // return values:
    // DHTLIB_OK
    // DHTLIB_ERROR_CHECKSUM
    // DHTLIB_ERROR_TIMEOUT
    int read11(uint8_t pin);
    int read(uint8_t pin);

    inline int read21(uint8_t pin) { return read(pin); };
    inline int read22(uint8_t pin) { return read(pin); };
    inline int read33(uint8_t pin) { return read(pin); };
    inline int read44(uint8_t pin) { return read(pin); };

    double humidity;
    double temperature;

private:
    uint8_t bits[5];  // buffer to receive data
    int _readSensor(uint8_t pin, uint8_t wakeupDelay);
};
#endif
//
// END OF FILE
//
//
//    FILE: dht.cpp
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.13b
// PURPOSE: DHT Temperature & Humidity Sensor library for Arduino
//     URL: --
//
// HISTORY:
// 0.1.13 fix negative temperature
// 0.1.12 support DHT33 and DHT44 initial version
// 0.1.11 renamed DHTLIB_TIMEOUT
// 0.1.10 optimized faster WAKEUP + TIMEOUT
// 0.1.09 optimize size: timeout check + use of mask
// 0.1.08 added formula for timeout based upon clockspeed
// 0.1.07 added support for DHT21
// 0.1.06 minimize footprint (2012-12-27)
// 0.1.05 fixed negative temperature bug (thanks to Roseman)
// 0.1.04 improved readability of code using DHTLIB_OK in code
// 0.1.03 added error values for temp and humidity when read failed
// 0.1.02 added error codes
// 0.1.01 added support for Arduino 1.0, fixed typos (31/12/2011)
// 0.1.0 by Rob Tillaart (01/04/2011)
//
// inspired by DHT11 library
//
// Released to the public domain
//

#include "dht.h"

/////////////////////////////////////////////////////
//
// PUBLIC
//

// return values:
// DHTLIB_OK
// DHTLIB_ERROR_CHECKSUM
// DHTLIB_ERROR_TIMEOUT
int dht::read11(uint8_t pin)
{
    // READ VALUES
    int rv = _readSensor(pin, DHTLIB_DHT11_WAKEUP);
    if (rv != DHTLIB_OK)
    {
        humidity    = DHTLIB_INVALID_VALUE; // invalid value, or is NaN prefered?
        temperature = DHTLIB_INVALID_VALUE; // invalid value
        return rv;
    }

    // CONVERT AND STORE
    humidity    = bits[0];  // bits[1] == 0;
    temperature = bits[2];  // bits[3] == 0;

    // TEST CHECKSUM
    // bits[1] && bits[3] both 0
    uint8_t sum = bits[0] + bits[2];
    if (bits[4] != sum) return DHTLIB_ERROR_CHECKSUM;

    return DHTLIB_OK;
}


// return values:
// DHTLIB_OK
// DHTLIB_ERROR_CHECKSUM
// DHTLIB_ERROR_TIMEOUT
int dht::read(uint8_t pin)
{
    // READ VALUES
    int rv = _readSensor(pin, DHTLIB_DHT_WAKEUP);
    if (rv != DHTLIB_OK)
    {
        humidity    = DHTLIB_INVALID_VALUE;  // invalid value, or is NaN prefered?
        temperature = DHTLIB_INVALID_VALUE;  // invalid value
        return rv; // propagate error value
    }

    // CONVERT AND STORE
    humidity = word(bits[0], bits[1]) * 0.1;
    temperature = word(bits[2] & 0x7F, bits[3]) * 0.1;
    if (bits[2] & 0x80)  // negative temperature
    {
        temperature = -temperature;
    }

    // TEST CHECKSUM
    uint8_t sum = bits[0] + bits[1] + bits[2] + bits[3];
    if (bits[4] != sum)
    {
        return DHTLIB_ERROR_CHECKSUM;
    }
    return DHTLIB_OK;
}

/////////////////////////////////////////////////////
//
// PRIVATE
//

// return values:
// DHTLIB_OK
// DHTLIB_ERROR_TIMEOUT
int dht::_readSensor(uint8_t pin, uint8_t wakeupDelay)
{
    // INIT BUFFERVAR TO RECEIVE DATA
    uint8_t mask = 128;
    uint8_t idx = 0;

    // EMPTY BUFFER
    for (uint8_t i = 0; i < 5; i++) bits[i] = 0;

    // REQUEST SAMPLE
    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);
    delay(wakeupDelay);
    pinMode(pin, INPUT);
    delayMicroseconds(40);

    // GET ACKNOWLEDGE or TIMEOUT
    uint16_t loopCnt = DHTLIB_TIMEOUT;
    while(digitalRead(pin) == LOW)
    {
        if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;
    }

    loopCnt = DHTLIB_TIMEOUT;
    while(digitalRead(pin) == HIGH)
    {
        if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;
    }

    // READ THE OUTPUT - 40 BITS => 5 BYTES
    for (uint8_t i = 40; i != 0; i--)
    {
        loopCnt = DHTLIB_TIMEOUT;
        while(digitalRead(pin) == LOW)
        {
            if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;
        }

        uint32_t t = micros();

        loopCnt = DHTLIB_TIMEOUT;
        while(digitalRead(pin) == HIGH)
        {
            if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;
        }

        if ((micros() - t) > 40)
        { 
            bits[idx] |= mask;
        }
        mask >>= 1;
        if (mask == 0)   // next byte?
        {
            mask = 128;
            idx++;
        }
    }

    return DHTLIB_OK;
}
//
// END OF FILE
//

Solved in both DHTstable version and the DHTLib (AVR optimized) version