arduino-estacion-meteorologica-dht22-lcd

How to Build a Weather Station with Arduino

  • 6 min

An essential weather station is a system that measures environmental variables and displays their values. In this project, we will read temperature and relative humidity and show them on an LCD screen.

Although it is tempting to use the cheap blue sensor (DHT11), at this level we strongly recommend the DHT22 (the white one). The difference in accuracy and range is abysmal for just a few cents difference.

Communication Protocols

Here we face two distinct communication challenges:

  1. The Sensor: The DHT family uses a proprietary protocol over a single data wire (not the Dallas/Maxim 1-Wire bus). Arduino sends a start pulse and then measures a series of pulses that encode the temperature and humidity.
  2. The Display (I2C): Older LCD screens required 6 or 8 wires to operate. We will use an I2C module. This protocol allows us to control the screen using only 2 wires (SDA and SCL), thanks to each device having a unique address (like an IP, but in hardware).

Components

  • [1x|blue] Arduino Uno | The brain of the project
  • [1x|green] DHT22 Sensor (AM2302) | Temperature and humidity {more accurate than DHT11}
  • [1x|purple] 16x2 LCD Screen with I2C adapter | Data visualization
  • [1x|gray] 4.7kΩ or 10kΩ Resistor | Only if the DHT22 is sold loose {pull-up}
  • [Various|gray] Breadboard and wires | Connections

Wiring Diagram

We will connect two devices simultaneously.

DHT22 Sensor

The DHT22 uses a single data wire. If the sensor is loose, add a pull-up resistor between VCC and DATA.

[ { “from”: “DHT22 VCC”, “to”: “Arduino 5V”, “color”: “red” }, { “from”: “DHT22 GND”, “to”: “Arduino GND”, “color”: “black” }, { “from”: “DHT22 DATA”, “to”: “Pin 2”, “color”: “green”, “note”: “4.7kΩ pull-up if loose” } ]

I2C LCD Display

The display shares the I2C bus of the Arduino Uno/Nano: SDA on A4 and SCL on A5.

[ { “from”: “LCD I2C SDA”, “to”: “Pin A4”, “color”: “blue”, “note”: “I2C Bus” }, { “from”: “LCD I2C SCL”, “to”: “Pin A5”, “color”: “purple”, “note”: “I2C Bus” }, { “from”: “LCD I2C VCC”, “to”: “Arduino 5V”, “color”: “red” }, { “from”: “LCD I2C GND”, “to”: “Arduino GND”, “color”: “black” } ]

On the Arduino UNO R3, the SDA and SCL pins are also duplicated near the reset button, explicitly labeled. They are the same as A4 and A5.

The Code

To make this work without going crazy decoding bits, we need two standard libraries that you must install from the IDE Library Manager:

  • DHT sensor library (by Adafruit).
  • A LiquidCrystal_I2C library compatible with your adapter. There are several implementations with that name, so make sure its examples use lcd.init() and the same constructor as this code.

Time Between Readings

DHT sensors are “slow”. The DHT22 has a sampling rate of 0.5 Hz (one reading every 2 seconds). If we try to read it faster, it will return errors or old data.

Therefore, we cannot bombard the sensor on every loop cycle. We will use millis() to read only at specific intervals, leaving the processor free to do other things (like updating the display or blinking an LED).

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <DHT.h>

// --- Hardware Configuration ---
const int PIN_DHT = 2;
#define TIPO_DHT DHT22   // If using the blue one, change to DHT11

// I2C address of the LCD: usually 0x27 or 0x3F.
// If it doesn't work, run an I2C scanner to find it.
LiquidCrystal_I2C lcd(0x27, 16, 2);  

DHT dht(PIN_DHT, TIPO_DHT);

// --- Time Variables ---
unsigned long tiempoAnterior = 0;
const unsigned long INTERVALO_LECTURA = 2500UL; // Read every 2.5 seconds

// Custom character for the DEGREES symbol (°)
byte simboloGrados[8] = {
  0b00110,
  0b01001,
  0b01001,
  0b00110,
  0b00000,
  0b00000,
  0b00000,
  0b00000
};

void setup() {
  Serial.begin(9600);
  
  // Initialize Sensor
  dht.begin();
  
  // Initialize Display
  lcd.init();
  lcd.backlight(); // Turn on backlight
  
  // Create the custom character in LCD memory (position 0)
  lcd.createChar(0, simboloGrados);

  // Welcome message
  lcd.setCursor(0, 0);
  lcd.print("Weather Stn.");
  lcd.setCursor(0, 1);
  lcd.print("Starting...");
  delay(2000);
  lcd.clear();
}

void loop() {
  unsigned long tiempoActual = millis();

  // Only read if the stipulated time has passed
  if (tiempoActual - tiempoAnterior >= INTERVALO_LECTURA) {
    tiempoAnterior = tiempoActual;
    
    leerYMostrarDatos();
  }
}

void leerYMostrarDatos() {
  // Read humidity and temperature
  float humedad = dht.readHumidity();
  float temperatura = dht.readTemperature();

  // Check if the reading failed (isnan = is not a number)
  if (isnan(humedad) || isnan(temperatura)) {
    Serial.println("Error reading from DHT sensor!");
    lcd.setCursor(0, 0);
    lcd.print("Sensor Error    ");
    return;
  }

  // --- Display on Serial (Debugging) ---
  Serial.print("Humidity: ");
  Serial.print(humedad);
  Serial.print(" %\t");
  Serial.print("Temp: ");
  Serial.print(temperatura);
  Serial.println(" *C");

  // --- Display on LCD ---
  // Line 0: Temperature
  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(temperatura, 1); // 1 decimal place
  lcd.write(0); // Write our degree symbol
  lcd.print("C   "); // Extra spaces to clean residues

  // Line 1: Humidity
  lcd.setCursor(0, 1);
  lcd.print("Hum:  ");
  lcd.print(humedad, 1);
  lcd.print("%   ");
}
Copied!

Analysis

Notice three important details in this code:

  1. Error Handling (isnan): We never assume the hardware works. If the cable comes loose, the sensor returns NaN. If we don’t detect it, the screen would show strange things.
  2. Output Formatting: When printing to the LCD, we use lcd.print(variable, 1) to limit to a single decimal place. Displaying “23.4532ºC” with a 5€ sensor makes no physical sense.
  3. Clean Refresh: We write blank spaces at the end of lines ("C "). This is a very old trick: if the temperature drops from “100.0” to “99.0”, the spaces erase the leftover last digit without needing to use lcd.clear(), which often causes annoying flickering.

Next Steps

This station is passive: it only shows what it measures. Here are some possible expansions:

  1. Atmospheric Pressure: Add a BMP280 sensor (also uses I2C) and record the pressure trend. A drop can accompany weather changes, but alone it doesn’t allow a reliable prediction.
  2. Data Logger: Connect a MicroSD card module to save the data in a CSV file for later analysis in Excel.
  3. Jump to IoT: Replace the Arduino Uno with an ESP32. The idea is similar, although you’ll need to adapt pins and libraries to send data to a service or Home Assistant.