Even though these sensors typically have 4 pins, only 3 are used. For example, here is the wiring diagram from the DHT11 datasheet:
In the diagram above, "MCU" is the microcontroller, i.e your Arduino UNO, Feather M4 Express, Raspberry Pi, or whatever you are connecting the DHT11/22 sensors to. There's only one pin used for data. This is a digital pin, so is either HIGH or LOW. The sensor readings (humidity, temperature) need to be sent over as a stream of 1's and 0's. But how is that done? Sure, HIGH=1 and LOW=0, but how does the MCU know when to read the DATA line? The answer is timing.
Here is a timing diagram taken from the same datasheet:
So a 0 is represented by a HIGH time of 26us-28us and a 1 is represented by a HIGH time of 70us. That "us" is units of microseconds. To put that in some perspective, Arduino's much used delay()
function uses units of milliseconds. The smallest delay you can program with that function is:
delay(1);
which ends up being 1000 microseconds. Yes, there is the Arduino delayMicroseconds()
function which works in units of microseconds. But delaying is not what is needed to read the signals. It's the ability to measure the pulse with microsecond level precision. Or at least with enough precision to be able to discern the 0 pulse from a 1 pulse. This can be non-trivial.
Trivial If...
There are definitely ways to measure that microsecond level data signal. If you had a microcontroller, like an Arduino UNO, and all it had to do was measure that signal, then it could do so reasonably well. Here's the section of code from the Adafruit DHT Arduino library that does the actual timing measurement:
uint8_t portState = level ? _bit : 0; while ((*portInputRegister(_port) & _bit) == portState) { if (count++ >= _maxcycles) { return TIMEOUT; // Exceeded timeout, fail. } }
That code is for AVR microcontrollers, like the Arduino UNO. For non-AVR boards, like an ARM based Feather M4 Express, this code is used:
while (digitalRead(_pin) == level) { if (count++ >= _maxcycles) { return TIMEOUT; // Exceeded timeout, fail. } }
Both of these do the same thing - loop forever counting clock cycles until the pin state changes. Nothing else is happening while these loops run! And there are about 40 bits that need to be read!
So. If you can spare all those clock cycles to nothing but reading the signal, it can be done.
Somewhat Trivial If...
Another approach is to use a timer/counter peripheral to do the heavy lifting. Instead of directly reading the data signal in code (like previous section), a separate dedicated piece of hardware can read the signal. Most microcontrollers provide one or more timer/counter.
However, this peripheral still needs to be configured and managed by the host microcontroller. Doing that to read in all the bits from the DHT11/22 can end up with the same issue as above - trivial if you have nothing else to do.
Non-Trivial If...
Once you move beyond a simple "hello world" example of reading and printing the DHT11/22 senor values, things can quickly become non-trivial. The blocking (nothing else can run) nature of the data read can get in the way of other tasks.
Another example is when trying to use a DHT11/22 on a single board computer like a Raspberry Pi. Now there is a full Linux operating system running numerous processes. The approach of simply "do nothing but read data pulses" does not work well (if at all) on such a system.
Even If...
These sensors also tend to be just plain finicky. Even if the tight timing requirement can be met, sometimes the signal read cannot be decoded properly.
That's why you see code like this, from the Adafruit DHT Arduino library example:
// Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t) || isnan(f)) { Serial.println(F("Failed to read from DHT sensor!")); return; }
Basically - something happened, don't care what, just try again.
Maybe there are code and other fixes for all of these issues, but a better option is to just get away from this style sensor.
Text editor powered by tinymce.