DHT11 on A0, read with no library, on an Arduino Uno
A DHT11 module on A0, read by hand: the sketch wakes the sensor, times each of its 40 bits, checks the sum and prints the humidity and temperature. The whole build, an Arduino Uno and 1 more part, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 15 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $34. On Mokxi they're free.
What you'll learn
- Bit-banging a one-wire protocol with
micros() - Using an analog pin as a digital pin
- Telling 0 from 1 by pulse length
- Checking data with a checksum
- Timeouts so a missing sensor does not hang the sketch
- Difficulty: Intermediate
- Time: About 15 minutes
- Board: Arduino Uno R3
- Parts: 3
How it works
The sketch talks to the sensor by hand, with no library. The data wire is on A0, which works as a digital pin. To start, the Uno pulls the line low for 20 ms and lets go. The sensor answers, then sends 40 bits.
Each bit is a low pulse followed by a high pulse. A short high of about 27 microseconds is a 0, and a long one of about 70 is a 1. The sketch times each high and treats anything over 45 as a 1.
The 40 bits make five bytes: humidity, its tenths, temperature, its tenths, and a checksum. If the checksum matches, the values print every 2 seconds. Otherwise the monitor says what went wrong.
Parts list
3 parts, plus the jumper wires. They retail for about $34; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × Temperature and humidity, DHT22
Kits that can build it
These 8 starter kits have every part in the circuit. Each kit's page lists what else it can build.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Temperature and humidity, DHT22 pin GND | Arduino Uno R3 pin GND |
| Temperature and humidity, DHT22 pin VCC | Arduino Uno R3 pin 5V |
| Temperature and humidity, DHT22 pin DATA | Arduino Uno R3 pin A0 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// DHT11 on A0, read with no library: the whole conversation by hand.
//
// DHT11 module S (data) -> A0 + -> 5V - -> GND
//
// An analog pin is a digital pin too, so A0 can talk to the sensor. The data
// line idles high on the module's pull-up, and the board speaks first:
//
// 1. hold the line low for 20 ms (the sensor's "wake up"), then let go;
// 2. the sensor answers low for 80 us, then high for 80 us;
// 3. it sends 40 bits, each a 50 us low and then a high: about 27 us high
// is a 0 and about 70 us is a 1;
// 4. the five bytes are humidity, humidity tenths, temperature,
// temperature tenths and a checksum, the low byte of the other four's sum.
//
// So the sketch only has to time how long each high lasts. A DHT11 sends
// whole numbers, so the tenths bytes are 0.
//
// Click the sensor and change the temperature or the humidity.
const int DHT_PIN = A0;
// Wait while the line stays at `level`. Returns the microseconds it took,
// or 0 when it took longer than `limit` (no sensor, or a lost bit).
unsigned int waitWhile(int level, unsigned int limit) {
unsigned long start = micros();
while (digitalRead(DHT_PIN) == level) {
if (micros() - start > limit) return 0;
}
unsigned int took = micros() - start;
return took == 0 ? 1 : took;
}
// Read the five bytes into `data`. Returns an error message, or 0.
const char *readDht11(byte data[5]) {
// The wake-up: low for 20 ms, then let the pull-up have the line.
pinMode(DHT_PIN, OUTPUT);
digitalWrite(DHT_PIN, LOW);
delay(20);
pinMode(DHT_PIN, INPUT);
// The answer: the line goes low, then high, then low for the first bit.
if (!waitWhile(HIGH, 200)) return "no answer";
if (!waitWhile(LOW, 200)) return "answer too long (low)";
if (!waitWhile(HIGH, 200)) return "answer too long (high)";
for (int i = 0; i < 40; i++) {
if (!waitWhile(LOW, 200)) return "lost a bit (low)";
unsigned int high = waitWhile(HIGH, 200);
if (!high) return "lost a bit (high)";
data[i / 8] <<= 1;
if (high > 45) data[i / 8] |= 1;
}
byte sum = data[0] + data[1] + data[2] + data[3];
if (sum != data[4]) return "checksum error";
return 0;
}
void setup() {
Serial.begin(9600);
Serial.println("Mokxi Uno: DHT11 on A0, no library");
// The sensor ignores anything in its first second after power comes up.
delay(1500);
}
void loop() {
byte data[5] = {0, 0, 0, 0, 0};
const char *error = readDht11(data);
if (error) {
Serial.print("DHT11: ");
Serial.println(error);
} else {
Serial.print("Humidity ");
Serial.print(data[0]);
Serial.print(".");
Serial.print(data[1]);
Serial.print(" % Temperature ");
Serial.print(data[2]);
Serial.print(".");
Serial.print(data[3]);
Serial.println(" C");
}
delay(2000);
}
Code walkthrough
The key functions of the sketch, and what each one does.
waitWhile sketch.ino
Waits while the line stays at a given level and returns how many microseconds that took. If it takes longer than the limit, it returns 0 to signal a timeout. This one helper both measures pulses and detects trouble.
// Wait while the line stays at `level`. Returns the microseconds it took,
// or 0 when it took longer than `limit` (no sensor, or a lost bit).
unsigned int waitWhile(int level, unsigned int limit) {
unsigned long start = micros();
while (digitalRead(DHT_PIN) == level) {
if (micros() - start > limit) return 0;
}
unsigned int took = micros() - start;
return took == 0 ? 1 : took;
}readDht11 sketch.ino
Sends the 20 ms wake-up, checks the sensor's reply, then reads 40 bits. For each bit it times the high pulse and shifts a 0 or 1 into the byte array. It finishes by comparing the checksum, and returns an error message or 0.
// Read the five bytes into `data`. Returns an error message, or 0.
const char *readDht11(byte data[5]) {
// The wake-up: low for 20 ms, then let the pull-up have the line.
pinMode(DHT_PIN, OUTPUT);
digitalWrite(DHT_PIN, LOW);
delay(20);
pinMode(DHT_PIN, INPUT);
// The answer: the line goes low, then high, then low for the first bit.
if (!waitWhile(HIGH, 200)) return "no answer";
if (!waitWhile(LOW, 200)) return "answer too long (low)";
if (!waitWhile(HIGH, 200)) return "answer too long (high)";
for (int i = 0; i < 40; i++) {
if (!waitWhile(LOW, 200)) return "lost a bit (low)";
unsigned int high = waitWhile(HIGH, 200);
if (!high) return "lost a bit (high)";
data[i / 8] <<= 1;
if (high > 45) data[i / 8] |= 1;
}
byte sum = data[0] + data[1] + data[2] + data[3];
if (sum != data[4]) return "checksum error";
return 0;
}loop sketch.ino
Calls readDht11 and prints either the error or the humidity and temperature, then waits 2 seconds before reading again.
void loop() {
byte data[5] = {0, 0, 0, 0, 0};
const char *error = readDht11(data);
if (error) {
Serial.print("DHT11: ");
Serial.println(error);
} else {
Serial.print("Humidity ");
Serial.print(data[0]);
Serial.print(".");
Serial.print(data[1]);
Serial.print(" % Temperature ");
Serial.print(data[2]);
Serial.print(".");
Serial.print(data[3]);
Serial.println(" C");
}
delay(2000);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The monitor says no answer This project
Check that the data pin goes to A0 and that the sensor has 5V and GND. - The monitor says checksum error This project
A bit was misread. Try again, and keepdelay(20)for the wake-up and the 45 microsecond threshold. - Readings are NaN or fail, or never appear. Temperature and humidity
Put a 10 k ohm pull-up between DATA and VCC (bare sensors need it). A 4-pin DHT22 has pin 3 unused. Check the data pin number in your code. - Values freeze or errors show up when you read often. Temperature and humidity
The DHT22 can only be read about once every 2 seconds. Wait between reads, and check for NaN before using the numbers. - Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected. - Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.