Community project

Temperature Humidity Trend Display

kickazor

Published July 31, 2026 · Updated July 31, 2026

ESP321 component4 assembly steps
Remix this project
Photo of Temperature Humidity Trend Display

This project builds a real-time temperature and humidity monitor using an ESP32 and DHT22 sensor. The display shows current readings alongside minimum and maximum values tracked over time, updating every 30 seconds to give a clear picture of environmental trends.

The guide includes a complete wiring diagram, parts list, and ready-to-deploy firmware. Assembly takes just minutes: identify the DHT22 pins, make three connections (power, ground, and data), then upload the code to start monitoring.

Wiring diagram

Interactive · read-only
Wiring diagram for Temperature Humidity Trend Display

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
DHT22DHT22 / AM23021Digital temperature and humidity sensor

Assembly

4 steps
  1. Power down and identify the DHT22 pins

    Disconnect the M5Stack Core2 from USB. With the DHT22 grille facing you, a bare four-pin DHT22 is normally VCC, DATA, NC, GND from left to right. If yours is on a three-pin breakout board, follow its printed VCC, DATA/OUT, and GND labels.

    • Tip: A four-pin bare DHT22 needs a 4.7 kΩ to 10 kΩ pull-up resistor between VCC and DATA. Most three-pin breakout boards include this resistor.
    • Tip: Keep the sensor away from the Core2's warm rear enclosure and direct sunlight for representative readings.
    • Use 3.3 V for the DHT22 VCC, not 5 V.
    • Confirm the pin order before connecting power; reversing VCC and GND can damage the sensor.
  2. Wire power and ground

    Connect DHT22 VCC to the Core2 expansion header's 3.3 V pin and DHT22 GND to a GND pin. For a bare four-pin sensor, fit the pull-up resistor from VCC to DATA.

    • Tip: The sensor and Core2 must share ground.
    • Tip: Leave the bare sensor's NC pin disconnected.
  3. Connect the sensor data line

    Connect the DHT22 DATA/OUT pin to GPIO13 on the Core2 expansion header. Do not connect the DATA wire to any other GPIO.

    • Tip: A three-pin DHT22 breakout generally includes its own DATA pull-up resistor.
    • Tip: The display history grows from startup until it contains one hour of readings.
    • For a bare DHT22, a missing DATA pull-up resistor causes unreliable or failed readings.
  4. Power and deploy

    Recheck VCC, GND, and GPIO13, then reconnect USB. Use Schematik’s Deploy button to build and flash the project. Live values update every 5 seconds; cyan is temperature and yellow is humidity in the one-hour chart.

    • Tip: The circular history buffer holds 720 valid samples: one hour at the required 5-second interval.
    • Tip: A red “DHT22 read failed” message usually indicates wiring, pin-order, or pull-up-resistor trouble.
    • History is stored in RAM, so it starts over after reset or power loss.

Pin assignments

Board wiring reference
PinConnectionType
3V3dht22_1 VCCpower
GNDdht22_1 GNDground
GPIO 13dht22_1 DATAdata

Firmware

ESP32
main.cppDeploy to device
#include <Arduino.h>
#include <M5Unified.h>
#include <DHT.h>
#include <math.h>


// Forward declarations
void drawStaticScreen();
void showReading(float temperature, float humidity);
void takeSample();

constexpr uint8_t DHT_PIN = 13;
constexpr uint8_t DHT_SENSOR_TYPE = DHT22;
constexpr uint32_t SAMPLE_INTERVAL_MS = 30000UL;
constexpr int SCREEN_W = 320;

DHT dht(DHT_PIN, DHT_SENSOR_TYPE);
uint32_t lastSampleMs = 0;
bool haveValidReading = false;
float minTemperature = 0.0f;
float maxTemperature = 0.0f;
float minHumidity = 0.0f;
float maxHumidity = 0.0f;

void drawStaticScreen() {
  M5.Lcd.fillScreen(TFT_BLACK);
  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  M5.Lcd.drawCentreString("DHT22 Environment Monitor", SCREEN_W / 2, 18, 2);
  M5.Lcd.drawFastHLine(8, 42, 304, TFT_DARKGREY);

  M5.Lcd.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
  M5.Lcd.drawCentreString("Temperature", 80, 70, 2);
  M5.Lcd.drawCentreString("Humidity", 240, 70, 2);
  M5.Lcd.drawFastVLine(160, 64, 135, TFT_DARKGREY);

  M5.Lcd.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
  M5.Lcd.drawCentreString("Min:", 22, 170, 2);
  M5.Lcd.drawCentreString("Max:", 22, 190, 2);
  M5.Lcd.drawCentreString("Min:", 182, 170, 2);
  M5.Lcd.drawCentreString("Max:", 182, 190, 2);

  M5.Lcd.setTextColor(TFT_DARKGREY, TFT_BLACK);
  M5.Lcd.drawCentreString("Updates every 30 seconds", SCREEN_W / 2, 215, 2);
}

void showReading(float temperature, float humidity) {
  M5.Lcd.fillRect(55, 101, 97, 104, TFT_BLACK);
  M5.Lcd.fillRect(215, 101, 97, 104, TFT_BLACK);

  M5.Lcd.setTextColor(TFT_CYAN, TFT_BLACK);
  M5.Lcd.drawCentreString(String(temperature, 1) + " C", 80, 115, 4);
  M5.Lcd.drawString(String(minTemperature, 1) + " C", 62, 170, 2);
  M5.Lcd.drawString(String(maxTemperature, 1) + " C", 62, 190, 2);

  M5.Lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
  M5.Lcd.drawCentreString(String(humidity, 1) + " %RH", 240, 115, 4);
  M5.Lcd.drawString(String(minHumidity, 1) + " %", 222, 170, 2);
  M5.Lcd.drawString(String(maxHumidity, 1) + " %", 222, 190, 2);
}

void takeSample() {
  const float humidity = dht.readHumidity();
  const float temperature = dht.readTemperature();

  if (isnan(temperature) || isnan(humidity)) {
    M5.Lcd.fillRect(8, 101, 304, 62, TFT_BLACK);
    M5.Lcd.setTextColor(TFT_RED, TFT_BLACK);
    M5.Lcd.drawCentreString("DHT22 read failed", SCREEN_W / 2, 122, 2);
    return;
  }

  if (!haveValidReading) {
    minTemperature = maxTemperature = temperature;
    minHumidity = maxHumidity = humidity;
    haveValidReading = true;
  } else {
    minTemperature = min(minTemperature, temperature);
    maxTemperature = max(maxTemperature, temperature);
    minHumidity = min(minHumidity, humidity);
    maxHumidity = max(maxHumidity, humidity);
  }

  showReading(temperature, humidity);
}

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);
  M5.Lcd.setRotation(1);
  dht.begin();
  drawStaticScreen();
  takeSample();
  lastSampleMs = millis();
}

void loop() {
  M5.update();
  const uint32_t now = millis();
  if (now - lastSampleMs >= SAMPLE_INTERVAL_MS) {
    lastSampleMs += SAMPLE_INTERVAL_MS;
    takeSample();
  }
}

“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik