1. Project Overview
In this tutorial, we'll build a real-time environmental monitor using the ESP32 microcontroller and DHT22 temperature/humidity sensor. The ESP32 will host its own web server accessible on your local WiFi network, displaying live sensor readings on a mobile-friendly dashboard.
2. Parts Required
| Component | Qty | Est. Price | Notes |
|---|---|---|---|
| ESP32 Dev Board | 1 | ~$5â8 | Any ESP32-WROOM-32 variant |
| DHT22 Sensor | 1 | ~$3â5 | More accurate than DHT11 |
| 10kΊ Resistor | 1 | ~$0.10 | Pull-up resistor for DHT22 |
| Breadboard | 1 | ~$2 | Half-size is sufficient |
| Jumper Wires | ~10 | ~$1 | M-M wires |
| USB Cable (Micro/USB-C) | 1 | Varies | For programming ESP32 |
Total project cost is under $20. All components are widely available on Tokopedia, Shopee, or AliExpress.
3. Wiring Diagram
Wire the DHT22 to your ESP32 as follows:
text
DHT22 Pin â ESP32 Pin âââââââââââââââââââââââââ VCC (Pin 1) â 3.3V Data (Pin 2)â GPIO 4 (+ 10kΊ pull-up to 3.3V) NC (Pin 3) â Not connected GND (Pin 4) â GND Note: Place the 10kΊ resistor between Data and VCC pins.
Use 3.3V for DHT22 with ESP32, NOT 5V. The ESP32 GPIO pins are NOT 5V tolerant. Using 5V may permanently damage your ESP32.
4. Arduino Code
First, install these libraries via Arduino IDE â Sketch â Include Library â Manage Libraries:
- DHT sensor library by Adafruit
- Adafruit Unified Sensor
- WiFi (built-in for ESP32)
cpp (Arduino)
#include <WiFi.h>
#include <WebServer.h>
#include <DHT.h>
#include <ArduinoJson.h>
// ââ Configuration ââ
#define DHTPIN 4 // GPIO connected to DHT22 data pin
#define DHTTYPE DHT22
const char* SSID = "Your_WiFi_SSID";
const char* PASSWORD = "Your_WiFi_Password";
// ââ Globals ââ
DHT dht(DHTPIN, DHTTYPE);
WebServer server(80);
// ââ Sensor Data ââ
float temperature = 0.0;
float humidity = 0.0;
unsigned long lastRead = 0;
const unsigned long READ_INTERVAL = 2000; // Read every 2 seconds
void readSensor() {
if (millis() - lastRead < READ_INTERVAL) return;
lastRead = millis();
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t) && !isnan(h)) {
temperature = t;
humidity = h;
Serial.printf("Temp: %.1f°C | Humidity: %.1f%%\n", t, h);
} else {
Serial.println("DHT22 read failed!");
}
}
void handleRoot() {
// Serve the web dashboard HTML (see next section)
server.send(200, "text/html", getDashboardHTML());
}
void handleData() {
// JSON API endpoint for live data
StaticJsonDocument<128> doc;
doc["temperature"] = temperature;
doc["humidity"] = humidity;
doc["heatIndex"] = dht.computeHeatIndex(temperature, humidity, false);
doc["timestamp"] = millis();
String output;
serializeJson(doc, output);
server.send(200, "application/json", output);
}
void setup() {
Serial.begin(115200);
dht.begin();
// Connect to WiFi
WiFi.begin(SSID, PASSWORD);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected! IP: " + WiFi.localIP().toString());
// Register routes
server.on("/", handleRoot);
server.on("/data", handleData);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
readSensor();
server.handleClient();
}
5. ESP32 Web Server Dashboard
The ESP32 serves an HTML page that fetches live data from the /data endpoint every 2 seconds:
cpp - getDashboardHTML()
String getDashboardHTML() {
return R"rawhtml(
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'/>
<meta name='viewport' content='width=device-width,initial-scale=1'/>
<title>ESP32 Monitor</title>
<style>
body{font-family:sans-serif;background:#0D1526;color:#fff;margin:0;padding:20px;text-align:center}
h1{font-size:1.5rem;color:#06B6D4;margin-bottom:30px}
.card{background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.1);
border-radius:16px;padding:30px;margin:15px auto;max-width:300px;display:inline-block}
.val{font-size:3rem;font-weight:900}
.unit{font-size:1.2rem;color:#94A3B8}
.label{font-size:14px;color:#64748B;margin-top:8px}
.temp{color:#EF4444}
.hum{color:#3B82F6}
#status{font-size:12px;color:#64748B;margin-top:20px}
</style>
</head>
<body>
<h1>⥠ESP32 Environment Monitor</h1>
<div class='card'>
<div class='val temp'><span id='temp'>--</span><span class='unit'>°C</span></div>
<div class='label'>đĄď¸ Temperature</div>
</div>
<div class='card'>
<div class='val hum'><span id='hum'>--</span><span class='unit'>%</span></div>
<div class='label'>đ§ Humidity</div>
</div>
<div id='status'>Fetching data...</div>
<script>
async function update() {
const r = await fetch('/data');
const d = await r.json();
document.getElementById('temp').textContent = d.temperature.toFixed(1);
document.getElementById('hum').textContent = d.humidity.toFixed(1);
document.getElementById('status').textContent = 'đ Last updated: ' + new Date().toLocaleTimeString();
}
update();
setInterval(update, 2000);
</script>
</body>
</html>)rawhtml";
}
6. Optional: Send Data to Firebase
To log data to the cloud for historical viewing, add the Firebase ESP32 Client library:
cpp
#include <Firebase_ESP_Client.h>
#include <addons/TokenHelper.h>
#define FIREBASE_HOST "https://your-project-default-rtdb.firebaseio.com/"
#define FIREBASE_AUTH "YOUR_DATABASE_SECRET"
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
void setupFirebase() {
config.host = FIREBASE_HOST;
config.signer.tokens.legacy_token = FIREBASE_AUTH;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
}
void sendToFirebase() {
if (Firebase.RTDB.setFloat(&fbdo, "/sensor/temperature", temperature) &&
Firebase.RTDB.setFloat(&fbdo, "/sensor/humidity", humidity)) {
Serial.println("Firebase: Data sent!");
} else {
Serial.println("Firebase error: " + fbdo.errorReason());
}
}
With Firebase logging, you can visualize historical data trends on a web dashboard connected to the same Firebase project you learned to build in the React + Firebase tutorial!
đ¤
Project Complete!
Explore more IoT and electronics projects: