ESP32 Web Server
for Remote Device Control
Host a smart home control panel on your ESP32. Control lights, fan, heater and door lock from any browser on your Wi-Fi network — no internet required.
What Are We Building?
The ESP32 runs a lightweight HTTP web server on port 80. When you open the ESP32's IP address in any browser on your local network, you get a fully responsive control panel. Toggle lights, slide the fan speed, lock or unlock the door — all from your phone or laptop, with no cloud, no app and no internet.
- Flash the code to your ESP32 and power it on.
- Open the Serial Monitor at 115200 baud — note the IP address printed.
- Open a browser on the same Wi-Fi network and type that IP.
- Login with
admin/1234
What You Need
| # | Component | Qty | Role |
|---|---|---|---|
| 1 | ESP32 DevKit V1 | ×1 | Wi-Fi microcontroller — hosts the web server |
| 2 | Yellow LED | ×1 | Living Room light indicator (Pin 26) |
| 3 | White LED | ×1 | Bedroom light indicator (Pin 27) |
| 4 | Blue LED | ×1 | Fan indicator (Pin 14) |
| 5 | Red LED | ×1 | Heater indicator (Pin 12) |
| 6 | Green LED | ×1 | Door Lock indicator (Pin 13) |
| 7 | 220Ω Resistors | ×5 | Current limiting for each LED |
| 8 | DHT22 Sensor | ×1 | Temperature & humidity readings (Pin 15) |
| 9 | SSD1306 OLED (128×64) | ×1 | Local status display via I2C |
| 10 | Piezo Buzzer | ×1 | Audio feedback on device toggle (Pin 25) |
| 11 | Breadboard & Jumper Wires | ×1 | Circuit assembly |
| 12 | Wi-Fi Router / Phone Hotspot | ×1 | Network for browser & ESP32 |
Circuit Connections
LED Bar — Living Room & Bedroom Lights
Connect each LED anode through a 220Ω resistor to its ESP32 pin. All cathodes go to GND.
💡 Living Room (Yellow)
💡 Bedroom (White)
Fan, Heater & Door Lock LEDs
Three more LEDs representing Fan (PWM), Heater and Door Lock. Same resistor-to-GND configuration.
🌀 Fan (Blue)
🔥 Heater (Red)
🔒 Door Lock (Green)
DHT22 Temperature & Humidity Sensor
SSD1306 OLED Display (I2C)
Piezo Buzzer
⚡ Power tip: Use the ESP32's 3.3V pin for DHT22 and OLED, not 5V. For real relay/motor control, use optocoupler-isolated relay modules and an external power supply.
diagram.json
Copy this file and paste it as diagram.json in your Wokwi project alongside the code to run the full simulation.
{
"version": 1,
"author": "ESP32 Web Server Control",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-esp32-devkit-v1", "id": "esp", "top": 0, "left": 0, "attrs": {} },
{ "type": "wokwi-led", "id": "led1", "top": -124.8, "left": 230.4, "attrs": { "color": "yellow", "label": "Living Room" } },
{ "type": "wokwi-resistor", "id": "r1", "top": -67.2, "left": 230.4, "rotate": 90, "attrs": { "value": "220" } },
{ "type": "wokwi-led", "id": "led2", "top": -124.8, "left": 307.2, "attrs": { "color": "white", "label": "Bedroom" } },
{ "type": "wokwi-resistor", "id": "r2", "top": -67.2, "left": 307.2, "rotate": 90, "attrs": { "value": "220" } },
{ "type": "wokwi-led", "id": "led3", "top": -124.8, "left": 384, "attrs": { "color": "blue", "label": "Fan" } },
{ "type": "wokwi-resistor", "id": "r3", "top": -67.2, "left": 384, "rotate": 90, "attrs": { "value": "220" } },
{ "type": "wokwi-led", "id": "led4", "top": -124.8, "left": 460.8, "attrs": { "color": "red", "label": "Heater" } },
{ "type": "wokwi-resistor", "id": "r4", "top": -67.2, "left": 460.8, "rotate": 90, "attrs": { "value": "220" } },
{ "type": "wokwi-led", "id": "led5", "top": -124.8, "left": 537.6, "attrs": { "color": "green", "label": "Door Lock" } },
{ "type": "wokwi-resistor", "id": "r5", "top": -67.2, "left": 537.6, "rotate": 90, "attrs": { "value": "220" } },
{ "type": "wokwi-dht22", "id": "dht1", "top": -86.4, "left": 105.6, "attrs": { "temperature": "25", "humidity": "55" } },
{ "type": "wokwi-ssd1306", "id": "oled1", "top": 141.1, "left": 289.9, "attrs": { "i2cAddress": "0x3C" } },
{ "type": "wokwi-buzzer", "id": "bz1", "top": 96, "left": 518.4, "attrs": { "volume": "0.3" } }
],
"connections": [
[ "esp:TX0", "$serialMonitor:RX", "", [] ],
[ "esp:RX0", "$serialMonitor:TX", "", [] ],
[ "led1:A", "esp:D26", "yellow", ["v0"] ],
[ "led1:C", "r1:1", "yellow", ["v0"] ],
[ "r1:2", "esp:GND.1", "black", ["v0"] ],
[ "led2:A", "esp:D27", "white", ["v0"] ],
[ "led2:C", "r2:1", "white", ["v0"] ],
[ "r2:2", "esp:GND.1", "black", ["v0"] ],
[ "led3:A", "esp:D14", "blue", ["v0"] ],
[ "led3:C", "r3:1", "blue", ["v0"] ],
[ "r3:2", "esp:GND.1", "black", ["v0"] ],
[ "led4:A", "esp:D12", "red", ["v0"] ],
[ "led4:C", "r4:1", "red", ["v0"] ],
[ "r4:2", "esp:GND.1", "black", ["v0"] ],
[ "led5:A", "esp:D13", "green", ["v0"] ],
[ "led5:C", "r5:1", "green", ["v0"] ],
[ "r5:2", "esp:GND.1", "black", ["v0"] ],
[ "dht1:VCC", "esp:3V3", "red", ["v0"] ],
[ "dht1:GND", "esp:GND.1", "black", ["v0"] ],
[ "dht1:SDA", "esp:D15", "green", ["v0"] ],
[ "oled1:VCC", "esp:3V3", "red", ["h0"] ],
[ "oled1:GND", "esp:GND.2", "black", ["h0"] ],
[ "oled1:SDA", "esp:D21", "blue", ["h0"] ],
[ "oled1:SCL", "esp:D22", "yellow", ["h0"] ],
[ "bz1:1", "esp:D25", "purple", ["v0"] ],
[ "bz1:2", "esp:GND.2", "black", ["v0"] ]
],
"dependencies": {}
}
What You See in the Browser
When you open the ESP32's IP in any browser, you get this control panel. Toggle switches and sliders update the hardware in real time.
🌐 This is a live demo of the control panel — buttons actually work in this preview!
Control via URL
Every device can be controlled by simply visiting a URL — great for automation scripts, Tasker, or any HTTP client.
{
"livingRoomLight": true,
"bedroomLight": false,
"fanSpeed": 75,
"temperature": 25.00,
"humidity": 55.00,
"uptime": 120
}
ESP32 Sketch
Install these libraries first via Arduino Library Manager: DHT sensor library, Adafruit GFX, Adafruit SSD1306. The WiFi.h and WebServer.h libraries come built-in with the ESP32 Arduino core.
/* * ESP32 Web Server for Remote Device Control * MakeMindz.com | makemindz.com * Features: 5 devices, DHT22 sensor, OLED display, * PWM fan control, HTTP Basic Auth, JSON API */ #include <WiFi.h> #include <WebServer.h> #include <DHT.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> // ── Wi-Fi Credentials ───────────────────────── const char* ssid = "Wokwi-GUEST"; // Change for real network const char* password = ""; // ── Web Server (port 80) ────────────────────── WebServer server(80); // ── Auth Credentials ────────────────────────── const char* webUsername = "admin"; const char* webPassword = "1234"; // ── Pin Definitions ─────────────────────────── #define LED1_PIN 26 // Living Room #define LED2_PIN 27 // Bedroom #define FAN_PIN 14 // Fan (PWM) #define HEATER_PIN 12 // Heater #define DOOR_LOCK_PIN 13 // Door Lock #define DHT_PIN 15 // DHT22 data #define BUZZER_PIN 25 // Buzzer #define DHT_TYPE DHT22 // ── Sensor & Display Objects ────────────────── DHT dht(DHT_PIN, DHT_TYPE); Adafruit_SSD1306 display(128, 64, &Wire, -1); // ── Device State Struct ─────────────────────── struct { bool livingRoomLight = false; bool bedroomLight = false; bool doorLock = true; // Locked by default bool heater = false; int fanSpeed = 0; // 0–100 % float temperature = 0; float humidity = 0; } devices; unsigned long lastSensor = 0; // ── Helpers ─────────────────────────────────── void applyStates() { digitalWrite(LED1_PIN, devices.livingRoomLight); digitalWrite(LED2_PIN, devices.bedroomLight); digitalWrite(HEATER_PIN, devices.heater); digitalWrite(DOOR_LOCK_PIN, devices.doorLock); ledcWrite(FAN_PIN, map(devices.fanSpeed, 0, 100, 0, 255)); } void readSensors() { devices.temperature = dht.readTemperature(); devices.humidity = dht.readHumidity(); if (isnan(devices.temperature)) devices.temperature = 0; if (isnan(devices.humidity)) devices.humidity = 0; } void updateOLED() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("SMART HOME"); display.drawLine(0, 9, 128, 9, SSD1306_WHITE); display.setCursor(0, 12); display.print("Temp: "); display.print(devices.temperature, 1); display.println("C"); display.setCursor(0, 22); display.print("Living: "); display.println(devices.livingRoomLight ? "ON" : "OFF"); display.setCursor(0, 32); display.print("Bedroom: "); display.println(devices.bedroomLight ? "ON" : "OFF"); display.setCursor(0, 42); display.print("Fan: "); display.print(devices.fanSpeed); display.println("%"); display.setCursor(0, 52); display.print("Door: "); display.println(devices.doorLock ? "LOCKED" : "OPEN"); display.display(); } bool checkAuth() { if (!server.authenticate(webUsername, webPassword)) { server.requestAuthentication(); return false; } return true; } void redirectHome() { server.sendHeader("Location", "/"); server.send(303); } // ── Route Handlers ──────────────────────────── void handleStatus() { String j = "{"; j += "\"livingRoomLight\":" + String(devices.livingRoomLight ? "true" : "false") + ","; j += "\"bedroomLight\":" + String(devices.bedroomLight ? "true" : "false") + ","; j += "\"fanSpeed\":" + String(devices.fanSpeed) + ","; j += "\"heater\":" + String(devices.heater ? "true" : "false") + ","; j += "\"doorLock\":" + String(devices.doorLock ? "true" : "false") + ","; j += "\"temperature\":" + String(devices.temperature, 2) + ","; j += "\"humidity\":" + String(devices.humidity, 2) + ","; j += "\"uptime\":" + String(millis() / 1000) + "}"; server.send(200, "application/json", j); } void setup() { Serial.begin(115200); pinMode(LED1_PIN, OUTPUT); pinMode(LED2_PIN, OUTPUT); pinMode(HEATER_PIN, OUTPUT); pinMode(DOOR_LOCK_PIN, OUTPUT); pinMode(BUZZER_PIN, OUTPUT); ledcAttach(FAN_PIN, 5000, 8); // ESP32 Arduino Core 3.x API dht.begin(); if (display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { display.clearDisplay(); display.display(); } applyStates(); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nIP: " + WiFi.localIP().toString()); server.on("/", []() { if(checkAuth()) server.send(200, "text/html", generateHTML()); }); server.on("/status", handleStatus); server.on("/living-room/on", []() { if(checkAuth()){ devices.livingRoomLight=true; applyStates(); redirectHome(); }}); server.on("/living-room/off",[]() { if(checkAuth()){ devices.livingRoomLight=false; applyStates(); redirectHome(); }}); server.on("/bedroom/on", []() { if(checkAuth()){ devices.bedroomLight=true; applyStates(); redirectHome(); }}); server.on("/bedroom/off", []() { if(checkAuth()){ devices.bedroomLight=false; applyStates(); redirectHome(); }}); server.on("/heater/on", []() { if(checkAuth()){ devices.heater=true; applyStates(); redirectHome(); }}); server.on("/heater/off", []() { if(checkAuth()){ devices.heater=false; applyStates(); redirectHome(); }}); server.on("/door/lock", []() { if(checkAuth()){ devices.doorLock=true; applyStates(); redirectHome(); }}); server.on("/door/unlock", []() { if(checkAuth()){ devices.doorLock=false; applyStates(); redirectHome(); }}); server.on("/all/on", []() { if(checkAuth()){ devices.livingRoomLight=devices.bedroomLight=true; applyStates(); redirectHome(); }}); server.on("/all/off", []() { if(checkAuth()){ devices.livingRoomLight=devices.bedroomLight=devices.heater=false; devices.fanSpeed=0; applyStates(); redirectHome(); }}); server.on("/fan/set", []() { if(!checkAuth()) return; if(server.hasArg("speed")) devices.fanSpeed = constrain(server.arg("speed").toInt(), 0, 100); applyStates(); redirectHome(); }); server.begin(); Serial.println("Server ready at http://" + WiFi.localIP().toString()); tone(BUZZER_PIN, 1000, 100); delay(150); tone(BUZZER_PIN, 1500, 100); } void loop() { server.handleClient(); if (millis() - lastSensor >= 2000) { lastSensor = millis(); readSensors(); updateOLED(); } }
DHT sensor library
Adafruit GFX Library
Adafruit SSD1306
WiFi.h (built-in)
WebServer.h (built-in)
Run in Wokwi Simulator
No hardware required. Open the Wokwi simulation, paste the diagram.json and code, then start the simulation. The Serial Monitor will show the IP address — click the link to open the web panel inside Wokwi's virtual browser.
Open Wokwi Simulation
The full circuit with ESP32, 5 LEDs, DHT22, OLED and buzzer is pre-configured. Start simulation and copy the IP from the Serial Monitor to open the dashboard.
- Start simulation — watch the OLED display show the startup message.
- Open the Serial Monitor — copy the IP address printed after Wi-Fi connects.
- Click the network link in the Wokwi panel to open the web control page.
- Toggle each device and watch the corresponding LED light up.
- Drag the fan slider to 75% and watch the blue LED brightness change.
- Challenge: Change the Wi-Fi SSID and password to use your real router.
- Advanced: Add a
/toggleroute that flips any device without visiting ON/OFF separately.
Quick Quiz
Click an option to check your answer.
Q1. Why does the ESP32 need to be on the same Wi-Fi network as your phone/laptop?
Q2. What does ledcAttach(FAN_PIN, 5000, 8) do?
Q3. What does visiting /status return?
Q4. Why does the handleRoot function call server.authenticate()?
Where Is This Used?
- Replace LEDs with relay modules to control real AC appliances
- Add mDNS (
esp32.local) so you never need to remember the IP - Add WebSocket support for real-time push updates without page refresh
- Add SPIFFS/LittleFS to serve HTML from the flash filesystem
- Connect to MQTT broker (Mosquitto) for cloud-free remote access
- Add a scheduling system using the DS3231 RTC module
Comments
Post a Comment