IoT / Web
Beginner
ESP32 Web Server: Control LEDs and Relays from Your Phone
A first IoT project: the ESP32 serves a page on your home Wi-Fi and you switch two outputs on and off right from the browser.
What we're building
The ESP32 joins your Wi-Fi network and runs a tiny web server. Open the board's IP address in a phone or laptop browser and you get two buttons that toggle GPIO 26 and GPIO 27. Swap the LEDs for a relay module and you can switch a lamp, fan or pump.
What you need
| Part | Qty |
|---|---|
| ESP32 DevKit board (any ESP32-WROOM based) | 1 |
| 5 mm LEDs | 2 |
| 220–330 Ω resistors | 2 |
| Breadboard and jumper wires | — |
| Optional: 1–2 channel relay module, 3.3 V logic compatible | 1 |
Wiring
- GPIO 26 → 330 Ω resistor → LED 1 anode (long leg) → cathode to GND
- GPIO 27 → 330 Ω resistor → LED 2 anode → cathode to GND
- Relay:
IN1→ GPIO 26,VCC→ 5V (VIN),GND→ GND.
⚠️ If the relay switches mains voltage, only work on the circuit while unplugged and keep it in a closed enclosure. Use LEDs for your first experiments.
Arduino IDE setup
- Install Arduino IDE 2.
- Boards Manager → search esp32 by Espressif Systems → Install (version 3.x).
- Select ESP32 Dev Module and the serial port your board is on.
Code
Fill in your WIFI_SSID and WIFI_PASS:
#include <WiFi.h>
#include <WebServer.h>
const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASS = "YOUR_PASSWORD";
const int PINS[] = {26, 27};
bool state[] = {false, false};
WebServer server(80);
String page() {
String html = F("<!doctype html><html><head><meta charset='utf-8'>"
"<meta name='viewport' content='width=device-width,initial-scale=1'>"
"<style>body{font-family:sans-serif;text-align:center;margin-top:40px}"
"a{display:inline-block;width:140px;padding:14px;margin:8px;border-radius:8px;"
"color:#fff;text-decoration:none;font-size:18px}"
".on{background:#1d7a46}.off{background:#666}</style></head>"
"<body><h1>ESP32 Web Server</h1>");
for (int i = 0; i < 2; i++) {
html += "<p>GPIO " + String(PINS[i]) + "</p>";
html += "<a class='" + String(state[i] ? "on" : "off") + "' href='/toggle?pin=" + String(i) + "'>";
html += state[i] ? "ON" : "OFF";
html += "</a>";
}
html += "</body></html>";
return html;
}
void handleToggle() {
int i = server.arg("pin").toInt();
if (i >= 0 && i < 2) {
state[i] = !state[i];
digitalWrite(PINS[i], state[i] ? HIGH : LOW);
}
server.sendHeader("Location", "/");
server.send(303);
}
void setup() {
Serial.begin(115200);
for (int p : PINS) {
pinMode(p, OUTPUT);
digitalWrite(p, LOW);
}
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print('.');
}
Serial.printf("\nIP address: %s\n", WiFi.localIP().toString().c_str());
server.on("/", [] { server.send(200, "text/html", page()); });
server.on("/toggle", handleToggle);
server.begin();
}
void loop() {
server.handleClient();
}
How it works
WiFi.begin()connects to your router; the Serial Monitor (115200 baud) prints the board's IP address.WebServerlistens on port 80. For/,page()builds HTML reflecting the current output states.- Each button is a link to
/toggle?pin=0. The handler flips the pin and redirects back to the home page (303 See Other), so the page always shows fresh state.
Testing
- Upload the sketch, open the Serial Monitor and press
EN/RSTon the board. - Copy the IP (e.g.
192.168.1.57) and open it in a browser on the same network. - Tap the buttons — the LEDs should turn on and off.
Next steps
- Reserve the board's IP in your router (DHCP reservation) so it never changes.
- Add mDNS (
ESPmDNS.h) and reach the board athttp://esp32.local. - The server has no password — never port-forward it to the internet. For remote access use MQTT or Home Assistant instead.