The ESP32 is a powerful microcontroller with built-in Wi-Fi, making it ideal for real-world IoT monitoring systems. In this guide, we expand on the basic web server concept by adding a temperature and humidity sensor, then displaying live environmental data directly in a browser.
You’ll also learn how to trigger an external LED when certain environmental conditions are met, forming the foundation of climate alerts, greenhouse automation, and smart monitoring systems. If you're looking for a guide that covers only blinking a LED, check our our ESP32 LED Blink tutorial.
An ESP32 that:
- Connects to your Wi-Fi network (Station Mode)
- Reads live temperature and humidity data from a DHT sensor
- Displays that data on a browser-based dashboard
- Automatically turns on an LED when temperature exceeds a defined threshold
How This Project Works
This project builds directly on the ESP32 web server foundation that we covered in our previous guide.
Instead of just displaying static text, the ESP32 will:
- Read temperature and humidity from a DHT sensor
- Evaluate environmental conditions using simple logic
- Control an external LED based on a threshold
- Generate a dynamic HTML page showing live data
Communication Flow:
Computer → Router → ESP32 → Sensor → ESP32 → Browser

This is how real IoT systems operate. Sensors collect data, microcontrollers process it, and web interfaces provide human-readable monitoring dashboards.
Understanding the DHT Temperature & Humidity Sensor
To measure environmental data, we will use a digital temperature and humidity sensor from the DHT family. We'll be using the DHT22, but the DHT11 can also work by changing the defined sensor type in the code.
Inside the module are two separate sensing elements. A thermistor is used to measure temperature, and a capacitive humidity sensor is used to measure moisture in the air. The humidity sensor works by detecting changes in capacitance as water vapour in the air alters the electrical properties of a tiny polymer layer. The temperature reading is measured electronically and converted into a digital signal by a small internal microcontroller. This means the ESP32 does not measure voltage directly. Instead, it receives already processed digital data over a single data pin.
One important limitation to be aware of is the 1sampling rate. The DHT sensors update relatively slowly and should only be read about once every 2 seconds. Reading it too quickly can cause invalid or repeated values.
Hardware Required
- ESP32 development board
-
DHT11 or DHT22 sensor
- 5mm LED (any colour)
- 220Ω resistor
- Breadboard
- Jumper wires
- USB cable
- Wi-Fi network
Get All the Parts You Need
This tutorial is part of our comprehensive ESP32 learning series. Instead of buying components individually, save time and money with our ESP32 Basic Starter Kit. It includes everything you need for this lesson and 20+ other projects.
What's Included: ESP32 board, OLED display, sensors (DHT11, PIR, LDR), relay module, buzzers, LEDs, buttons, breadboard, resistors, and all cables, plus access to our complete lesson plans.
View ESP32 Starter Kit →Wiring the Circuit
The DHT sensor uses a single digital data line to communicate with the ESP32.
DHT Sensor Connections
- VCC → 3.3V
- GND → GND
-
DATA → GPIO 5
LED Connections
- Anode (long leg) → GPIO 4 through 220Ω resistor
- Cathode → GND

Installing Required Libraries
Before uploading the sketch, install the required libraries in the Arduino IDE.
- Within the Arduino IDE, click Sketch
- Click Include Library
- Click Manage Libraries
- In the Library Manager search box, search DHT sensor library
- Under DHT sensor library by Adafruit click Install

Create & Upload the Complete Sketch
This sketch connects to Wi-Fi, reads the DHT sensor, controls an LED when temperature exceeds a defined threshold, and serves a live dashboard page.
#include <WiFi.h> #include <WebServer.h> #include "DHT.h" // ===== WiFi Credentials ===== const char* ssid = "YOUR_WIFI_NAME"; // Replace these const char* password = "YOUR_WIFI_PASSWORD"; // Replace these // ===== DHT Setup ===== #define DHTPIN 5 #define DHTTYPE DHT22 // Change to DHT11 if using DHT11 DHT dht(DHTPIN, DHTTYPE); // ===== LED Setup ===== #define LED_PIN 4 float temperatureThreshold = 30.0; // Celsius WebServer server(80); void handleRoot() { float humidity = dht.readHumidity(); float temperature = dht.readTemperature(); if (temperature > temperatureThreshold) { digitalWrite(LED_PIN, HIGH); } else { digitalWrite(LED_PIN, LOW); } String html = "<!DOCTYPE html><html>"; html += "<head><meta http-equiv='refresh' content='5'/>"; html += "<title>ESP32 Climate Monitor</title></head>"; html += "<body style='font-family: Arial; text-align:center;'>"; html += "<h1>ESP32 Temperature & Humidity Monitor</h1>"; html += "<p>Temperature: " + String(temperature) + " °C</p>"; html += "<p>Humidity: " + String(humidity) + " %</p>"; html += "</body></html>"; server.send(200, "text/html", html); } void setup() { Serial.begin(115200); dht.begin(); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); WiFi.begin(ssid, password); Serial.print("Connecting to WiFi"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("Connected!"); Serial.print("IP Address: "); Serial.println(WiFi.localIP()); server.on("/", handleRoot); server.begin(); } void loop() { server.handleClient(); }
Testing the System
- Open Serial Monitor.
- Confirm Wi-Fi connection.
- Copy the printed IP address.
- Open a browser on the same network.
- Enter the IP address.


You should now see live temperature and humidity readings updating automatically.
Below Threshold

Above Threshold

Understanding the Code
WiFi Credentials
Your network name and password are stored at the top of the sketch:
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
These values allow the ESP32 to connect to your local wireless network. Replace them with your actual router credentials before uploading the sketch.
Including Required Libraries
#include <WiFi.h>
#include <WebServer.h>
#include "DHT.h"
Three libraries power this project:
- WiFi.h handles wireless connectivity.
- WebServer.h creates a lightweight HTTP server on the ESP32.
- DHT.h allows communication with the DHT temperature and humidity sensor.
DHT22 Sensor Configuration
#define DHTPIN 5
#define DHTTYPE DHT22
This section defines which GPIO pin the sensor is connected to and specifies the sensor model. If you are using a DHT11 instead of a DHT22, simply change the sensor type definition.
The line below creates the sensor object used throughout the program:
DHT dht(DHTPIN, DHTTYPE);
LED Setup and Threshold
#define LED_PIN 4
float temperatureThreshold = 30.0;
The temperatureThreshold variable defines the trigger point in degrees Celsius. When the measured temperature exceeds 30°C, the LED turns on. Otherwise, it remains off.
Using a variable instead of a hardcoded value makes it easy to adjust the trigger point later.
Creating the Web Server
WebServer server(80);
This initializes a web server that listens on port 80, which is the standard HTTP port used by web browsers.
Reading Sensor Data
Inside the handleRoot() function, the ESP32 reads live data from the sensor:
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
Each time a browser loads the page, these two lines request fresh temperature and humidity values from the DHT sensor. The readings are stored in variables so they can be displayed and used for decision making.
Temperature-Based LED Logic
if (temperature > temperatureThreshold) {
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
This conditional statement checks if the temperature exceeds the defined threshold. If it does, the LED turns ON. If not, it remains OFF.
You can modify this conditional statement to trigger when humidity exceeds a certain threshold. Replace temperature with humidity in the comparison, and change temperatureThreshold to a new variable such as humidityThreshold to reflect the new trigger condition.
Building the Dynamic HTML Page
Instead of storing a static webpage, the ESP32 builds the entire HTML document dynamically using string concatenation.
String html = "";
html += "<!DOCTYPE html><html>";
Each line adds more content to the page before it is sent to the browser.
Meta Tags and Auto Refresh
html += "<head>";=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
html += "<meta charset-\"UTF-8\">";
html += "<meta namehtml += "<meta http-equiv=\"refresh\" content=\"5\">";
These lines include UTF-8 character encoding for proper symbol display (°C), viewport settings for mobile responsiveness and forces the browser to refresh every 5 seconds. The refresh triggers a new sensor reading, keeping the displayed values up to date automatically.
Modern CSS Styling
Your updated version includes embedded CSS to create a clean card-style layout:
body{ margin:0; font-family:Arial,sans-serif; background:linear-gradient(135deg,#eef2f3,#dfe9f3); text-align:center; }
This styling:
- Adds a soft gradient background
- Centers all content
- Uses a responsive card layout with rounded corners and shadow
The result is a professional-looking interface rather than plain text output.
Dynamic Temperature Colour
One of the most important upgrades in your new code is dynamic colour logic:
html += ".temp{color:" + String(temperature > temperatureThreshold ? "#e53935" : "#ff7043") + ";}";
This uses a conditional (ternary) operator inside the HTML builder.
- If the temperature exceeds the threshold → the value appears red.
- If not → it appears orange.
This visual feedback mirrors the LED behaviour, giving both physical and on-screen alerts.
SVG Icons for Visual Feedback
Your updated layout includes inline SVG icons for both temperature and humidity:
<svg class='icon temp' viewBox='0 0 24 24' fill='currentColor'> <path d='M14 14.76V5a2 2 0 10-4 0v9.76A4 4 0 1014 14.76zM12 20a2 2 0 110-4 2 2 0 010 4z'/> </svg>
Instead of using image files, SVG graphics are embedded directly into the HTML. This has several advantages:
- No external image loading required
- Icons scale cleanly on all screen sizes
- Colours can be controlled using CSS
The fill="currentColor" property allows the icon colour to automatically match the CSS text colour. That means when the temperature turns red, the thermometer icon turns red as well.
Sending the Response
server.send(200, "text/html", charset=UTF-8", html);
This line sends an HTTP response back to the browser:
- 200 means the request was successful.
- text/html tells the browser the content is a web page.
- charset=UTF-8 ensures special characters like ° render correctly.
- html contains the dynamically generated page.
Setup Function
The setup() function runs once when the ESP32 powers on.
Serial.begin(115200);
dht.begin();
Serial communication is started for debugging, and the DHT sensor is initialized.
Configuring the LED
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
The LED pin is configured as an output and set to OFF at startup.
Connecting to WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
This starts the connection process using your router credentials.
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
This loop keeps checking the connection status until the ESP32 successfully joins the network. While waiting, it prints dots to the Serial Monitor.
Serial.println("Connected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Once connected, the assigned local IP address is displayed. You enter this IP address into your browser to view the climate monitor page.
Starting the Server
server.on("/", handleRoot);
server.begin();
This tells the ESP32 what to do when someone visits the root URL.
-
"/"represents the homepage. -
handleRootis the function that builds and sends the webpage.
server.begin() starts the web server so it can begin listening for incoming HTTP requests.
Main Loop
void loop() {
server.handleClient();
}
The loop continuously checks for incoming browser requests. When a request is detected, it runs handleRoot(), reads the sensor, applies the LED logic, generates the HTML page, and sends it back to the client.
What Happens Behind the Scenes
- The ESP32 powers on.
- The DHT22 sensor and LED are initialized.
- The ESP32 connects to your WiFi network.
- The web server starts listening on port 80.
- A browser sends an HTTP request to the ESP32 IP address.
- The sensor is read in real time.
- The temperature is compared against the threshold.
- The LED is updated accordingly.
- A fully styled HTML page is generated dynamically.
- The page is sent back to the browser.
- The browser refreshes every 5 seconds and repeats the cycle.
Even though this system runs on a small embedded device, it follows the same architecture as professional web applications: server-side processing and client-side rendering.
This project combines environmental sensing, conditional logic, wireless networking, and live browser monitoring into a single embedded system. Mastering this structure forms the foundation of most ESP32 IoT applications.
Expanding the Project
This simple LED can be replaced with more advanced outputs:
- Relay module to control fans or heaters
- Buzzer for audible alerts
- Transistor-driven high-power loads
- Data logging to SD card
- Cloud-based logging platforms
You can also improve the dashboard interface by:
- Adding colour indicators (red when above threshold)
- Displaying LED status
- Adding buttons for manual override
- Styling the page with CSS
Adding Simple Data Logging (Serial Monitor)
To log readings for debugging or testing, add this inside handleRoot():
Serial.print("Temp: ");
Serial.print(temperature);
Serial.print(" C | Humidity: ");
Serial.print(humidity);
Serial.println(" %");
Now every browser refresh also logs the values in the Serial Monitor.
Common Issues & Troubleshooting
1. ESP32 Not Connecting to Wi-Fi
- Double-check SSID and password.
- Ensure 2.4GHz network is enabled (ESP32 does not support 5GHz).
- Make sure firewall settings aren’t blocking local devices.
2. Sensor Returning “NaN”
- Check wiring.
- Confirm correct sensor type (DHT11 vs DHT22).
- Ensure 10kΩ pull-up resistor is installed.
3. Web Page Not Loading
- Confirm devices are on the same Wi-Fi network.
- Re-check the printed IP address.
- Disable mobile data if accessing from a phone.
Project Summary
In this guide, you built an ESP32-based environmental monitoring system that:
- Connects to Wi-Fi
- Hosts a local web server
- Reads temperature and humidity
- Displays live data in a browser
- Uses conditional logic to control an LED
You now understand:
- How embedded web servers operate
- How sensors integrate with networked systems
- How to apply threshold-based automation
- How AND/OR logic affects control behaviour
From here, you can expand into:
- Cloud-connected monitoring
- Remote alerts
- Home automation systems
- Industrial environment tracking
You’ve now moved beyond basic web serving and into true environmental IoT system design.
ESP32 IoT Systems Pathway
- Step 0 (prerequisite): Installing & Configuring Arduino IDE for ESP32
- Step 1: Connecting to Wi-Fi & Hosting a Simple Web Page
- Step 2: Building a Real-Time Sensor Dashboard with a DHT11/DHT22