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()fonksiyonuloop()içinde sürekli çağrılmalıdır.delay()kullanırsanız sunucu o süre boyunca isteklere cevap veremez. Bu, web sunuculu projelerdemillis()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
| Belirti | Neden |
|---|---|
| Sayfa açılmıyor | Telefon/bilgisayar farklı ağda; ikisi de aynı Wi-Fi’de olmalı |
| Sayfa yavaş/takılıyor | loop() içinde delay() var — millis() kullanın |
| IP her açılışta değişiyor | DHCP; sabit IP atayın |
| Türkçe karakterler bozuk | <meta charset='utf-8'> etiketini ekleyin |
| Telefonda çok küçük görünüyor | viewport meta etiketi eksik |
| Butona basınca komut tekrarlanıyor | 303 yönlendirmesi eklenmemiş |
| Bir süre sonra cevap vermiyor | Bellek 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