Tap a button on your phone and watch an LED light up across the room. This External LED via Wi-Fi turns an ESP32 into a tiny web server that hosts a page with ON/OFF buttons. Open the page on any device on your Wi-Fi, tap a button, and a 5 mm LED wired to the board flips instantly.
You only need three parts for the prototype: an ESP32, a 5 mm LED, and a 100 Ω series resistor. The LED sits on GPIO 23 so it stays free from the default serial pins, and the resistor limits current for a long, safe demo. Drop everything into a small 3D-printed case if you want a clean, hand-held gadget.
Setup is straightforward: flash the sketch, change the ssid/password, and connect the LED (anode → resistor → GPIO23, cathode → GND). When the Serial Monitor prints the IP address, type it into your phone’s browser. The page loads with big buttons and a live status label so you always know whether the LED is on or off.
Once you’re comfortable with this concept, you can explore more advanced boards like the ESP32-CAM. Instead of just switching an LED, the ESP32-CAM lets you capture images, stream video, and store files on an SD card—perfect for smart cameras and visual IoT projects. If you want to try that next, check out the ESP32-CAM Image Capture and SD Storagetutorial on Circuitrocks Learn.
Why Build?
First, this gives you instant IoT feedback. You learn what it feels like to control real hardware from a browser—no special app, no pairing, just the local network. That fast loop makes it perfect for workshops and first-time demos.
Second, it’s a clean starting point for any Wi-Fi project. The same server pattern can switch relays, drive MOSFETs, or trigger animations. Swapping an LED for a bigger load later doesn’t change the interaction your users already understand.
Third, it teaches practical design choices. You’ll see why we use a redirect (303) after a click, why we keep the web page small for phones, and why a simple state variable keeps the UI and hardware in sync. Those decisions make projects feel reliable, not fragile.
What You’ll Learn
By building this, you’ll:
- ESP32 as a web server: Serving a minimal HTML/CSS page on port 80.
- Routes & handlers: Mapping
/,/on, and/offto functions that update hardware. - State management: Keeping a
bool ledOnso the page displays the correct status. - GPIO control: Setting GPIO23 as output and toggling HIGH/LOW safely through a 100 Ω resistor.
- Wi-Fi workflow: Joining a network, checking connection, and finding the board’s IP in Serial Monitor.
Sample Code
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
WebServer server(80);
// External 5mm LED pin (recommended)
const int LED_PIN = 23; // GPIO23
bool ledOn = false;
String page() {
String s = R"(
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP32 LED Control</title>
<style>
body{font-family:Arial;margin:20px}
.card{max-width:420px;margin:auto;padding:16px;border:1px solid #ddd;border-radius:12px}
.btn{padding:12px 16px;border:none;border-radius:10px;cursor:pointer;margin-right:8px}
.on{background:#1f9d55;color:#fff}
.off{background:#e53e3e;color:#fff}
.state{font-weight:bold}
</style>
</head>
<body>
<div class="card">
<h2>ESP32 - External LED (GPIO23)</h2>
<p>Status: <span class="state">)";
s += (ledOn ? "ON" : "OFF");
s += R"(</span></p>
<p>
<a href="/on"><button class="btn on">ON</button></a>
<a href="/off"><button class="btn off">OFF</button></a>
</p>
</div>
</body>
</html>
)";
return s;
}
void handleRoot() { server.send(200, "text/html", page()); }
void handleOn() {
ledOn = true;
digitalWrite(LED_PIN, HIGH);
server.sendHeader("Location", "/");
server.send(303);
}
void handleOff() {
ledOn = false;
digitalWrite(LED_PIN, LOW);
server.sendHeader("Location", "/");
server.send(303);
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(400);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.print("Open website: http://");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/on", handleOn);
server.on("/off", handleOff);
server.begin();
Serial.println("Web server started.");
}
void loop() {
server.handleClient();
}
How It Works
When the ESP32 boots, it joins your Wi-Fi with WiFi.begin(ssid, password) and waits until WL_CONNECTED. It then prints its local IP address so you know what to open in a browser. The sketch also configures GPIO23 as an output and starts a tiny HTTP server on port 80.
Three routes power the experience. The root path / serves a small HTML page with an ON and OFF button and a visible status label. The /on and /off routes flip the ledOn flag, write HIGH or LOW to GPIO23, and then send an HTTP 303 redirect back to / so the page refreshes with the updated state without you pressing back.
Inside loop(), server.handleClient() processes incoming requests. Because the page is static and the handlers are tiny, the response feels instant even on a phone. The result is a predictable interaction loop: connect → tap → LED reacts → page reflects the new state
Applications & Extensions
Start with the desktop LED in a small 3D-printed case—great for task reminders, meeting indicators, or a silent “ping” light for teammates. Since it’s just a web page, anyone on the same network can toggle it from a browser without installing anything.
Grow it into multi-output control. Add more pins and buttons for a lamp, fan, or relay board, or swap the LED for a MOSFET-driven LED strip. You can also add a slider UI for brightness or animation speed and map it to PWM on another pin.
Harden it for the real world. Add a simple login form, mDNS (http://esp32.local/) so you don’t memorize IPs, or move to AsyncWebServer for heavier pages. If you need control outside your LAN, pair it with a VPN, a reverse proxy, or a cloud dashboard—same hardware, same wiring, bigger reach.





