Built-in project · Arduino Uno R3

Weather station on an Arduino Uno

A DHT22 on one wire and a 16x2 I2C display on two. Forty bits and a checksum, a first reading that fails, and a sensor that will not be hurried. The whole build, an Arduino Uno and 2 more parts, 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 $38. On Mokxi they're free.

Step by step, with the wiring and the common mistakes: Arduino DHT22 and DHT11: Temperature and Humidity

The circuit runs here once the page has loaded.

What you'll learn

  • Reading a DHT22 on one wire
  • Driving a 16x2 display over I2C with two wires
  • Why a sensor needs a second to settle after power-up
  • Checking data with a checksum
  • Using tenths instead of floating point numbers
  • Difficulty: Intermediate
  • Time: About 15 minutes
  • Board: Arduino Uno R3
  • Parts: 4

How it works

A DHT22 on pin 2 measures temperature and humidity over a single wire. The sensor answers with 40 pulses whose widths are the bits, and the driver checks the answer with a checksum. A 16x2 display on the I2C pins A4 and A5 shows the result.

The sketch asks every 2.5 seconds, a little slower than the sensor's own two second rate. The first request on purpose lands in the sensor's settling second, so you see a no answer message once. After that the display shows the temperature in C and the humidity in percent.

Parts list

4 parts, plus the jumper wires. They retail for about $38; on Mokxi they're free.

Kits that can build it

These 3 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 pinConnects to
Temperature and humidity, DHT22 pin DATAArduino Uno R3 pin 2
Temperature and humidity, DHT22 pin VCCArduino Uno R3 pin 5V
Character LCD with I2C backpack pin VCCArduino Uno R3 pin 5V
Temperature and humidity, DHT22 pin GNDArduino Uno R3 pin GND
Character LCD with I2C backpack pin GNDArduino Uno R3 pin GND
Character LCD with I2C backpack pin SDAArduino Uno R3 pin A4
Character LCD with I2C backpack pin SCLArduino Uno R3 pin A5

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
// Weather station: a DHT22 on one wire, the reading on an I2C display.
//
//   pin 2   the DHT22's DATA. The module carries its own pull-up.
//   LCD     SDA -> A4    SCL -> A5    VCC -> 5 V    GND -> GND
//
// Two wires for the display and *one* for the sensor, which is why this pair
// is in every kit. There is a great deal going on under those three wires and
// almost none of it is visible from the sketch, so it is worth writing down.
//
// # One wire, and it is all timing
//
// There is no bus standard on the sensor's pin. The board pulls the line down
// to ask, lets go, and the sensor answers with forty pulses whose *widths* are
// the bits: 50 us of low, then 27 us of high for a nought and 70 us for a one.
// The driver (firmware/lib/mokxi_dht.h) is a stopwatch and nothing else.
//
// Five bytes come back (humidity high, humidity low, temperature high,
// temperature low, checksum), and the checksum is the low eight bits of the
// sum of the other four. If it does not match, the reading is thrown away.
// That happens on a real bench and it happens here.
//
// # The first reading always fails
//
// Both datasheets say to leave the sensor alone for a second after power comes
// up, and Mokxi's part does exactly that: a request inside that second is not
// answered at all. So the first loop prints "waiting" rather than a number,
// and `setup()` does not hide it behind a delay, because meeting it once is
// worth more than never seeing it.
//
// # Ask it twice in two seconds and it repeats itself
//
// The DHT22 measures every two seconds and hands back the last reading in
// between. Drag the temperature slider and watch: the display does not move
// until the sensor has looked again. The sketch asks every two and a half
// seconds, which is the rate the sheet wants.
//
// # No floating point
//
// This board has none. Everything here is tenths (23.4 C is 234), and the
// display's printFixed() puts the point back in. A float would be a library
// call per reading for accuracy that was never in the sensor.

#include "mokxi_dht.h"
#include "mokxi_lcd1602_i2c.h"

const int DHT_PIN = 2;
const int SDA_PIN = A4;
const int SCL_PIN = A5;

// Every two and a half seconds: past the sensor's own two.
const unsigned long PERIOD_MS = 2500;

Dht sensor(DHT_PIN, DHT22);
Lcd1602I2c lcd(SCL_PIN, SDA_PIN);

unsigned long askedAt = 0;
int readings = 0;
int failures = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("Mokxi Uno: DHT22 on pin 2, reading on the I2C display");
  sensor.begin();
  lcd.begin();
  lcd.print("DHT22 weather");
  lcd.setCursor(0, 1);
  lcd.print("waiting...");
}

void loop() {
  // The very first loop asks straight away, on purpose: that lands inside the
  // sensor's settling second and gets no answer, which is what a real one does
  // and is worth seeing once.
  if (readings + failures > 0 && millis() - askedAt < PERIOD_MS) {
    return;
  }
  askedAt = millis();

  unsigned char frame[5];
  if (!sensor.read(frame)) {
    failures++;
    Serial.print("no answer (");
    Serial.print(failures);
    Serial.println(" so far)");
    lcd.setCursor(0, 1);
    lcd.print("no answer");
    lcd.padLine();
    return;
  }

  readings++;
  int celsius = sensor.temperatureTenths(frame);
  int humidity = sensor.humidityTenths(frame);

  lcd.setCursor(0, 0);
  lcd.printFixed(celsius, 1);
  lcd.print(" C");
  lcd.padLine();
  lcd.setCursor(0, 1);
  lcd.printFixed(humidity, 1);
  lcd.print(" %RH");
  lcd.padLine();

  Serial.print("reading ");
  Serial.print(readings);
  Serial.print(": ");
  Serial.print(celsius / 10);
  Serial.print(".");
  Serial.print(celsius < 0 ? -(celsius % 10) : celsius % 10);
  Serial.print(" C, ");
  Serial.print(humidity / 10);
  Serial.println(" %RH");
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Starts the Serial Monitor, the sensor and the display, and shows a waiting message. There is deliberately no delay here, so the first loop asks the sensor too early and meets the no answer case once.

void setup() {
  Serial.begin(115200);
  Serial.println("Mokxi Uno: DHT22 on pin 2, reading on the I2C display");
  sensor.begin();
  lcd.begin();
  lcd.print("DHT22 weather");
  lcd.setCursor(0, 1);
  lcd.print("waiting...");
}

loop sketch.ino

Returns early until 2500 ms have passed since the last request. Then it reads a five byte frame from the sensor. On failure it counts it and shows no answer. On success it converts the frame to tenths of a degree and of a percent, shows them on the display and prints them.

void loop() {
  // The very first loop asks straight away, on purpose: that lands inside the
  // sensor's settling second and gets no answer, which is what a real one does
  // and is worth seeing once.
  if (readings + failures > 0 && millis() - askedAt < PERIOD_MS) {
    return;
  }
  askedAt = millis();

  unsigned char frame[5];
  if (!sensor.read(frame)) {
    failures++;
    Serial.print("no answer (");
    Serial.print(failures);
    Serial.println(" so far)");
    lcd.setCursor(0, 1);
    lcd.print("no answer");
    lcd.padLine();
    return;
  }

  readings++;
  int celsius = sensor.temperatureTenths(frame);
  int humidity = sensor.humidityTenths(frame);

  lcd.setCursor(0, 0);
  lcd.printFixed(celsius, 1);
  lcd.print(" C");
  // ... 16 more lines in the editor

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The display always says no answer This project
    Check that DATA goes to pin 2 and that the sensor has 5 V and ground.
  • The display shows nothing This project
    Wire SDA to A4 and SCL to A5, and give the display 5 V and ground.
  • 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.
  • The screen stays blank or shows only a row of blocks. Character LCD with I2C backpack
    The I2C address is usually 0x27 or 0x3F. Run an I2C scanner to find yours. Then turn the contrast trimmer on the backpack slowly until text appears.
  • The backlight is on but there is no text, or text is garbled. Character LCD with I2C backpack
    Wire SDA and SCL to the right pins (A4 and A5 on an Uno), power it from 5 V, and call lcd.init() and the backlight call in setup.
  • 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.