← All projects
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.

Difficulty
Beginner
Time
1 h
Category
IoT / Web

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

  1. Install Arduino IDE 2.
  2. Boards Manager → search esp32 by Espressif Systems → Install (version 3.x).
  3. 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

  1. WiFi.begin() connects to your router; the Serial Monitor (115200 baud) prints the board's IP address.
  2. WebServer listens on port 80. For /, page() builds HTML reflecting the current output states.
  3. 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

  1. Upload the sketch, open the Serial Monitor and press EN/RST on the board.
  2. Copy the IP (e.g. 192.168.1.57) and open it in a browser on the same network.
  3. 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 at http://esp32.local.
  • The server has no password — never port-forward it to the internet. For remote access use MQTT or Home Assistant instead.