Skip to Content
ESP ve IoTWeb Sunucu ile Uzaktan Kontrol

Web Sunucu ile Uzaktan Kontrol

ESP kartının en pratik kullanımlarından biri, kendi web sayfasını yayınlamasıdır. Aynı ağdaki telefon veya bilgisayardan tarayıcıyı açıp kartın IP adresine giderek cihazınızı kontrol edebilirsiniz.

Ek bir uygulama yazmaya, bulut servisine kaydolmaya veya abonelik ödemeye gerek yoktur.

En Basit Web Sunucu

#include <WiFi.h> // ESP32 #include <WebServer.h> // ESP8266 icin: // #include <ESP8266WiFi.h> // #include <ESP8266WebServer.h> const char* SSID = "AgAdi"; const char* SIFRE = "Sifre123"; WebServer server(80); // ESP8266: ESP8266WebServer server(80); void anaSayfa() { server.send(200, "text/plain", "Merhaba! ESP calisiyor."); } void bulunamadi() { server.send(404, "text/plain", "Sayfa bulunamadi"); } void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(SSID, SIFRE); while (WiFi.status() != WL_CONNECTED) { delay(300); Serial.print("."); } Serial.println(); Serial.print("IP adresi: "); Serial.println(WiFi.localIP()); server.on("/", anaSayfa); server.onNotFound(bulunamadi); server.begin(); Serial.println("Web sunucu basladi."); } void loop() { server.handleClient(); // her donguDE cagrilmali }

Seri Monitör’de görünen IP adresini tarayıcıya yazın (örneğin http://192.168.1.42).

server.handleClient() fonksiyonu loop() içinde sürekli çağrılmalıdır. delay() kullanırsanız sunucu o süre boyunca isteklere cevap veremez. Bu, web sunuculu projelerde millis() kullanmanın zorunlu olmasının nedenidir.

Tarayıcıdan LED Kontrolü

#include <WiFi.h> #include <WebServer.h> const char* SSID = "AgAdi"; const char* SIFRE = "Sifre123"; const int LED_PIN = 2; WebServer server(80); bool ledDurumu = false; String sayfaOlustur() { String html = "<!DOCTYPE html><html lang='tr'><head>"; html += "<meta charset='utf-8'>"; html += "<meta name='viewport' content='width=device-width,initial-scale=1'>"; html += "<title>ESP Kontrol</title><style>"; html += "body{font-family:system-ui,sans-serif;text-align:center;padding:40px;background:#f4f4f5}"; html += "h1{color:#17324d}"; html += ".btn{display:inline-block;padding:16px 40px;margin:10px;font-size:18px;"; html += "text-decoration:none;border-radius:8px;color:#fff}"; html += ".ac{background:#16a34a}.kapat{background:#dc2626}"; html += ".durum{font-size:22px;margin:24px 0;font-weight:600}"; html += "</style></head><body>"; html += "<h1>ESP LED Kontrol</h1>"; html += "<p class='durum'>Durum: "; html += (ledDurumu ? "ACIK" : "KAPALI"); html += "</p>"; html += "<a class='btn ac' href='/ac'>AC</a>"; html += "<a class='btn kapat' href='/kapat'>KAPAT</a>"; html += "</body></html>"; return html; } void anaSayfa() { server.send(200, "text/html", sayfaOlustur()); } void ledAc() { ledDurumu = true; digitalWrite(LED_PIN, HIGH); server.sendHeader("Location", "/"); server.send(303); // ana sayfaya yonlendir } void ledKapat() { ledDurumu = 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.mode(WIFI_STA); WiFi.begin(SSID, SIFRE); while (WiFi.status() != WL_CONNECTED) { delay(300); } Serial.print("IP: "); Serial.println(WiFi.localIP()); server.on("/", anaSayfa); server.on("/ac", ledAc); server.on("/kapat", ledKapat); server.begin(); } void loop() { server.handleClient(); }

303 yönlendirmesi neden var?

Butona bastıktan sonra kullanıcıyı ana sayfaya geri göndermezseniz tarayıcıda /ac adresi kalır. Kullanıcı sayfayı yenilediğinde komut tekrar çalışır. 303 yönlendirmesi bunu engeller ve arayüzün tutarlı kalmasını sağlar.

viewport etiketi

<meta name='viewport' ...> satırı, sayfanın telefonda düzgün görünmesini sağlar. Bu satır olmadan sayfa masaüstü genişliğinde açılır ve butonlar minicik görünür.

Sensör Verisini Göstermek

Sayfanın otomatik yenilenmesi için basit bir yol, HTML’e meta refresh eklemektir:

void sensorSayfasi() { int ham = analogRead(34); float volt = ham * (3.3 / 4095.0); String html = "<!DOCTYPE html><html lang='tr'><head>"; html += "<meta charset='utf-8'>"; html += "<meta name='viewport' content='width=device-width,initial-scale=1'>"; html += "<meta http-equiv='refresh' content='5'>"; // 5 saniyede bir yenile html += "<title>Sensor</title></head><body style='font-family:sans-serif;text-align:center'>"; html += "<h1>Sensor Degeri</h1>"; html += "<p style='font-size:48px;margin:0'>" + String(ham) + "</p>"; html += "<p style='font-size:20px;color:#666'>" + String(volt, 2) + " V</p>"; html += "<p style='color:#999'>Calisma suresi: " + String(millis() / 1000) + " sn</p>"; html += "</body></html>"; server.send(200, "text/html", html); }

Bu yöntem basittir ama tüm sayfayı yeniden yükler; ekran her beş saniyede bir titrer.

JSON API ve AJAX ile Yenileme

Daha iyi yöntem, sayfayı bir kez yükleyip yalnızca veriyi periyodik olarak çekmektir.

void jsonVeri() { int ham = analogRead(34); float volt = ham * (3.3 / 4095.0); String json = "{"; json += "\"ham\":" + String(ham) + ","; json += "\"volt\":" + String(volt, 3) + ","; json += "\"sure\":" + String(millis() / 1000); json += "}"; server.send(200, "application/json", json); } void anaSayfa() { String html = "<!DOCTYPE html><html lang='tr'><head>"; html += "<meta charset='utf-8'>"; html += "<meta name='viewport' content='width=device-width,initial-scale=1'>"; html += "<title>Canli Sensor</title>"; html += "<style>body{font-family:system-ui,sans-serif;text-align:center;padding:40px}"; html += "#deger{font-size:56px;font-weight:700;color:#ff6500}</style>"; html += "</head><body>"; html += "<h1>Canli Sensor</h1>"; html += "<div id='deger'>--</div>"; html += "<div id='volt' style='color:#666'>-- V</div>"; html += "<script>"; html += "async function guncelle(){"; html += " try{"; html += " const r = await fetch('/veri');"; html += " const d = await r.json();"; html += " document.getElementById('deger').textContent = d.ham;"; html += " document.getElementById('volt').textContent = d.volt + ' V';"; html += " }catch(e){}"; html += "}"; html += "setInterval(guncelle, 1000);"; html += "guncelle();"; html += "</script></body></html>"; server.send(200, "text/html", html); } void setup() { // ... Wi-Fi baglantisi ... server.on("/", anaSayfa); server.on("/veri", jsonVeri); server.begin(); }

Bu yaklaşım sayfayı yenilemeden veriyi günceller ve çok daha az veri aktarır.

Parametre Almak

URL üzerinden değer göndermek için:

// Ornek istek: http://192.168.1.42/parlaklik?deger=180 void parlaklikAyarla() { if (server.hasArg("deger")) { int deger = server.arg("deger").toInt(); deger = constrain(deger, 0, 255); ledcWrite(0, deger); // ESP32 PWM server.send(200, "text/plain", "Parlaklik: " + String(deger)); } else { server.send(400, "text/plain", "Eksik parametre: deger"); } } void setup() { // ... server.on("/parlaklik", parlaklikAyarla); }

Kaydırıcılı (slider) bir arayüz için HTML tarafında:

<input type="range" min="0" max="255" value="0" oninput="fetch('/parlaklik?deger='+this.value)">

Röle Kontrolü

Aynı yapıyla bir röleyi kontrol etmek mümkündür. Röle kartlarının çoğu aktif LOW çalıştığını unutmayın:

const int ROLE_PIN = 26; bool roleAcik = false; void roleAyarla(bool ac) { roleAcik = ac; digitalWrite(ROLE_PIN, ac ? LOW : HIGH); // aktif LOW } void setup() { digitalWrite(ROLE_PIN, HIGH); // once pasif seviye pinMode(ROLE_PIN, OUTPUT); // ... }

220 V ile çalışacaksanız Röle Nedir? yazısındaki güvenlik uyarılarını mutlaka okuyun.

Basit Şifre Koruması

Ev ağınızdaki herkes kartınıza erişebilir. Temel bir koruma için HTTP kimlik doğrulaması ekleyebilirsiniz:

const char* KULLANICI = "admin"; const char* PAROLA = "gizli123"; void korumaliSayfa() { if (!server.authenticate(KULLANICI, PAROLA)) { return server.requestAuthentication(); } server.send(200, "text/html", sayfaOlustur()); }

Bu koruma HTTP üzerinden şifresiz taşınır. Yerel ağda basit bir engel olarak iş görür, ancak internete açılan bir cihaz için yeterli değildir. Kartınızı doğrudan internete (port yönlendirme ile) açmayın; bunun yerine MQTT veya bir bulut aracısı kullanın.

Kodu Temiz Tutmak: LittleFS

HTML’i C++ string olarak yazmak kısa sayfalarda iş görür ama hızla okunmaz hale gelir.

LittleFS ile HTML, CSS ve JS dosyalarını kartın flash belleğine ayrı dosyalar olarak yükleyebilirsiniz:

#include <LittleFS.h> void setup() { if (!LittleFS.begin()) { Serial.println("LittleFS baslatilamadi"); return; } // ... Wi-Fi ... server.serveStatic("/", LittleFS, "/index.html"); server.serveStatic("/style.css", LittleFS, "/style.css"); server.begin(); }

Dosyaları yüklemek için Arduino IDE’ye “ESP32 LittleFS Data Upload” eklentisi kurulur ve dosyalar proje klasöründeki data/ dizinine konur.

Sayfanız birkaç butondan fazlaysa bu yönteme geçmek kodu belirgin şekilde sadeleştirir.

Sık Karşılaşılan Sorunlar

BelirtiNeden
Sayfa açılmıyorTelefon/bilgisayar farklı ağda; ikisi de aynı Wi-Fi’de olmalı
Sayfa yavaş/takılıyorloop() içinde delay() var — millis() kullanın
IP her açılışta değişiyorDHCP; sabit IP atayın
Türkçe karakterler bozuk<meta charset='utf-8'> etiketini ekleyin
Telefonda çok küçük görünüyorviewport meta etiketi eksik
Butona basınca komut tekrarlanıyor303 yönlendirmesi eklenmemiş
Bir süre sonra cevap vermiyorBellek sızıntısı; uzun String birleştirmelerinden kaçının

Özet

ESP kartları kendi web sunucusunu çalıştırarak tarayıcıdan kontrol edilebilir. server.on() ile adresler tanımlanır, server.handleClient() ile istekler işlenir.

Bu derste şunları öğrendik:

  • Temel web sunucu kurulumunu
  • Tarayıcıdan LED/röle kontrolünü ve 303 yönlendirmesini
  • Sensör verisini sayfada göstermeyi
  • JSON API ve AJAX ile sayfayı yenilemeden güncellemeyi
  • URL parametresi almayı
  • Basit şifre korumasını ve sınırlarını
  • Büyüyen projelerde LittleFS’e geçmeyi