🌐🔧 Alin Li · ESP32 Web Server

Build a Web Server
on a Tiny Chip!

Turn a $5 ESP32 microcontroller into a web server that you control from your phone's browser. Toggle LEDs, read sensors, and build a smart home dashboard — all running on a chip smaller than your thumb. Step by step, line by line, fully commented code.

What You Need 🛒Start Building ⚡
🤔 MIDA ME EHITAME?

Veebiserver $5 kiibil

🌐 Your ESP32 Becomes a Website!

A web server is a program that listens for requests from web browsers and sends back web pages. When you visit google.com, a Google server sends you a web page. In this project, YOUR ESP32 will do the same thing — but on your local Wi-Fi network.

You'll open your phone's browser, type the ESP32's IP address (like 192.168.1.42) ja näe veebilehte, mida SERVEERIB sinu tilluke kiip. Lehel on nupud LED-ide juhtimiseks, andurite näitude kuvamiseks ja muuks — kõik reaalajas!

🧠 Põhimõiste: kuidas veebiserver töötab

1. Your browser sends a request → "Hey server, give me the page at /"

2. The server processes the request → "They want the homepage, let me build it"

3. The server sends a response → an HTML page with buttons and data

4. Your browser renders the HTML → you see a beautiful page with controls!

This is the same process that powers EVERY website on Earth — and your ESP32 will do it.

🛒 MIDA VAJAD

Ostunimekiri — kokku ~$15–$25

🔧

ESP32 Dev Board

ESP32-WROOM-32 or ESP32-S3

~$5–$10
🔌

USB Cable

Micro-USB või USB-C (oleneb plaadist)

~$2
🔴

LEDs (3–5 pcs)

Any color — red, green, blue

~$1
⚡

Resistors (220Ω)

Üks iga LED-i kohta nende kaitsmiseks

~$1
🌡️

DHT11 Sensor (optional)

Temperature + humidity sensor

~$2
🧵

Breadboard + Jumper Wires

For connecting everything

~$3
💡 Minimum Setup

For Project 1, you ONLY need the ESP32 board + USB cable — the ESP32 has a built-in LED! You can start coding immediately without buying any extra components. Add LEDs and sensors for Projects 2 and 3.

💻 SEADISTAMINE

Samm 0: seadista Arduino IDE ESP32 jaoks

0

Install Arduino IDE + ESP32 Board Support

⏱️ 10 minutes (one-time setup)

1. Download Arduino IDE from arduino.cc/en/software (tasuta, avatud lähtekoodiga)

2. Ava Arduino IDE → mine File → Preferences

3. In "Additional Board Manager URLs", paste this URL:

Board Manager URL
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

4. Go to Tools → Board → Boards Manager → Search "ESP32" → Install "esp32 by Espressif Systems"

5. Connect your ESP32 via USB → Go to Tools → Board → Select "ESP32 Dev Module"

6. Go to Tools → Port → Select the COM port (e.g., COM3 on Windows, /dev/ttyUSB0 on Linux)

🐧 Linux Users

You may need to add yourself to the dialout group: sudo usermod -a -G dialout $USER then log out and back in. This gives permission to access the USB serial port.

🟢 PROJEKT 1

Juhi sisseehitatud LEDi oma brauserist

Your first web server — no extra hardware needed!

📐 Mida see projekt teeb

The ESP32 connects to your Wi-Fi, starts a web server, and serves a page with Sisse/välja nupud. Nupule klõpsates saadab brauser ESP32-le päringu, mis lülitab sisseehitatud LED-i sisse või välja. Näed tulemust kohe — telefonist või mis tahes samas Wi-Fi-võrgus olevast seadmest!

project1_led_webserver.ino — Full Code with Every Line Explained
// ============================================ // ALIN LI — ESP32 veebiserver: LED-i juhtimine // Projekt 1: sisseehitatud LED, lisariistvara pole vaja! // ============================================ // SAMM 1: lisa Wi-Fi teek // See teek võimaldab ESP32-l Wi-Fi-võrkudega ühenduda // See on ESP32 plaadipaketiga eelpaigaldatud #include <WiFi.h> // SAMM 2: määra Wi-Fi sisselogimisandmed // Asenda need OMA koduse Wi-Fi nime ja parooliga! const char* ssid = "YOUR_WIFI_NAME"; // Sinu Wi-Fi-võrgu nimi const char* password = "YOUR_WIFI_PASSWORD"; // Sinu Wi-Fi parool // SAMM 3: loo veebiserver pordil 80 // Port 80 on HTTP vaikeport — brauserid kasutavad seda automaatselt // IP-aadressi sisestamisel ühendub brauser pordiga 80 WiFiServer server(80); // SAMM 4: määra LED-i viik // Enamikul ESP32 plaatidel on sisseehitatud LED viigul 2 // (Kontrolli oma plaati — mõned kasutavad viiku 5 või 13) const int LED_PIN = 2; // SAMM 5: muutuja LED-i oleku jälgimiseks // Peame meeles pidama, kas LED on SEES või VÄLJAS // et saaksime veebilehel õiget olekut näidata bool ledState = false; // ============================================ // SETUP — töötab ÜKS KORD ESP32 käivitumisel // ============================================ void setup() { // Alusta jadasidet kiirusel 115200 boodi // See võimaldab näha sõnumeid Arduino jadamonitoris // (Tööriistad → Jadamonitor → määra kiiruseks 115200 boodi) Serial.begin(115200); // Määra LED-i viik režiimi OUTPUT // OUTPUT tähendab signaali SAATMIST (LED-i sisse/välja lülitamist) // (INPUT tähendaks signaali LUGEMIST, näiteks andurist) pinMode(LED_PIN, OUTPUT); // Veendu, et LED on alguses VÄLJAS digitalWrite(LED_PIN, LOW); // Ühendu Wi-Fi-ga Serial.print("Connecting to Wi-Fi: "); Serial.println(ssid); // WiFi.begin() alustab ühendamist // See saadab sisselogimisandmed ruuterile WiFi.begin(ssid, password); // Oota ühendust — while-tsükkel kontrollib pidevalt // WiFi.status() tagastab õnnestumisel WL_CONNECTED while (WiFi.status() != WL_CONNECTED) { delay(500); // Oota pool sekundit Serial.print("."); // Väljasta punkt, et näeksime ühendamiskatset } // Ühendatud! Väljasta IP-aadress // Selle aadressi sisestad brauserisse Serial.println(""); Serial.println("✅ Connected to Wi-Fi!"); Serial.print("📡 IP Address: "); Serial.println(WiFi.localIP()); // ^^^ KIRJUTA SEE IP-AADRESS ÜLES! Sul läheb seda vaja! // Käivita veebiserver // Nüüd OOTAB ESP32 brauseriühendusi server.begin(); Serial.println("🌐 Web server started!"); } // ============================================ // LOOP — töötab IGAVESTI, ikka uuesti // ============================================ void loop() { // Kontrolli, kas klient (brauser) on ühendunud // server.available() tagastab kliendiobjekti, kui keegi ühendus WiFiClient client = server.available(); // Kui klienti pole, jäta vahele ja kontrolli uuesti if (!client) { return; } // Klient ühendus! Oota, kuni ta andmeid saadab Serial.println("👤 New client connected!"); while (!client.available()) { delay(1); } // Loe HTTP-päringu esimene rida // See näeb välja nii: „GET /on HTTP/1.1“ või „GET /off HTTP/1.1“ String request = client.readStringUntil('\r'); Serial.println(request); // ============================================ // TÖÖTLE PÄRINGUT — mida kasutaja klõpsas? // ============================================ // Kui URL sisaldab „/on“ → lülita LED SISSE if (request.indexOf("/on") != -1) { digitalWrite(LED_PIN, HIGH); // HIGH = 3.3V = LED SEES ledState = true; Serial.println("💡 LED turned ON"); } // Kui URL sisaldab „/off“ → lülita LED VÄLJA if (request.indexOf("/off") != -1) { digitalWrite(LED_PIN, LOW); // LOW = 0V = LED VÄLJAS ledState = false; Serial.println("🌑 LED turned OFF"); } // ============================================ // LOO VEEBILEHT (HTML) // Seda brauser kuvabki! // ============================================ // Saada HTTP-vastuse päised // „HTTP/1.1 200 OK“ tähendab „päring õnnestus“ // „Content-Type: text/html“ tähendab „saadan veebilehte“ client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(""); // Tühi rida = päiste lõpp // Saada nüüd HTML-leht client.println("<!DOCTYPE html><html>"); client.println("<head>"); client.println("<title>Alin Li ESP32 Controller</title>"); client.println("<meta name='viewport' content='width=device-width'>"); // CSS-kujundus — et leht näeks telefonis kena välja! client.println("<style>"); client.println("body{font-family:Arial;text-align:center;background:#1a1a2e;color:white;padding:40px 20px;}"); client.println("h1{font-size:2rem;margin-bottom:10px;}"); client.println(".status{font-size:3rem;margin:20px 0;}"); client.println(".btn{display:inline-block;padding:20px 50px;margin:10px;"); client.println("border-radius:50px;font-size:1.2rem;font-weight:bold;"); client.println("text-decoration:none;color:white;}"); client.println(".on{background:#16a34a;}.off{background:#dc2626;}"); client.println("</style></head><body>"); // Lehe sisu client.println("<h1>🔧 Alin Li ESP32 Controller</h1>"); client.println("<p>Control the LED from your browser!</p>"); // Näita LED-i olekut emotikoniga if (ledState) { client.println("<div class='status'>💡 LED is ON</div>"); } else { client.println("<div class='status'>🌑 LED is OFF</div>"); } // Nupud linkidega /on ja /off // Klõpsates avab brauser selle URL-i // mis käivitab meie ülaltoodud if-laused! client.println("<a href='/on' class='btn on'>Turn ON</a>"); client.println("<a href='/off' class='btn off'>Turn OFF</a>"); client.println("</body></html>"); // Valmis! Flush saadab kõik andmed ja sulgeb ühenduse client.flush(); Serial.println("📤 Page sent to client!"); }
▶️

Upload & Test!

⏱️ 2 minutes

1. Change "YOUR_WIFI_NAME" ja "YOUR_WIFI_PASSWORD" to your real Wi-Fi

2. Click the Upload button (nool →) Arduino IDE-s

3. Ava Tools → Serial Monitor (määra kiiruseks 115200 boodi)

4. Wait for the message: "📡 IP Address: 192.168.1.XX"

5. Ava telefonis brauser ja sisesta see IP-aadress!

6. 🎉 You see your web page! Click the buttons — the LED responds!

🎉 Congratulations!

You just built a web server on a $5 chip! Your phone is talking to a microcontroller through Wi-Fi and HTTP — the same protocol that powers the entire World Wide Web. This is real IoT (Internet of Things) engineering!

🎯 Activity 1: Customize Your Page!

Nüüd, kui see töötab, proovi neid muudatusi:

  1. Change the page title from "Alin Li ESP32 Controller" to YOUR name
  2. Change the background color — try #0f172a (tumesinine) või #064e3b (tumeroheline)
  3. Lisa kolmas nupp, mis paneb LED-i 5 korda vilkuma (vihje: kasuta for loop with delay())
  4. Lisa lehe lõppu oma nimi ja tänane kuupäev
🔴🟢🔵 PROJEKT 2

Juhi 3 välist LEDi

Control red, green, and blue LEDs independently!

🔌 Ühendusskeem
    ESP32              LEDs
    ┌──────────┐
    │          │
    │  GPIO 26 ├─────[220Ω]────🔴 Red LED──┐
    │          │                              │
    │  GPIO 27 ├─────[220Ω]────🟢 Green LED─┤
    │          │                              │
    │  GPIO 14 ├─────[220Ω]────🔵 Blue LED──┤
    │          │                              │
    │  GND    ├──────────────────────────────┘
    │          │          (all LED negatives
    └──────────┘           share ground)
    
⚡ Why the 220Ω Resistor?

LEDs are hungry — they'll try to draw too much current and burn out. The resistor limits current to a safe level (~15mA). Kasuta iga LED-iga alati takistit! Without it, you'll destroy the LED and possibly damage the ESP32 pin.

project2_three_leds.ino — Control 3 LEDs from browser
#include <WiFi.h> const char* ssid = "YOUR_WIFI_NAME"; const char* password = "YOUR_WIFI_PASSWORD"; WiFiServer server(80); // Määra iga LED-i viigud const int RED_PIN = 26; const int GREEN_PIN = 27; const int BLUE_PIN = 14; // Jälgi iga LED-i olekut bool redOn = false, greenOn = false, blueOn = false; void setup() { Serial.begin(115200); pinMode(RED_PIN, OUTPUT); pinMode(GREEN_PIN, OUTPUT); pinMode(BLUE_PIN, OUTPUT); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\n✅ IP: " + WiFi.localIP().toString()); server.begin(); } void loop() { WiFiClient client = server.available(); if (!client) return; while (!client.available()) { delay(1); } String req = client.readStringUntil('\r'); // Lülita iga LED-i vastavalt URL-ile if (req.indexOf("/red") != -1) { redOn = !redOn; digitalWrite(RED_PIN, redOn); } if (req.indexOf("/green") != -1) { greenOn = !greenOn; digitalWrite(GREEN_PIN, greenOn); } if (req.indexOf("/blue") != -1) { blueOn = !blueOn; digitalWrite(BLUE_PIN, blueOn); } // Loo kena veebileht 3 lülitusnupuga client.println("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n"); client.println("<!DOCTYPE html><html><head>"); client.println("<meta name='viewport' content='width=device-width'>"); client.println("<style>"); client.println("body{font-family:Arial;text-align:center;background:#0f172a;color:#e2e8f0;padding:40px 20px;}"); client.println("h1{font-size:1.8rem;} .btns{display:flex;flex-direction:column;gap:14px;max-width:300px;margin:20px auto;}"); client.println(".b{padding:18px;border-radius:14px;font-size:1.1rem;font-weight:bold;text-decoration:none;color:white;display:block;}"); client.println("</style></head><body>"); client.println("<h1>🔧 Alin Li LED Controller</h1>"); client.println("<div class='btns'>"); // Iga nupp näitab olekut ja lingib lülitamise URL-ile client.print("<a class='b' style='background:"); client.print(redOn ? "#dc2626" : "#333"); client.println("' href='/red'>🔴 Red: " + String(redOn ? "ON" : "OFF") + "</a>"); client.print("<a class='b' style='background:"); client.print(greenOn ? "#16a34a" : "#333"); client.println("' href='/green'>🟢 Green: " + String(greenOn ? "ON" : "OFF") + "</a>"); client.print("<a class='b' style='background:"); client.print(blueOn ? "#2563eb" : "#333"); client.println("' href='/blue'>🔵 Blue: " + String(blueOn ? "ON" : "OFF") + "</a>"); client.println("</div></body></html>"); client.flush(); }

🎯 Activity 2: Expand It!

  1. Lisa neljas LED viigule GPIO 25 — lisa nupp ja loogika ise!
  2. Lisa nupp „KÕIK SISSE”, mis lülitab kõik 3 LED-i korraga sisse (vihje: uus URL /all)
  3. Lisa nupp „KÕIK VÄLJA”
  4. Lisa nupp „DISCO”, mis paneb kõik LED-id 10 korda juhusliku mustriga vilkuma
  5. Challenge: Lisa heleduse liugur PWM-i abil (analogWrite() või ledcWrite() on ESP32)
🌡️ PROJECT 3

Temperatuuri ja niiskuse töölaud

Reaalajas andurite juhtpaneel automaatse värskendusega!

🔌 DHT11 anduri ühendamine
    ESP32           DHT11
    ┌──────────┐    ┌─────┐
    │          │    │     │
    │  3.3V   ├────┤ VCC │  (Power)
    │          │    │     │
    │  GPIO 4 ├────┤ DATA│  (Signal)
    │          │    │     │
    │  GND    ├────┤ GND │  (Ground)
    │          │    │     │
    └──────────┘    └─────┘

    Note: Add a 10kΩ pull-up resistor between DATA and VCC
    (some DHT11 modules have it built-in)
    
📚 Paigalda esmalt DHT teek!

In Arduino IDE: Sketch → Include Library → Manage Libraries → Search "DHT sensor library" by Adafruit → Install. Also install "Adafruit Unified Sensor" when prompted.

project3_sensor_dashboard.ino — Live Temperature Dashboard
#include <WiFi.h> #include <DHT.h> // DHT-anduri teek const char* ssid = "YOUR_WIFI_NAME"; const char* password = "YOUR_WIFI_PASSWORD"; WiFiServer server(80); // DHT11-andur GPIO 4 peal #define DHT_PIN 4 #define DHT_TYPE DHT11 DHT dht(DHT_PIN, DHT_TYPE); const int LED_PIN = 2; bool ledOn = false; void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); dht.begin(); // Lähtesta DHT-andur WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\n📡 IP: " + WiFi.localIP().toString()); server.begin(); } void loop() { WiFiClient client = server.available(); if (!client) return; while (!client.available()) { delay(1); } String req = client.readStringUntil('\r'); if (req.indexOf("/toggle") != -1) { ledOn = !ledOn; digitalWrite(LED_PIN, ledOn); } // Loe andurite väärtused float temp = dht.readTemperature(); // Celsius float hum = dht.readHumidity(); // Protsent // Kontrolli, kas lugemine ebaõnnestus String tempStr = isnan(temp) ? "Error" : String(temp, 1) + "°C"; String humStr = isnan(hum) ? "Error" : String(hum, 1) + "%"; // Loo kena juhtpaneeli leht client.println("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n"); client.println("<!DOCTYPE html><html><head>"); client.println("<meta name='viewport' content='width=device-width'>"); // AUTOMAATNE VÄRSKENDUS iga 5 sekundi järel! // See laadib lehe uuesti, et näidata värskeid anduriandmeid client.println("<meta http-equiv='refresh' content='5'>"); client.println("<style>"); client.println("body{font-family:Arial;text-align:center;background:#0f172a;color:#e2e8f0;padding:30px 20px;}"); client.println("h1{font-size:1.5rem;color:#60a5fa;}"); client.println(".cards{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin:20px 0;}"); client.println(".card{background:#1e293b;border-radius:16px;padding:24px;min-width:140px;}"); client.println(".card .icon{font-size:2.5rem;}"); client.println(".card .val{font-size:2rem;font-weight:bold;margin:8px 0;}"); client.println(".card .label{font-size:.8rem;color:#94a3b8;}"); client.println(".btn{display:inline-block;padding:16px 40px;border-radius:50px;margin-top:16px;"); client.println("font-size:1rem;font-weight:bold;text-decoration:none;color:white;}"); client.println("</style></head><body>"); client.println("<h1>🌐 Alin Li Sensor Dashboard</h1>"); client.println("<p style='color:#94a3b8;font-size:calc(.85rem * var(--alinli-text-scale,1));'>Auto-refreshes every 5 seconds</p>"); client.println("<div class='cards'>"); // Temperatuurikaart client.println("<div class='card'><div class='icon'>🌡️</div>"); client.println("<div class='val' style='color:#f59e0b'>" + tempStr + "</div>"); client.println("<div class='label'>Temperature</div></div>"); // Õhuniiskuse kaart client.println("<div class='card'><div class='icon'>💧</div>"); client.println("<div class='val' style='color:#60a5fa'>" + humStr + "</div>"); client.println("<div class='label'>Humidity</div></div>"); // LED-i olekukaart client.println("<div class='card'><div class='icon'>" + String(ledOn ? "💡" : "🌑") + "</div>"); client.println("<div class='val'>" + String(ledOn ? "ON" : "OFF") + "</div>"); client.println("<div class='label'>LED</div></div>"); client.println("</div>"); // LED-i lülitusnupp client.print("<a class='btn' style='background:"); client.print(ledOn ? "#dc2626" : "#16a34a"); client.println("' href='/toggle'>" + String(ledOn ? „Lülita välja“ : „Lülita sisse“) + "</a>"); client.println("</body></html>"); client.flush(); }

🎯 Activity 3: Make It Your Own!

  1. Lisa hoiatus: kui temperatuur > 30°C, kuva kaart PUNASELT tekstiga „🔥 KUUM!”
  2. Lisa loendur, mis näitab lehe külastuste arvu (vihje: globaalne int visits = 0; ja visits++ in loop)
  3. Logi temperatuur iga minut jadapordi monitori ja arvuta keskmine
  4. Lisa valgusandur (LDR analoogviigul) ja näita valgustaset protsentides
  5. Boss Challenge: Lisa HTML-i abil diagramm, mis näitab viimast 10 temperatuurinäitu <div> bars
🧠 PÕHIMÕISTED

What You Learned

🌐 HTTP Protocol

Browsers speak HTTP (HyperText Transfer Protocol). A request like GET /on HTTP/1.1 means "give me the page at /on." The server responds with 200 OK + HTML-sisu. See on kogu veebi alus — ja sinu ESP32 räägib nüüd seda keelt!

📡 Wi-Fi + TCP/IP

The ESP32 connects to your router, gets an IP address (like 192.168.1.42), and listens on port 80. When your phone sends a request to that IP, the router forwards it to the ESP32. This is networking in action.

🔌 GPIO (üldotstarbeline sisend/väljund)

GPIO pins can be set as OUTPUT (send voltage to control LEDs, motors, relays) or INPUT (read sensors, buttons). digitalWrite(pin, HIGH) sends 3.3V. digitalRead(pin) reads the voltage. This is how software controls the physical world.

📄 HTML + CSS

The web page your ESP32 sends is real HTML + CSS — the same technology behind every website. You're building full-stack IoT: hardware (ESP32 + sensors) + backend (C++ web server) + frontend (HTML/CSS page). That's 3 skill domains in one project!

🔄 Klient-server-arhitektuur

Your phone is the client (küsib lehti). Sinu ESP32 on server (saadab lehti ja täidab käske). Sama arhitektuur toetab Netflixi, Google’it, pangarakendusi ja kõike muud veebis.

🚀 MIS EDASI?

Vii oma ESP32 projektid uuele tasemele

⚡ Use ESPAsyncWebServer (Professional)

The WiFiServer we used is basic. For real projects, use the ESPAsyncWebServer library — it handles multiple clients simultaneously, supports WebSockets (real-time updates without page refresh), file serving from SPIFFS/LittleFS, and template processing. It's what professional IoT products use.

📱 Lisa AJAX uuendusteks ilma lehte värskendamata

Instead of reloading the page to update sensor data, use JavaScript fetch() to request data from the ESP32 in the background and update the page dynamically. The dashboard updates live without flickering!

🏠 Connect to Home Assistant

Pane ESP32 tööle koos Home Assistant (avatud lähtekoodiga koduautomaatika), kasutades ESPHome — a YAML-based firmware that turns ESP32 into smart home sensors and controllers without writing C++ code.

🔒 Lisa Tailscale kaugjuurdepääsuks

Want to access your dashboard from outside your home? Don't use port forwarding! Install Tailscale on a Raspberry Pi on the same network — now you can access your ESP32 dashboard from anywhere through an encrypted tunnel.

📊 Salvesta andmeid InfluxDB + Grafana abil

Send sensor readings to InfluxDB (aegridade andmebaas) ja visualiseeri neid tööriistaga Grafana dashboards — see weeks of temperature history, humidity trends, and alerts. All open source.

🌐🔧✨

$5 maksev kiip. Veebiserver. Your Creation.

You built a real web server on a chip smaller than your thumb. You learned HTTP, Wi-Fi networking, GPIO control, HTML, and client-server architecture — all in one project. These are the same skills used by IoT engineers at companies like Tesla, Philips Hue, and Ring. The difference? They had expensive equipment. You did it for $5.

«وَأَلَنَّا لَهُ الْحَدِيدَ»

Tilluke ränikiip — meie ajastu pehmendatud raud. Teadmiste ja koodiga andsid sellele võime serveerida veebilehti, juhtida valgust ning mõõta temperatuuri. Tehnoloogia pole maagia — see on sihipäraselt rakendatud teadmine. ✨