Built-in project · Arduino Uno R3

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.

DHT11 on A0, read with no library · Arduino Uno R3live0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

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.

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 pinConnects to
Temperature and humidity, DHT22 pin GNDArduino Uno R3 pin GND
Temperature and humidity, DHT22 pin VCCArduino Uno R3 pin 5V
Temperature and humidity, DHT22 pin DATAArduino 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.

sketch.ino · Arduino Uno R3
// 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 keep delay(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.