The Quick Fix: Why Your Arduino DHT Library Returns NaN
When your serial monitor spits out NaN (Not a Number) instead of a temperature or humidity reading, the microcontroller is failing to decode the sensor's 40-bit data packet. The NaN return from dht.readTemperature() or dht.readHumidity() almost always stems from a missing pull-up resistor, a 5V/3.3V logic mismatch, or a macro mismatch in your code.
- Pull-up Resistor: Is there a 4.7kΩ to 10kΩ resistor physically wired between the VCC and DATA pins? The DHT protocol requires this to pull the line high.
- Sensor Macro Mismatch: Does your code declare
#define DHTTYPE DHT11while you have a blue DHT22 (AM2302) plugged into the breadboard? The timing thresholds are entirely different. - Logic Level Voltage: If you are using a 3.3V board (like an ESP32) but powering the sensor from a 5V rail, the data line will output 5V highs, potentially damaging your GPIO or causing the library to misread the timing edges.
Hardware Spec Sheet & Pin Mapping
This guide targets the Arduino Uno R3 (ATmega328P, 5V logic). While the DHT11 is common in starter kits, its 1°C resolution and 20-80% RH range make it practically useless for precision environmental monitoring. We strongly recommend the DHT22.
Bill of Materials
- Microcontroller: Arduino Uno R3 (or compatible ATmega328P clone)
- Sensor: DHT22 / AM2302 (White housing, 4-pin or 3-pin module)
- Resistor: 10kΩ 1/4W carbon film (for pull-up, if using a raw 4-pin sensor)
- Decoupling Capacitor: 100nF (0.1µF) ceramic capacitor across VCC and GND
- Wiring: 22 AWG solid core jumper wires (keep data runs under 1 meter)
Sensor Specifications Comparison
| Parameter | DHT11 (Blue) | DHT22 / AM2302 (White) |
|---|---|---|
| Temperature Range | 0°C to 50°C | -40°C to 80°C |
| Temperature Resolution | 1.0°C | 0.1°C |
| Humidity Range | 20% to 80% RH | 0% to 100% RH |
| Humidity Resolution | 1% RH | 0.1% RH |
| Sampling Rate | 1 Hz (1 read/sec) | 0.5 Hz (1 read/2 sec) |
Pin Mapping Table (Arduino Uno R3)
| DHT22 Pin (Raw 4-Pin) | Module Pin (3-Pin PCB) | Arduino Uno R3 Connection |
|---|---|---|
| 1 (VDD) | VCC / + | 5V Pin |
| 2 (DATA) | DATA / OUT | Digital Pin 2 (with 10kΩ pull-up to 5V) |
| 3 (NC) | Not Present | Leave Disconnected |
| 4 (GND) | GND / - | GND Pin |
Bulletproof DHT22 Code for Arduino Uno R3
The following sketch uses the official Adafruit DHT Sensor Library. Unlike basic tutorials that use delay(2000) to wait between reads—which freezes your entire microcontroller—this code uses a non-blocking millis() timer. This allows your Arduino to handle button presses, display updates, or motor control while waiting for the mandatory 2-second DHT22 sampling window.
#include <DHT.h>
// --- PIN DEFINITIONS ---
#define DHTPIN 2 // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22 // Sensor type (DHT11, DHT22, or DHT21)
// Initialize DHT sensor for normal 16MHz Arduino
DHT dht(DHTPIN, DHTTYPE);
// --- TIMING VARIABLES ---
unsigned long previousMillis = 0;
const long interval = 2500; // DHT22 requires 2s minimum; 2.5s adds safety margin
void setup() {
Serial.begin(115200);
Serial.println(F("DHT22 Non-Blocking Initialization..."));
// Start the sensor
dht.begin();
}
void loop() {
unsigned long currentMillis = millis();
// Check if the sampling interval has passed
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Readings can take up to 250ms to complete
float humidity = dht.readHumidity();
float temperatureC = dht.readTemperature();
float temperatureF = dht.readTemperature(true);
// --- ERROR HANDLING ---
// Check if any reads failed and exit early (to try again)
if (isnan(humidity) || isnan(temperatureC) || isnan(temperatureF)) {
Serial.println(F("ERROR: Failed to read from DHT sensor! Check wiring and pull-up resistor."));
return;
}
// Compute heat index (must be in Fahrenheit)
float heatIndexF = dht.computeHeatIndex(temperatureF, humidity);
// --- OUTPUT ---
Serial.print(F("Humidity: "));
Serial.print(humidity);
Serial.print(F("% | Temp: "));
Serial.print(temperatureC);
Serial.print(F("°C / "));
Serial.print(temperatureF);
Serial.print(F("°F | Heat Index: "));
Serial.print(heatIndexF);
Serial.println(F("°F"));
}
// Other non-blocking code can run here
}
F() macro around your static serial strings (e.g., F("Humidity: ")). This forces the compiler to store the string in Flash memory rather than consuming your Uno's limited 2KB SRAM.
Debugging the "DHT timeout reading!" Error
The DHT protocol is not a true hardware bus like I2C or SPI; it is a proprietary, timing-critical single-bus protocol. The microcontroller must read voltage transitions with microsecond precision. When the timing drifts, the library throws a timeout.
If you enable debug modes or use wrapper libraries, you will see the exact error string: DHT timeout reading! or DHTLIB_ERROR_TIMEOUT. Alternatively, the compile-time error expected unqualified-id before numeric constant frequently halts compilation before you even reach the serial monitor.
Ranked Causes and Fixes
- Cause 1: Interrupt Starvation (Most Common on Complex Sketches)
ThereadTemperature()function disables interrupts to measure microsecond pulses. If you have heavy interrupt service routines (ISRs) running for encoders, or if you are using software serial, the DHT library misses the bit transitions.
Fix: Move the DHT read to a dedicated microsecond window where no other ISRs are firing, or switch to RobTillaart's DHTlib, which handles interrupt disabling more gracefully. - Cause 2: Wire Capacitance on Long Runs
Data lines longer than 1 meter act as capacitors. The 10kΩ pull-up resistor combined with the wire capacitance creates an RC low-pass filter, rounding off the sharp digital edges the DHT library relies on to measure timing.
Fix: Keep wires under 1 meter. If you must run 3 meters of shielded cable, drop the pull-up resistor to 2.2kΩ or 1kΩ to charge the line capacitance faster. - Cause 3: The Semicolon Macro Compile Error
If your serial monitor never even opens and the IDE throwsexpected unqualified-id before numeric constant, look at your pin definition. Beginners often write#define DHTPIN 2;. The preprocessor replaces every instance ofDHTPINwith2;, breaking the C++ syntax.
Fix: Remove the semicolon:#define DHTPIN 2.
Extending and Simplifying Your Sensor Build
Once you have stable readings, you will likely want to integrate the DHT22 into a larger IoT or logging system.
How to Extend the Build
- Add Local Display: Wire an SSD1306 128x64 I2C OLED display to the A4/A5 pins. Because the OLED uses I2C and the DHT uses a digital GPIO, they will not conflict.
- Add Wireless Telemetry: Upgrade from the Uno R3 to an ESP32 DevKit V1. Use the PubSubClient library to publish the
temperatureCandhumidityfloats to an MQTT broker like Mosquitto for Home Assistant integration.
How to Simplify the Build (The I2C Alternative)
If you are tired of dealing with 1-Wire timing timeouts and pull-up resistors, ditch the raw DHT22. Purchase an SHT31-D or an AM2320. These sensors offer equal or better accuracy but communicate over the standard I2C hardware bus. I2C handles all the timing via dedicated hardware clocks, completely eliminating DHT timeout reading! errors and freeing up your CPU cycles for other tasks.
Arduino DHT Library FAQ
Can I use the Arduino DHT library on an ESP32 3.3V board?
Yes, but you must manage the logic levels. The DHT22 outputs a HIGH signal equal to its VCC voltage. If you power the DHT22 from a 5V source, the DATA pin will output 5V, which will fry the ESP32's 3.3V GPIO pins. Either power the DHT22 from the ESP32's 3.3V rail (which works fine for short wire runs), or use a bidirectional logic level converter / voltage divider on the data line.
Why does my DHT11 read exactly 255 or -127 for temperature?
Returning 255 (for raw byte reads) or -127 / -99 (in older library versions) indicates a checksum failure or a complete read timeout where the uninitialized data buffer defaults to maximum byte values. The library's built-in math fails to validate the 8-bit checksum against the first 32 bits of data, triggering the error state. Check your breadboard connections for loose jumper wires.
Is the Adafruit DHT library better than DHTlib by RobTillaart?
It depends on your project constraints. The Adafruit library is beginner-friendly, natively returns floats, and includes heat index calculations out of the box. However, RobTillaart's DHTlib is significantly more memory-efficient, executes faster, and provides specific integer error codes (like DHTLIB_ERROR_CHECKSUM) which are invaluable for advanced debugging in production firmware.
How accurate is the DHT22 right out of the box?
The datasheet claims ±0.2°C accuracy, but bench testing against calibrated NIST-traceable thermometers reveals that cheap, unbranded DHT22 clones often read 1°C to 2°C high due to internal self-heating of the thermistor. For critical applications, apply a software calibration offset in your code (e.g., float calibratedTemp = temperatureC - 1.2;) after benchmarking your specific sensor against a known good reference.






