Rehberler · 12 dk okuma

ESP32 Async Web Server: WebSocket ile Canlı Kontrol Paneli

ESP32 async web server ve WebSocket ile sayfa yenilemeden röle kontrolü ve canlı sensör paneli: kurulum, PROGMEM ve LittleFS, esp32.local, parola, sık hatalar.

ESP32 async web server, yani ESPAsyncWebServer kütüphanesi, ESP32'yi aynı anda birden çok telefona ve bilgisayara yanıt verebilen hızlı bir web sunucusuna çevirir. Kütüphanenin içinde gelen WebSocket desteğiyle tarayıcı ile kart arasında sürekli açık bir hat kurulur: sayfadaki düğmeye bastığında röle anında çeker, sıcaklık değiştiğinde sayfadaki rakam kendiliğinden güncellenir ve bunun için sayfayı yenilemen gerekmez. Bir telefonda yaptığın değişikliği aynı paneli açmış diğer cihazlar da aynı saniyede görür. Kısacası bu ikili, kendi akıllı ev panelini bulut hizmeti kullanmadan, ev ağının içinde kurmanın en pratik yoludur.

Bu rehberde önce asenkron sunucunun klasik WebServer kütüphanesinden farkını ve WebSocket'in HTTP isteklerinden neden daha hızlı olduğunu göreceksin. Ardından doğru kütüphaneleri kurup en basit sunucuyu çalıştıracak, sonra bir röle ve DHT22 sensörüyle gerçek zamanlı bir kontrol paneli yapacaksın. Devamında HTML'i PROGMEM ya da LittleFS içinde tutmayı, IP adresi yerine esp32.local adresini kullanmayı, birden çok istemciyi yönetmeyi, panele basit bir parola koymayı ve en sık karşılaşılan derleme ve bağlantı hatalarının çözümlerini bulacaksın. Kodlar, Arduino IDE'deki ESP32 çekirdeğinin 2.x ve 3.x sürümleriyle çalışacak şekilde yazıldı.

ESP32 geliştirme kartı ile web sunucu projesi
ESP32 DevKit: Wi-Fi yongası sayesinde tek başına bir web sunucusu çalıştırır; telefonunda açtığın panel doğrudan bu kartın flaş belleğinden gelir.

ESP32 Async Web Server nedir, nasıl çalışır?

ESP32 çekirdeğiyle birlikte gelen WebServer.h kütüphanesi senkron çalışır: gelen istekler ancak loop() içinde server.handleClient() çağrıldığında, sırayla ve tek tek işlenir. loop() içinde 2 saniyelik bir delay() varsa sayfa da 2 saniye bekler; iki kişi aynı anda bağlanırsa biri diğerinin bitmesini bekler. ESPAsyncWebServer ise AsyncTCP kütüphanesinin kurduğu ayrı bir FreeRTOS görevinde (async_tcp) çalışır. Bir istek geldiğinde başlıklar arka planda okunur, istek hazır olunca senin yazdığın fonksiyon (callback) çağrılır ve yanıt yine arka planda gönderilir. Bu sırada loop() kendi işine devam eder. Böylece aynı anda birkaç bağlantı açık kalabilir; WebSocket ve Server-Sent Events (SSE) gibi sürekli açık bağlantılar da mümkün olur. ESP32'nin iki çekirdeğini ve görevleri merak ediyorsan ESP32 FreeRTOS yazısına göz atabilirsin.

ÖzellikWebServer.h (senkron)ESPAsyncWebServer (asenkron)
KurulumESP32 çekirdeğiyle hazır gelirKütüphane Yöneticisi'nden 2 kütüphane
İsteklerin işlenmesiloop() içinde handleClient() ile, sıraylaArka plandaki async_tcp görevinde, aynı anda birden çok
loop() içinde delay()Sayfa yanıtını geciktirirSunucuyu etkilemez
WebSocketYok; ayrı bir kütüphane gerekir (ör. Markus Sattler'ın WebSockets kütüphanesi)Yerleşik (AsyncWebSocket)
Server-Sent EventsYokYerleşik (AsyncEventSource)
Statik dosya sunmaserveStatic ileserveStatic, otomatik .gz desteği ve önbellek ayarı
Dikkat edilecek noktaÖğrenmesi en kolay olanıCallback içinde delay() ve uzun işlem yok

WebSocket nedir? HTTP isteğinden farkı ne?

Normal bir HTTP isteğinde tarayıcı bir bağlantı açar, "bana bu sayfayı ver" der, yanıtı alır ve konuşma biter. Sunucu kendiliğinden tarayıcıya bir şey söyleyemez; yeni bir değer görmek için tarayıcının tekrar sorması gerekir. WebSocket (RFC 6455) ise HTTP ile başlayan bir el sıkışmadır: tarayıcı isteğine Upgrade: websocket başlığını ekler, ESP32 101 Switching Protocols yanıtıyla kabul eder ve aynı TCP bağlantısı açık kalır. Artık iki taraf da istediği an mesaj gönderebilir (full-duplex). Her mesajın ek yükü yalnızca 2 ila 14 bayttır; oysa her HTTP isteği yüzlerce baytlık başlık taşır. Bu yüzden WebSocket mesajları milisaniyeler içinde ulaşır ve saniyede birkaç güncelleme gönderen paneller için en verimli yöntemdir. Sayfayı sensör değerleriyle güncel tutmanın dört yolu şöyle karşılaştırılır:

YöntemNasıl çalışır?Gecikme ve yükNe zaman kullanılır?
Sayfa yenileme (meta refresh)Tarayıcı tüm sayfayı birkaç saniyede bir baştan indirirYüksek; sayfa her seferinde göz kırparTercih etme
fetch ile yoklama (polling)JavaScript her 1–2 saniyede bir küçük bir JSON isterOrta; her istek yeni başlıklar taşırSeyrek değişen tek bir değer
Server-Sent Events (SSE)Sunucu açık bir bağlantı üzerinden tek yönlü olay yollarDüşük; yalnızca sunucudan tarayıcıyaYalnızca izleme yapan paneller
WebSocketAçık bağlantı üzerinden iki yönlü mesajlaşmaEn düşük; birkaç baytlık çerçevelerDüğme, kaydırıcı ve canlı veri bir arada

Gerekli kütüphaneler: ESPAsyncWebServer ve AsyncTCP kurulumu

  1. Arduino IDE'de ESP32 kart paketinin (Espressif Systems) kurulu olduğundan emin ol; kurulum adımları ESP32 Nedir? yazısında.
  2. Kütüphane Yöneticisi'ni aç (Ctrl+Shift+I) ve ESPAsyncWebServer ara. Yazarı ESP32Async olan ESP Async WebServer kütüphanesini kur.
  3. Bu kütüphane bağımlılığını Kütüphane Yöneticisi'ne bildirmediği için IDE ek kütüphane sormaz: yine ESP32Async'in yayımladığı Async TCP kütüphanesini ayrıca kur. ESP8266 kullanıyorsan bunun yerine ESP32Async'in ESP Async TCP kütüphanesi (ESPAsyncTCP.h) gerekir.
  4. Panel örneği için Adafruit'in DHT sensor library kütüphanesini ve istediği Adafruit Unified Sensor bağımlılığını kur. Kütüphane kurmayı ilk kez yapıyorsan Arduino'ya kütüphane ekleme rehberine göz at.
  5. Belgeler/Arduino/libraries klasöründe eskiden ZIP ile eklenmiş ESPAsyncWebServer-master, AsyncTCP-master ya da ESPAsyncWebSrv klasörleri varsa sil. Aynı kütüphanenin iki kopyası, derleyicinin yanlış olanı seçmesine yol açar.
İnternetteki eski rehberlerin çoğu, me-no-dev hesabındaki orijinal ESPAsyncWebServer kütüphanesini gösterir. Bu kütüphane artık güncellenmiyor ve ESP32 çekirdeğinin 3.x sürümüyle derlenmiyor; derleme 'mbedtls_md5_starts_ret' was not declared in this scope hatasıyla durur. Çözüm, eski kopyayı silip ESP32Async sürümlerini kurmaktır. Yeni sürümde send_P() da kullanımdan kaldırıldı (deprecated): ESP32'de PROGMEM dizisini doğrudan request->send(200, "text/html", index_html) ile gönderebilirsin.

İlk adım: AsyncWebServer ile en basit sunucu

Panele geçmeden önce kütüphanenin mantığını küçük bir örnekle görelim. Aşağıdaki kod ESP32'yi ev ağına bağlar, ana sayfada bir karşılama yazısı gösterir ve /led?durum=1 ile /led?durum=0 adresleriyle dahili LED'i yakıp söndürür. Kodu yükle, Seri Monitör'ü 115200 baud hızında aç ve görünen adresi aynı Wi-Fi'ye bağlı telefonunun tarayıcısına yaz. Kart ağa bağlanmıyorsa ESP32 WiFi bağlanmıyor rehberindeki teşhis kodunu kullan.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4
5const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
6const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
7const int LED_PIN = 2;               // çoğu DevKit'te dahili LED / onboard LED on most DevKits
8
9AsyncWebServer server(80);           // 80 = standart HTTP portu / standard HTTP port
10bool ledAcik = false;
11
12void setup() {
13  Serial.begin(115200);
14  pinMode(LED_PIN, OUTPUT);
15
16  WiFi.mode(WIFI_STA);
17  WiFi.begin(ssid, password);
18  while (WiFi.status() != WL_CONNECTED) {
19    delay(500);
20    Serial.print(".");
21  }
22  Serial.println();
23  Serial.print("Adres / address: http://");
24  Serial.println(WiFi.localIP());
25
26  // Ana sayfa / home page
27  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
28    request->send(200, "text/plain", "Merhaba, ben ESP32 / Hello from ESP32");
29  });
30
31  // /led?durum=1 yakar, /led?durum=0 söndürür / turns the LED on or off
32  server.on("/led", HTTP_GET, [](AsyncWebServerRequest *request) {
33    if (request->hasParam("durum")) {
34      ledAcik = request->getParam("durum")->value() == "1";
35      digitalWrite(LED_PIN, ledAcik ? HIGH : LOW);
36    }
37    request->send(200, "text/plain", ledAcik ? "LED: ON" : "LED: OFF");
38  });
39
40  // Tanımsız adresler / unknown addresses
41  server.onNotFound([](AsyncWebServerRequest *request) {
42    request->send(404, "text/plain", "Sayfa yok / Not found");
43  });
44
45  server.begin();
46}
47
48void loop() {
49  // Sunucu arka planda çalışır, loop() boş kalabilir / the server runs in the background
50}
KomutNe işe yarar?
AsyncWebServer server(80)80 numaralı HTTP portunu dinleyen sunucuyu oluşturur
server.on("/led", HTTP_GET, ...)Bir adres ve istek türü için çalışacak fonksiyonu (callback) kaydeder
request->hasParam("durum")Adreste ?durum=... parametresi olup olmadığına bakar
request->getParam("durum")->value()Parametrenin değerini String olarak verir
request->send(kod, tür, içerik)Yanıtı gönderir: 200 başarılı, 404 bulunamadı; tür text/plain, text/html ya da application/json
server.onNotFound(...)Tanımlanmamış her adres için çalışır
server.begin()Sunucuyu başlatır; tüm server.on() satırlarından sonra çağrılır
loop() fonksiyonunun boş kalması hata değildir; sunucu kendi görevinde çalışır. Tersine, callback fonksiyonlarının içinde delay(), uzun while döngüleri ya da internetten veri çeken istekler kullanma. async_tcp görevi bu sırada kilitlenir ve birkaç saniye sonra ESP32 Task watchdog got triggered mesajıyla yeniden başlar. Uzun işleri bir bayrak (flag) değişkeniyle loop()'a bırak, zamanlamayı da millis() ile yap.

Kontrol paneli için malzemeler ve bağlantı

Panel örneğinde bir röle modülü ile bir lambayı ya da fanı açıp kapatacak, DHT22 ile sıcaklık ve nemi ölçeceksin. Gerekenler: bir ESP32 DevKit, tek kanallı 5 V röle modülü, DHT22 (ya da DHT11) sensör modülü, breadboard ve jumper kablolar. Röle seçerken optokuplörlü modülleri tercih et. DHT22, −40 ile 80 °C arasında ±0,5 °C doğrulukla ölçer ve en fazla 2 saniyede bir okunabilir; DHT11 daha ucuzdur ama 0–50 °C aralığında ±2 °C doğruluk verir. Sensörün ayrıntıları için DHT11 sıcaklık ve nem sensörü yazısına, kartının pin haritası için Pinout aracına bakabilirsin.

Modül piniESP32 DevKitESP8266 NodeMCUNot
Röle VCCVIN (5 V)VIN ya da VU (5 V)Röle bobini 5 V ister; USB'den beslenir
Röle GNDGNDGNDOrtak toprak
Röle INGPIO 26D1 (GPIO 5)Çoğu modül LOW sinyalle çeker
DHT22 VCC (+)3V33V33,3 V ile beslenince veri hattı da 3,3 V olur
DHT22 DATA (OUT)GPIO 4D2 (GPIO 4)Çıplak sensörde DATA ile VCC arasına 10 kΩ direnç
DHT22 GND (−)GNDGNDOrtak toprak
ESP32'nin pinleri 3,3 V çıkış verir. Bazı 5 V röle modülleri bu seviyeyi HIGH olarak tanımaz ve röle hiç bırakmaz; böyle bir modülde 3,3 V uyumlu bir röle, JD-VCC atlamalı bir modül ya da araya bir transistör kullan. Röleyle 220 V şebeke gerilimi anahtarlayacaksan kabloları enerji kesikken bağla, açık klemens bırakma ve emin değilsen önce düşük gerilimli bir yükle (LED şerit, 12 V fan) dene. Ayrıntılar Röle Modülü Nedir? yazısında.

Gerçek zamanlı kontrol paneli: röle, DHT22 ve WebSocket

Şimdi asıl projeye geçelim. Aşağıdaki kod tek bir dosyadır: HTML, CSS ve JavaScript, PROGMEM dizisi olarak kodun içindedir. Sayfa açıldığında tarayıcı /ws adresine bir WebSocket bağlantısı kurar. ESP32 her 2 saniyede bir DHT22'yi okur ve sıcaklık, nem, röle durumu ile bağlı cihaz sayısını tek bir JSON mesajıyla bütün tarayıcılara gönderir. Düğmeye bastığında tarayıcı toggle mesajını yollar, ESP32 röleyi çevirir ve yeni durumu yine herkese bildirir. Ağ adını ve şifreni değiştirip yükle, ardından tarayıcıda http://esp32.local ya da Seri Monitör'deki IP adresini aç.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4#include <ESPmDNS.h>
5#include <DHT.h>
6
7const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
8const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
9
10const int ROLE_PIN = 26;             // röle modülü IN / relay module IN
11const bool ROLE_AKTIF_LOW = true;    // çoğu modül LOW ile çeker / most modules switch on LOW
12const int DHT_PIN = 4;               // DHT22 veri pini / DHT22 data pin
13
14DHT dht(DHT_PIN, DHT22);             // DHT11 için DHT11 yaz / write DHT11 for a DHT11
15AsyncWebServer server(80);
16AsyncWebSocket ws("/ws");            // WebSocket adresi / WebSocket path
17
18bool roleAcik = false;
19float sicaklik = NAN;
20float nem = NAN;
21volatile bool gonder = false;        // loop() yayın yapsın mı? / should loop() broadcast?
22unsigned long sonOlcum = 0;
23unsigned long sonTemizlik = 0;
24
25// Tarayıcıya giden sayfa / the page sent to the browser
26const char index_html[] PROGMEM = R"rawliteral(
27<!DOCTYPE html>
28<html lang="tr">
29<head>
30<meta charset="utf-8">
31<meta name="viewport" content="width=device-width, initial-scale=1">
32<title>ESP32 Panel</title>
33<style>
34body{font-family:sans-serif;background:#0f172a;color:#e2e8f0;text-align:center;margin:0;padding:16px}
35.kart{background:#1e293b;border-radius:12px;padding:16px;margin:12px auto;max-width:320px}
36.deger{font-size:2.4em;font-weight:bold}
37button{font-size:1.2em;padding:14px 32px;border:0;border-radius:8px;color:#fff;width:100%}
38.acik{background:#16a34a}.kapali{background:#475569}
39</style>
40</head>
41<body>
42<h2>ESP32 Panel</h2>
43<p id="baglanti">Bağlanıyor / Connecting...</p>
44<div class="kart">Sıcaklık / Temperature<div class="deger"><span id="sicaklik">--</span> &deg;C</div></div>
45<div class="kart">Nem / Humidity<div class="deger"><span id="nem">--</span> %</div></div>
46<div class="kart">Röle / Relay<p><button id="dugme" class="kapali" onclick="degistir()">KAPALI / OFF</button></p></div>
47<p>Bağlı cihaz / Clients: <span id="istemci">0</span></p>
48<script>
49let ws;
50const $ = (id) => document.getElementById(id);
51function baglan() {
52  ws = new WebSocket('ws://' + location.host + '/ws');
53  ws.onopen = () => { $('baglanti').textContent = 'Bağlı / Connected'; };
54  ws.onclose = () => {
55    $('baglanti').textContent = 'Koptu, yeniden deneniyor / Reconnecting...';
56    setTimeout(baglan, 2000);
57  };
58  ws.onmessage = (e) => {
59    const d = JSON.parse(e.data);
60    $('sicaklik').textContent = d.sicaklik === null ? '--' : d.sicaklik.toFixed(1);
61    $('nem').textContent = d.nem === null ? '--' : d.nem.toFixed(0);
62    $('istemci').textContent = d.istemci;
63    $('dugme').textContent = d.role ? 'AÇIK / ON' : 'KAPALI / OFF';
64    $('dugme').className = d.role ? 'acik' : 'kapali';
65  };
66}
67function degistir() {
68  if (ws.readyState === WebSocket.OPEN) ws.send('toggle');
69}
70baglan();
71</script>
72</body>
73</html>
74)rawliteral";
75
76void roleAyarla(bool acik) {
77  roleAcik = acik;
78  digitalWrite(ROLE_PIN, ROLE_AKTIF_LOW ? !acik : acik);
79}
80
81// Durumu JSON metnine çevir / build the state as JSON text
82String durumJson() {
83  String s = "{\"role\":";
84  s += roleAcik ? "true" : "false";
85  s += ",\"sicaklik\":";
86  s += isnan(sicaklik) ? String("null") : String(sicaklik, 1);
87  s += ",\"nem\":";
88  s += isnan(nem) ? String("null") : String(nem, 0);
89  s += ",\"istemci\":";
90  s += (unsigned long)ws.count();
91  s += "}";
92  return s;
93}
94
95// WebSocket olayları (async_tcp görevinde çalışır) / WebSocket events (run in the async_tcp task)
96void wsOlay(AsyncWebSocket *sunucu, AsyncWebSocketClient *client, AwsEventType type,
97            void *arg, uint8_t *data, size_t len) {
98  switch (type) {
99    case WS_EVT_CONNECT:
100      Serial.printf("Istemci #%u baglandi / connected: %s\n",
101                    (unsigned)client->id(), client->remoteIP().toString().c_str());
102      gonder = true;                 // yeni gelen güncel durumu alsın / newcomer gets the state
103      break;
104    case WS_EVT_DISCONNECT:
105      Serial.printf("Istemci #%u ayrildi / disconnected\n", (unsigned)client->id());
106      gonder = true;
107      break;
108    case WS_EVT_DATA: {
109      AwsFrameInfo *info = (AwsFrameInfo *)arg;
110      // Tek parça, tam bir metin mesajı mı? / a complete single-frame text message?
111      if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
112        if (len == 6 && memcmp(data, "toggle", 6) == 0) {
113          roleAyarla(!roleAcik);
114          gonder = true;             // tüm ekranlar eşitlensin / sync every screen
115        }
116      }
117      break;
118    }
119    default:                         // PONG, ERROR vb. / PONG, ERROR etc.
120      break;
121  }
122}
123
124void setup() {
125  Serial.begin(115200);
126  pinMode(ROLE_PIN, OUTPUT);
127  roleAyarla(false);                 // açılışta röle kapalı / relay off at start
128  dht.begin();
129
130  WiFi.mode(WIFI_STA);
131  WiFi.begin(ssid, password);
132  Serial.print("Baglaniyor / connecting");
133  while (WiFi.status() != WL_CONNECTED) {
134    delay(500);
135    Serial.print(".");
136  }
137  WiFi.setSleep(false);              // daha hızlı tepki / lower latency
138  Serial.println();
139  Serial.print("http://");
140  Serial.println(WiFi.localIP());
141
142  if (MDNS.begin("esp32")) {         // http://esp32.local
143    MDNS.addService("http", "tcp", 80);
144    Serial.println("http://esp32.local");
145  }
146
147  ws.onEvent(wsOlay);
148  server.addHandler(&ws);
149
150  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
151    request->send(200, "text/html", index_html);
152  });
153  server.onNotFound([](AsyncWebServerRequest *request) {
154    request->send(404, "text/plain", "Sayfa yok / Not found");
155  });
156  server.begin();
157}
158
159void loop() {
160  if (millis() - sonOlcum >= 2000) {       // DHT22 en fazla 2 sn'de bir / DHT22: once per 2 s
161    sonOlcum = millis();
162    float t = dht.readTemperature();
163    float h = dht.readHumidity();
164    if (!isnan(t) && !isnan(h)) {          // hatalı okumayı atla / skip failed reads
165      sicaklik = t;
166      nem = h;
167    }
168    gonder = true;
169  }
170
171  if (gonder) {
172    gonder = false;
173    ws.textAll(durumJson());               // tüm tarayıcılara yolla / send to every browser
174  }
175
176  if (millis() - sonTemizlik >= 1000) {
177    sonTemizlik = millis();
178    ws.cleanupClients();                   // kopuk istemcileri temizle / drop stale clients
179  }
180}

Kod nasıl çalışıyor?

  • AsyncWebSocket ws("/ws") WebSocket uç noktasını oluşturur, server.addHandler(&ws) ise onu sunucuya bağlar. JavaScript'teki adres ile buradaki yol birebir aynı olmalı.
  • wsOlay() fonksiyonu her bağlantıda, kopmada ve gelen mesajda çağrılır. WS_EVT_CONNECT yeni bir istemci, WS_EVT_DISCONNECT ayrılan bir istemci, WS_EVT_DATA ise gelen bir mesaj demektir.
  • AwsFrameInfo, gelen çerçevenin bilgisini taşır. final, index == 0 ve len kontrolü mesajın tek parça ve eksiksiz geldiğini, WS_TEXT ise metin olduğunu doğrular. Kısa komutlar her zaman tek çerçeveye sığar.
  • Gelen veri memcmp() ile uzunluğuyla birlikte karşılaştırılır; böylece verinin sonunda sıfır karakteri olup olmamasına bağımlı kalmazsın.
  • Callback fonksiyonları async_tcp görevinde çalıştığı için yalnızca röleyi çevirip gonder bayrağını kaldırır. Asıl gönderim loop() içinde ws.textAll() ile yapılır; böylece tüm mesajlar tek bir yerden, sırayla çıkar.
  • ws.cleanupClients() saniyede bir çağrılır: kopmuş bağlantıların belleğini geri verir ve istemci sayısı sınırı aştığında en eski bağlantıyı kapatır.
  • JavaScript tarafında onclose olayı 2 saniye sonra yeniden bağlanır. ESP32 yeniden başlasa ya da Wi-Fi bir anlığına kopsa bile sayfa kendini toparlar. Adres location.host ile oluşturulduğu için sayfa IP ile de esp32.local ile de çalışır.
  • WiFi.setSleep(false), Wi-Fi güç tasarrufunu kapatır. Varsayılan modem uykusu tepkilere yüzlerce milisaniyeye varan gecikme ekleyebilir; prizden beslenen bir panelde bunu kapatmak düğmeleri anlık hale getirir.

Mesaj biçimi olarak JSON seçmek işini kolaylaştırır: tarayıcı JSON.parse() ile gelen metni tek satırda nesneye çevirir. Örnek bir mesaj şöyle görünür: {"role":true,"sicaklik":23.4,"nem":48,"istemci":2}. Sensör okunamadığında değer null gönderilir ve sayfa "--" gösterir; okuma hatası NaN olarak gitseydi geçersiz JSON yüzünden sayfa hiç güncellenmezdi. Mesaj başına 60 bayt civarında veri gittiği için saniyede birkaç güncelleme bile ağı yormaz. Daha karmaşık mesajlar için ArduinoJson kütüphanesine geçebilirsin, ama bu boyutta bir panelde String birleştirmek fazlasıyla yeterlidir. Röle yerine bir LED ya da buzzer bağlayarak aynı kodu hiç değiştirmeden deneyebilirsin.

HTML'i nerede tutmalı: PROGMEM mi, LittleFS mi?

Panel kodunda sayfa, R"rawliteral( ... )rawliteral" biçiminde ham bir metin dizisi olarak duruyor. Bu yazım, tırnakları ve satır sonlarını kaçış karakteri yazmadan kullanmanı sağlar. AVR tabanlı Arduino'larda PROGMEM, metni RAM yerine flaşta tutmak için gereklidir; ESP32'de ise sabit diziler zaten flaştan okunduğu için PROGMEM yalnızca uyumluluk için yazılır ve zararı yoktur. Sayfa büyüdükçe kodun içinde HTML düzenlemek zorlaşır; o noktada dosyaları flaştaki LittleFS dosya sistemine taşımak daha düzenlidir.

ÖlçütPROGMEM (kodun içinde)LittleFS (dosya sistemi)
KurulumEk adım yok, tek dosyadata klasörü ve yükleme eklentisi gerekir
DüzenlemeHTML, C++ dizisinin içindeAyrı index.html, style.css ve app.js dosyaları
GüncellemeHer değişiklikte kodu yeniden yüklersinYalnızca dosya sistemini yeniden yüklersin
BoyutBirkaç KB'lık sayfalar için idealVarsayılan bölümlemede yaklaşık 1,5 MB alan
Görsel ve yazı tipiPratik değilSimge, resim ve .gz dosyaları sunulabilir
Ne zaman?İlk deneme ve küçük panellerÇok sayfalı ya da büyüyen arayüzler

LittleFS ile HTML, CSS ve JavaScript dosyalarını sunmak

  1. Proje klasörünün (.ino dosyasının bulunduğu klasör) içinde data adlı bir klasör oluştur ve panel kodunda R"rawliteral( ile )rawliteral" arasında kalan HTML'i data/index.html dosyasına kaydet. İstersen CSS ve JavaScript'i ayrı dosyalara bölebilirsin.
  2. Arduino IDE 2, eski sürümdeki ESP32 Sketch Data Upload menüsünü desteklemez. Bunun yerine arduino-littlefs-upload eklentisinin .vsix dosyasını indir; Windows'ta C:\Users\(kullanıcı adın)\.arduinoIDE\plugins, macOS ve Linux'ta ~/.arduinoIDE/plugins klasörüne koy (klasör yoksa oluştur) ve IDE'yi yeniden başlat. Eklenti için IDE 2.2.1 ya da daha yeni bir sürüm gerekir.
  3. Seri Monitör açıksa kapat, sonra Ctrl+Shift+P (macOS'ta Cmd+Shift+P) ile komut paletini aç ve Upload LittleFS to Pico/ESP8266/ESP32 komutunu seç. Dosyalar flaştaki dosya sistemi bölümüne yazılır.
  4. Araçlar > Partition Scheme menüsünde dosya sistemi bölümü olan bir şema seçili olsun (örneğin Default 4MB with spiffs). LittleFS, adı spiffs olan bu bölümü kullanır.
  5. Aşağıdaki kodu her zamanki gibi yükle. Araçlar menüsündeki Erase All Flash seçeneği kapalıysa kod yüklemek dosya sistemini silmez; dosyaları değiştirdiğinde yalnızca 3. adımı tekrarlaman yeter.
C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4#include <LittleFS.h>
5
6const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
8
9AsyncWebServer server(80);
10
11void setup() {
12  Serial.begin(115200);
13
14  if (!LittleFS.begin(true)) {       // true: bağlanamazsa biçimlendir / format if mount fails
15    Serial.println("LittleFS hatasi / LittleFS error");
16    return;
17  }
18
19  WiFi.mode(WIFI_STA);
20  WiFi.begin(ssid, password);
21  while (WiFi.status() != WL_CONNECTED) {
22    delay(500);
23  }
24  Serial.println(WiFi.localIP());
25
26  // data/ klasörünün içeriğini sun / serve the contents of the data/ folder
27  server.serveStatic("/", LittleFS, "/")
28        .setDefaultFile("index.html")    // varsayılan index.htm'dir / default is index.htm
29        .setCacheControl("max-age=600"); // tarayıcı 10 dk önbelleğe alsın / cache for 10 min
30
31  server.onNotFound([](AsyncWebServerRequest *request) {
32    request->send(404, "text/plain", "Sayfa yok / Not found");
33  });
34  server.begin();
35}
36
37void loop() {
38}
serveStatic(), bir dosya istendiğinde önce aynı adın .gz uzantılı sıkıştırılmış sürümünü arar. index.html dosyasını gzip ile sıkıştırıp index.html.gz olarak yüklersen sayfa genellikle üçte bir ile dörtte bir boyutuna iner ve daha hızlı açılır. Klasör adresi istendiğinde varsayılan dosya adı index.htm olduğu için setDefaultFile("index.html") satırını unutma. Kütüphanenin şablon (template) özelliğini kullanırsan sayfadaki %DEGER% gibi yer tutucular değiştirilir; bu durumda CSS'teki width:100% gibi yüzde işaretlerini %% olarak yazman gerekir.

mDNS: IP adresi yerine esp32.local

Modem ESP32'ye her açılışta farklı bir IP adresi verebilir ve 192.168.1.47 gibi sayıları akılda tutmak zordur. mDNS (multicast DNS), cihazın yerel ağda kendi adını duyurmasını sağlar. Panel kodundaki MDNS.begin("esp32") satırı sayesinde aynı ağdaki cihazlar sayfayı http://esp32.local adresinden açabilir; MDNS.addService("http", "tcp", 80) ise ağ tarayıcı uygulamalarına burada bir web sunucusu olduğunu söyler. Ada ".local" ekleme; yalnızca küçük harf, rakam ve tire kullan. Birden çok ESP32 kullanıyorsan her birine farklı bir ad ver (mutfak, salon gibi), çünkü aynı adı taşıyan iki cihaz birbirini bozar. mDNS her sistemde aynı ölçüde desteklenmez:

Sistem.local desteğiÇalışmazsa
iPhone, iPad, macOSYerleşik (Bonjour)Aynı Wi-Fi ağında olduğundan emin ol
Windows 10 ve 11Güncel sürümlerde genellikle çalışırBonjour hizmetini kur ya da IP kullan
Linuxavahi-daemon kuruluysa çalışıravahi-daemon ve nss-mdns paketlerini kur
AndroidSürüme ve tarayıcıya göre değişirIP adresini kullan ya da modemde DHCP rezervasyonu yap

Birden çok istemci: telefon, tablet ve bilgisayar aynı anda

WebSocket'in en sevilen yanı, paneli açan herkesin aynı durumu görmesidir. Telefonundan röleyi açtığında ESP32 yeni durumu textAll() ile bütün bağlantılara gönderir; bilgisayardaki sekme de aynı anda yeşile döner. Her sekme ayrı bir istemcidir: aynı tarayıcıda üç sekme açarsan sayaç 3 gösterir. Kütüphane, ESP32'de varsayılan olarak 8, ESP8266'da 4 eşzamanlı WebSocket istemcisine izin verir (DEFAULT_MAX_WS_CLIENTS). Bu sınır kendiliğinden uygulanmaz; cleanupClients() çağrıldığında sınırı aşan en eski bağlantı kapatılır. Bunun yanında dikkat etmen gerekenler şunlar:

  • Telefon uykuya geçince: Ekranı kapanan telefon bağlantıyı düzgün kapatmayabilir. Bu tür yarım kalmış bağlantılar, cleanupClients() ve düzenli veri gönderimi sayesinde zamanla temizlenir.
  • Gönderim hızı: Her istemci için en fazla 32 mesaj kuyrukta bekleyebilir (ESP8266'da 8). Her loop() turunda textAll() çağırırsan kuyruk dolar ve yeni mesajlar sessizce atılır; Araçlar > Core Debug Level ayarını Warn ya da daha ayrıntılı bir seviyeye alırsan Seri Monitör'de Too many messages queued uyarısını görürsün. Değeri yalnızca değiştiğinde ya da saniyede birkaç kez gönder.
  • Yeni gelen istemci: Bağlanan her sayfaya güncel durumu hemen yolla; yoksa kullanıcı ilk ölçüme kadar boş bir panele bakar. Panel kodunda bu işi WS_EVT_CONNECT içinde kaldırılan bayrak yapar.
  • Tek bir istemciye yanıt: Herkese değil yalnızca soran tarayıcıya cevap vermek için client->text("...") kullan; istemcinin numarasını client->id() verir.
  • Bellek: Her açık bağlantı bir TCP soketi ve tampon bellek tutar. ESP32'nin 520 KB RAM'i birkaç istemci için fazlasıyla yeterlidir, ama ESP.getFreeHeap() ile boş belleği ara sıra yazdırmak sızıntıları erkenden yakalamanı sağlar.

Basit parola koruması

Ev ağındaki herkes panelin adresini bilirse röleyi açıp kapatabilir. Kütüphanedeki request->authenticate() ve request->requestAuthentication() fonksiyonlarıyla sayfaya kullanıcı adı ve parola koyabilirsin; tarayıcı kendi giriş penceresini açar ve bilgiyi oturum boyunca hatırlar. Varsayılan yöntem Digest kimlik doğrulamadır, yani parola ağda açık metin olarak gitmez. Ancak yalnızca sayfayı korumak yetmez: WebSocket adresi ayrı bir kapıdır ve biri ws://ip/ws adresine doğrudan bağlanabilir. Aşağıdaki örnek bu kapıyı da kapatır: ESP32 her açılışta rastgele bir anahtar üretir, bu anahtarı yalnızca giriş yapmış tarayıcıya /anahtar adresinden verir ve setFilter() ile doğru anahtarı getirmeyen WebSocket bağlantılarını reddeder.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4
5const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
6const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
7const char* KULLANICI = "admin";     // panel kullanıcı adı / panel username
8const char* PAROLA = "Gizli-2026";   // panel parolası / panel password
9const int LED_PIN = 2;
10
11AsyncWebServer server(80);
12AsyncWebSocket ws("/ws");
13String wsAnahtar;                    // her açılışta yeni anahtar / a new key on every boot
14bool ledAcik = false;
15
16const char index_html[] PROGMEM = R"rawliteral(
17<!DOCTYPE html>
18<html><head><meta charset="utf-8">
19<meta name="viewport" content="width=device-width, initial-scale=1"></head>
20<body style="font-family:sans-serif;text-align:center">
21<h2>Korumali panel / Protected panel</h2>
22<button style="font-size:1.4em;padding:12px 32px" onclick="ws && ws.send('toggle')">LED</button>
23<p id="durum">...</p>
24<script>
25let ws;
26fetch('/anahtar').then(r => r.text()).then(k => {
27  ws = new WebSocket('ws://' + location.host + '/ws?k=' + k);
28  ws.onmessage = (e) => { document.getElementById('durum').textContent = e.data; };
29});
30</script>
31</body></html>
32)rawliteral";
33
34bool girisVar(AsyncWebServerRequest *request) {
35  return request->authenticate(KULLANICI, PAROLA);
36}
37
38void setup() {
39  Serial.begin(115200);
40  pinMode(LED_PIN, OUTPUT);
41
42  WiFi.mode(WIFI_STA);
43  WiFi.begin(ssid, password);
44  while (WiFi.status() != WL_CONNECTED) {
45    delay(500);
46  }
47  Serial.println(WiFi.localIP());
48
49  // Wi-Fi açıkken random() donanım üretecini kullanır / random() uses the hardware RNG
50  wsAnahtar = String(random(0x7FFFFFFF), HEX) + String(random(0x7FFFFFFF), HEX);
51
52  // Sayfa: tarayıcı kullanıcı adı ve parola sorar / page: the browser asks for login
53  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
54    if (!girisVar(request)) return request->requestAuthentication();
55    request->send(200, "text/html", index_html);
56  });
57
58  // Anahtar yalnız giriş yapana verilir / the key is given only after login
59  server.on("/anahtar", HTTP_GET, [](AsyncWebServerRequest *request) {
60    if (!girisVar(request)) return request->requestAuthentication();
61    request->send(200, "text/plain", wsAnahtar);
62  });
63
64  // Doğru anahtarı getirmeyen bağlantıyı reddet / reject sockets without the right key
65  ws.setFilter([](AsyncWebServerRequest *request) {
66    return request->hasParam("k") && request->getParam("k")->value() == wsAnahtar;
67  });
68
69  ws.onEvent([](AsyncWebSocket *sunucu, AsyncWebSocketClient *client, AwsEventType type,
70                void *arg, uint8_t *data, size_t len) {
71    if (type == WS_EVT_DATA && len == 6 && memcmp(data, "toggle", 6) == 0) {
72      ledAcik = !ledAcik;
73      digitalWrite(LED_PIN, ledAcik ? HIGH : LOW);
74      sunucu->textAll(ledAcik ? "LED: ON" : "LED: OFF");
75    }
76  });
77
78  server.addHandler(&ws);
79  server.begin();
80}
81
82void loop() {
83  ws.cleanupClients();               // saniyede bir yeterli / once per second is enough
84  delay(1000);
85}
Bu koruma ev ağı için yeterli bir kilittir ama şifreleme değildir: ESPAsyncWebServer ESP32'de HTTPS sunmaz ve WebSocket mesajları açık metin olarak gider. Parola kodun içinde durduğu için kodu paylaşırken silmeyi unutma. Modeminde port yönlendirme yapıp paneli internete açma; dışarıdan kontrol için ESP32 Telegram bot, MQTT ve Home Assistant ya da modemindeki VPN özelliği çok daha güvenlidir. Yeni kütüphane sürümlerinde aynı işi AsyncAuthenticationMiddleware sınıfıyla da yapabilirsin.

ESP8266 ve Raspberry Pi Pico W için notlar

Aynı kütüphane ESP8266 (NodeMCU, Wemos D1 mini) üzerinde de çalışır. Değiştirmen gerekenler şunlar: Async TCP yerine ESP32Async'in ESP Async TCP kütüphanesini kur; WiFi.h yerine ESP8266WiFi.h, AsyncTCP.h yerine ESPAsyncTCP.h, ESPmDNS.h yerine ESP8266mDNS.h ekle ve loop() içine MDNS.update() satırını koy. WiFi.setSleep(false) satırını WiFi.setSleepMode(WIFI_NONE_SLEEP) ile değiştir, pinleri de bağlantı tablosundaki NodeMCU sütununa göre ayarla. ESP8266'nın kullanılabilir RAM'i 80 KB civarında olduğu için sayfayı küçük tut ve istemci sınırının 4 olduğunu unutma. Raspberry Pi Pico W kullanıyorsan MicroPython ile web sunucu kurmayı Raspberry Pi Pico W Wi-Fi rehberinde bulabilirsin; ESPAsyncWebServer'ın yeni sürümleri RP2040 ve RP2350 yongalarını da Arduino tarafında destekler.

ESP32 web sunucusu ile proje fikirleri

  • Çok kanallı akıllı priz: 4 kanallı bir röle kartıyla lamba, fan ve priz için ayrı düğmeler yap; komutları "r1", "r2" gibi kısa metinlerle ayır. Röle seçimi için röle modülü yazısına bak.
  • Bitki sulama paneli: Toprak nem sensörü değerini canlı göster, sulama eşiğini sayfadaki kaydırıcıyla ayarla ve pompayı istersen elle de çalıştır.
  • Hava istasyonu: DHT22 yerine BME280 kullanıp basıncı da ekle; son 60 ölçümü sayfada küçük bir çizgi grafikle göster.
  • Renk seçici lamba: Sayfadaki renk seçicinin değerini WebSocket ile gönder ve WS2812B NeoPixel şeridini anında boya.
  • Kaydırıcıyla servo: Kaydırıcının değerini her harekette gönderip bir servo motoru çevir; kamera yönlendirme ya da kapı mandalı için idealdir.
  • Zaman damgalı kayıt: Ölçümlerin yanına NTP ile alınan saati ekle ve kodu her seferinde USB ile değil, OTA ile kablosuz güncelle.

Sık hatalar ve çözümleri

BelirtiSebepÇözüm
'mbedtls_md5_starts_ret' was not declared in this scopeEski me-no-dev kütüphanesi ve ESP32 çekirdeği 3.xESP32Async'in ESP Async WebServer ve Async TCP sürümlerini kur, eski klasörleri sil
Multiple libraries were found for "AsyncTCP.h"Aynı kütüphanenin iki kopyası varlibraries klasöründeki eski -master kopyalarını sil
send_P için deprecated uyarısıYeni sürümde send_P kaldırılıyorrequest->send(200, "text/html", index_html) kullan
Sayfa hiç açılmıyorTelefon misafir ağında ya da mobil veride, IP adresi değişmişAynı Wi-Fi'ye bağlan, Seri Monitör'deki güncel IP'yi kullan
Sayfa açılıyor ama değerler -- kalıyorWebSocket bağlanamadı: yol farklı ya da addHandler unutulmuşF12 konsolunu aç, ws("/ws") ile JavaScript'teki adresi eşitle
Task watchdog got triggered ve async_tcpCallback içinde delay(), uzun döngü ya da HTTP isteğiCallback'i kısa tut, işi bayrakla loop()'a taşı
Too many messages queuedtextAll() çok sık çağrılıyorYalnızca değişince ya da saniyede birkaç kez gönder
LittleFS'teki sayfa 404 veriyordata klasörü yüklenmemiş ya da setDefaultFile eksikEklentiyle dosyaları yükle, setDefaultFile("index.html") ekle
Seri Monitör'de does not exist, no permits for creationİstenen dosya LittleFS'te yokDosya adını ve büyük-küçük harfleri kontrol et; .gz geçen satırlar zararsızdır
Röle hiç kapanmıyor5 V modül 3,3 V HIGH sinyalini tanımıyor3,3 V uyumlu modül, JD-VCC ya da transistör kullan
Röle çekince ESP32 resetleniyorBobin akımı beslemeyi çökertiyor (brownout)Sağlam bir 5 V adaptör ve 470 µF kondansatör kullan
esp32.local açılmıyorCihazın mDNS desteği yok ya da ad .local ile yazılmışMDNS.begin("esp32") yaz, gerekirse IP adresini kullan
Düğme geç tepki veriyorWi-Fi modem uykusu açıkBağlantıdan sonra WiFi.setSleep(false) çağır
Bir şey çalışmadığında tarayıcıda F12 ile geliştirici araçlarını aç. Console sekmesi WebSocket connection to 'ws://192.168.1.50/ws' failed gibi hataları gösterir; Network sekmesinde WebSocket bağlantısına tıklarsan Messages bölümünde gelip giden her mesajı görürsün. ESP32 tarafında bağlanan ve ayrılan istemcileri Seri Monitör'e yazdırmak, sorunun tarayıcıda mı kartta mı olduğunu birkaç saniyede ayırır. Röle çektiği anda kart yeniden başlıyorsa ESP32 brownout rehberine bak.

ESP32 async web server ve WebSocket ikilisi, bulut hizmetine ya da telefon uygulaması yazmaya gerek kalmadan çalışan, hızlı ve birden çok cihazla paylaşılabilen bir kontrol paneli kurmanın en kısa yoludur. Önce tek dosyalık PROGMEM paneliyle başla, arayüz büyüdükçe LittleFS'e geç, ağına yeni cihazlar ekledikçe her birine kendi mDNS adını ver. ESP32 ile yeni tanışıyorsan temelleri ESP32 Nedir? yazısında bulabilirsin. BlueGrays uygulamasında da async web server, WebSocket sayacı, mDNS ile erişim, NTP saati ve OTA güncelleme gibi ESP32 projelerini devre şemaları ve hazır kodlarıyla adım adım inceleyebilirsin.

Sıkça sorulan sorular

ESP32 AsyncWebServer ile WebServer arasındaki fark nedir?

WebServer kütüphanesi istekleri loop() içindeki handleClient() çağrısıyla sırayla işler; loop() yavaşlarsa sayfa da yavaşlar. ESPAsyncWebServer ise istekleri arka plandaki ayrı bir görevde işler, aynı anda birden çok bağlantıyı kaldırır ve WebSocket ile SSE desteğini hazır getirir. Gerçek zamanlı paneller için asenkron sürüm daha uygundur.

ESP32 web sunucusuna aynı anda kaç cihaz bağlanabilir?

ESPAsyncWebServer, ESP32'de varsayılan olarak 8 WebSocket istemcisine izin verir ve cleanupClients() çağrıldığında fazlasını kapatır; ESP8266'da bu sınır 4'tür. Normal sayfa istekleri kısa sürdüğü için daha fazla cihaz sırayla sayfa açabilir. Pratikte bir ev panelinde 3–5 eşzamanlı cihaz sorunsuz çalışır.

ESP32 web sunucusuna internetten erişebilir miyim?

Modemde port yönlendirme yaparak teknik olarak mümkündür, ama önerilmez: kütüphane ESP32'de HTTPS sunmaz ve cihaz internetten gelen saldırılara açık kalır. Dışarıdan kontrol için Telegram bot, MQTT tabanlı bir sistem ya da modemindeki VPN özelliği çok daha güvenlidir.

esp32.local adresi neden açılmıyor?

En sık sebep, telefonun ya da bilgisayarın mDNS adlarını çözememesidir; bu durum özellikle bazı Android telefonlarda ve eski Windows sürümlerinde görülür. Kodda MDNS.begin("esp32.local") yazmak da hatadır, adı .local olmadan vermelisin. Ad yine çözülmüyorsa Seri Monitör'deki IP adresini kullan ya da modemden ESP32'ye sabit bir IP ayır.

ESP32 web sunucusu modem olmadan çalışır mı?

Evet. WiFi.softAP("ESP32-Panel", "12345678") ile ESP32 kendi Wi-Fi ağını kurar; telefonunu bu ağa bağlayıp tarayıcıda 192.168.4.1 adresini açarsın. Bu erişim noktası modunda internet yoktur, ama panel ve WebSocket aynen çalışır. Ağ şifresi en az 8 karakter olmalıdır.

ESPAsyncWebServer için hangi kütüphaneyi kurmalıyım?

Arduino Kütüphane Yöneticisi'nde yazarı ESP32Async olan ESP Async WebServer ve Async TCP kütüphanelerini kur. me-no-dev hesabındaki orijinal sürüm artık güncellenmiyor ve ESP32 çekirdeği 3.x ile derlenmiyor. ZIP ile eklenmiş eski kopyaları da libraries klasöründen sil.

Tüm projeleri gör
ESP32 Async Web Server (LCD'de IP Göstergeli)
#129İleri

ESP32 Async Web Server (LCD'de IP Göstergeli)

Bu projede ESP32'yi aynı Wi-Fi ağındaki telefonunuzdan veya bilgisayarınızdan kontrol edebileceğiniz asenkron bir web sunucusuna dönüştürüyoruz. ESPAsyncWebServer kütüphanesi sayesinde sunucu birden fazla isteği aynı anda işleyebiliyor (klasik WebServer.h gibi tek istekte bloklamıyor). Tarayıcıdan ESP32'nin IP adresine bağlanıp ON/OFF butonlarına basarak (/on ve /off adresleri) GPIO 5'e bağlı LED'i açıp kapatabileceksiniz.

4.8(16)
26
ESP32 WebSocket Sayaç (2 Buton + LCD + Web Realtime)
#130İleri
Premium

ESP32 WebSocket Sayaç (2 Buton + LCD + Web Realtime)

Bu projede ESP32'ye bağlı iki butonla (GPIO 5 artırır, GPIO 4 azaltır) bir sayaç değiştirilir. Değer hem 16x2 I2C LCD'de hem de ESPAsyncWebServer'ın WebSocket desteğiyle bağlı tüm tarayıcılarda anında güncellenir; sayfayı yenilemek gerekmez. HTTP polling yerine tek bir kalıcı bağlantı kullanılır ve ESP32 yeni değeri ws.textAll() ile herkese gönderir.

4.5(14)
23
ESP32 mDNS: bluegrays-esp.local ile Cihaza Erişim (LCD Ekranlı)
#131İleri
Premium

ESP32 mDNS: bluegrays-esp.local ile Cihaza Erişim (LCD Ekranlı)

Bu projede ESP32'ye mDNS (Multicast DNS) servisi ekleyerek IP adresi yerine bluegrays-esp.local gibi okunabilir bir host adıyla erişim sağlıyoruz. DHCP'nin verdiği IP değişse bile cihaza her zaman aynı isimle ulaşabilirsiniz. Üzerinde küçük bir web server da çalıştırarak tarayıcıdan http://bluegrays-esp.local/ adresiyle LED kontrolü gönderiyoruz. I2C LCD ekranda hostname, IP ve LED durumu hem Türkçe hem İngilizce gösterilir. iOS, macOS ve Linux mDNS'i doğal olarak destekler; Windows'ta Bonjo

4.8(14)
23
ESP32 Wi-Fi STA Bağlantısı (LCD Ekranlı)
#127İleri

ESP32 Wi-Fi STA Bağlantısı (LCD Ekranlı)

Bu projede ESP32'yi Station (STA) modunda evdeki Wi-Fi ağına güvenli ve hata toleranslı şekilde bağlıyoruz. SSID/şifreyi secrets.h dosyasına ayırarak Git'e sızmasını önlüyor, otomatik yeniden bağlanma mekanizması kuruyor, RSSI sinyal seviyesini ve IP adresini I2C LCD ekranda gösteriyoruz. Dahili LED durum göstergesi olarak çalışır. Üretim seviyesi bir bağlantı yöneticisidir.

4.4(21)
34
ESP32 DHT22 Sıcaklık ve Nem (LCD Ekranlı)
#117Orta

ESP32 DHT22 Sıcaklık ve Nem (LCD Ekranlı)

DHT22 sensörü ile ortam sıcaklığını ve nem oranını ölçüp 16x2 I2C LCD ekranda gerçek zamanlı olarak gösteren bir projedir. Adafruit DHT kütüphanesi kullanılarak tek kablo (one-wire) protokolü ile sensörden veri okunur. Bu proje, IoT hava istasyonu, akıllı ev otomasyonu veya seralarda ortam takip sistemlerinin temelini oluşturur. ESP32'nin GPIO 4 pini üzerinden DHT22 ile haberleşme sağlanır.

4.8(38)
56
ESP32 Akıllı Aydınlatma (PIR + LDR + Röle + OLED)
#141İleri
Premium

ESP32 Akıllı Aydınlatma (PIR + LDR + Röle + OLED)

ESP32, PIR hareket sensörü, LDR ışık sensörü ve röle modülü ile evinin koridorunu, tuvaletini veya kilerini akıllı aydınlatmaya dönüştürüyoruz. Klasik "karanlıkta hareket algılanınca ışık yansın" senaryosunu inşa ediyoruz — ortam aydınlıksa hareket olsa bile ışık yanmaz, böylece gündüz gereksiz yere yanmaz ve enerji tasarrufu sağlanır. Işık 30 saniye yanık kalır; hareket devam ederse timer otomatik sıfırlanır ve süre uzar. SSD1306 OLED ekranda büyük fontlarla durum (AÇIK / GÜNDÜZ / BEKLE), geri

4.3(6)
11
ESP32 OTA Update — Kablosuz Firmware Yükleme (LCD Ekranlı)
#132İleri
Premium

ESP32 OTA Update — Kablosuz Firmware Yükleme (LCD Ekranlı)

Bu projede ESP32'ye USB kablosu olmadan, Wi-Fi üzerinden yeni firmware yüklemeyi öğreneceksin. ArduinoOTA kütüphanesi ile ESP32 ağda görünür hale gelir ve Arduino IDE'nin "Network port" sekmesinde bluegrays-ota olarak otomatik listelenir. İlk yükleme USB ile yapılır, sonrasında tüm güncellemeler kablosuz olur — saha cihazları için çok kritik bir özelliktir. I2C LCD ekranda WiFi durumu, IP, hostname, OTA yükleme yüzdesi ve hata mesajları hem Türkçe hem İngilizce gösterilir. Parola koruması, h

4.6(14)
22
ESP32 NTP ile İnternetten Gerçek Zaman
#128İleri
Premium

ESP32 NTP ile İnternetten Gerçek Zaman

ESP32, Wi-Fi'a bağlanıp NTP (Network Time Protocol) sunucularından milisaniye hassasiyetiyle gerçek zamanı çeker. configTzTime() fonksiyonu sayesinde Türkiye saat dilimi (TRT, UTC+3) otomatik uygulanır ve yaz/kış saati uygulayan ülkelerde DST geçişi POSIX TZ string'i ile otomatik yönetilir. Saat hem Serial Monitor'e hem de opsiyonel I2C LCD'ye yazdırılır. configTzTime() SNTP hizmetini başlatır; ağ erişimi varken periyodik yeniden senkronizasyon yapılır ve iki senkronizasyon arasında sistem s

4.5(13)
21