ESPAsyncWebServer ile Bloklamayan Çoklu İstemci REST API
Proje Amacı
Birden fazla tarayıcı veya istemcinin aynı anda, birbirini bloklamadan bağlanıp iki rölenin durumunu okuyup değiştirebildiği asenkron (event-driven) bir REST API sunmayı öğretir — akıllı ev panelleri ve endüstriyel kontrol arayüzlerinde tercih edilen mimarinin küçük ölçekli bir örneğidir. ESPAsyncWebServer, gelen her isteği ayrı ayrı bekletmeden (non-blocking) LwIP ağ yığınıyla doğrudan çalışır; bunun için ESP32'nin ikinci çekirdeği ve yeterli RAM'i (520KB SRAM) devreye girer. Bu mimari Arduino Uno'da kurulamaz: WiFi donanımı yoktur; harici bir WiFi shield eklense bile klasik senkron 'WiFiServer' modeli her isteği sırayla işler ve 2KB RAM, birden fazla eşzamanlı bağlantı arabelleğini aynı anda tutmaya yetmez.
Gerekli Malzemeler
- ESP32 DevKit V1 (38 pin)
- 2x Röle modülü (5V, optokuplörlü)
- Breadboard
- Jumper kablolar
- Harici 5V güç kaynağı (röle bobinleri için önerilir)
Gerekli Kütüphaneler
ESPAsyncWebServer (me-no-dev, GitHub'dan zip olarak kurulmalı — Library Manager'da yok) + AsyncTCP (me-no-dev, aynı şekilde GitHub'dan). ArduinoJson (>= 6.x) JSON yanıtları için.
Pin Bağlantıları
| Bileşen Pini | Bağlantı |
|---|---|
| Röle-1 IN | GPIO4 |
| Röle-2 IN | GPIO16 |
| Röle VCC | 5V (VIN, harici kaynak önerilir) |
| Röle GND | GND |
Devre Şeması
ESP32 Kodu
| 1 | #include <WiFi.h> |
| 2 | #include <ESPAsyncWebServer.h> |
| 3 | #include <ArduinoJson.h> |
| 4 | |
| 5 | const char* ssid = "WIFI_ADINIZ"; |
| 6 | const char* password = "WIFI_SIFRENIZ"; |
| 7 | const int ROLE1 = 4, ROLE2 = 16; |
| 8 | |
| 9 | bool role1Durum = false, role2Durum = false; |
| 10 | |
| 11 | AsyncWebServer server(80); |
| 12 | |
| 13 | void jsonDurumGonder(AsyncWebServerRequest* istek) { |
| 14 | StaticJsonDocument<128> doc; |
| 15 | doc["role1"] = role1Durum; |
| 16 | doc["role2"] = role2Durum; |
| 17 | String cikti; |
| 18 | serializeJson(doc, cikti); |
| 19 | istek->send(200, "application/json", cikti); |
| 20 | } |
| 21 | |
| 22 | void setup() { |
| 23 | Serial.begin(115200); |
| 24 | pinMode(ROLE1, OUTPUT); |
| 25 | pinMode(ROLE2, OUTPUT); |
| 26 | digitalWrite(ROLE1, LOW); |
| 27 | digitalWrite(ROLE2, LOW); |
| 28 | |
| 29 | WiFi.begin(ssid, password); |
| 30 | while (WiFi.status() != WL_CONNECTED) { delay(400); Serial.print("."); } |
| 31 | Serial.println("\nBağlandı: " + WiFi.localIP().toString()); |
| 32 | |
| 33 | // GET /durum -> mevcut röle durumlarını JSON olarak döner |
| 34 | server.on("/durum", HTTP_GET, [](AsyncWebServerRequest* istek) { |
| 35 | jsonDurumGonder(istek); |
| 36 | }); |
| 37 | |
| 38 | // POST /role/<no>/<on|off> -> ilgili röleyi anında değiştirir, bloklamaz |
| 39 | server.on("^\\/role\\/([1-2])\\/(on|off)$", HTTP_POST, [](AsyncWebServerRequest* istek) { |
| 40 | int roleNo = istek->pathArg(0).toInt(); |
| 41 | bool acik = istek->pathArg(1) == "on"; |
| 42 | if (roleNo == 1) { role1Durum = acik; digitalWrite(ROLE1, acik ? HIGH : LOW); } |
| 43 | else { role2Durum = acik; digitalWrite(ROLE2, acik ? HIGH : LOW); } |
| 44 | jsonDurumGonder(istek); |
| 45 | }); |
| 46 | |
| 47 | server.onNotFound([](AsyncWebServerRequest* istek) { |
| 48 | istek->send(404, "application/json", "{\"hata\":\"bulunamadi\"}"); |
| 49 | }); |
| 50 | |
| 51 | server.begin(); // async sunucu ayrı bir görevde çalışır, loop()'u BLOKLAMAZ |
| 52 | } |
| 53 | |
| 54 | void loop() { |
| 55 | // ESPAsyncWebServer kendi görevinde çalıştığı için loop() tamamen boş |
| 56 | // kalabilir veya başka işler (sensör okuma vb.) için kullanılabilir. |
| 57 | delay(1000); |
| 58 | } |
Çalışma Açıklaması
ESPAsyncWebServer callback'leri kendi async görev bağlamında çalışır — bu fonksiyonlar içinde delay() kullanmak asenkron modelin tüm amacını bozar ve kesinlikle kullanılmamalıdır. pathArg() ile regex tabanlı route (^\\/role\\/([1-2])\\/(on|off)$) kullanılmıştır; kütüphane sürümüne göre regex desteği farklılık gösterebilir, uyumsuzluk yaşanırsa iki ayrı sabit route (/role/1/on, /role/1/off vb.) tanımlamak daha güvenilir bir yoldur.
Test Bilgisi
Kodu yükleyin, Seri Monitör'den IP adresini alın. Aynı anda 2-3 farklı sekmeden veya cihazdan curl ya da tarayıcıyla 'curl -X POST http://<IP>/role/1/on' ve 'curl http://<IP>/durum' isteklerini art arda gönderin; hiçbiri diğerini beklemeden anında yanıt vermeli — senkron bir sunucuda bu istekler sıraya girip gözle görülür şekilde gecikirdi, bu farkı iki mimariyi ayrı ayrı deneyerek gözlemleyebilirsiniz.
Olası Sorunlar ve Çözümleri
Derleme hatası 'AsyncWebServer.h bulunamadı' alıyorsanız: bu kütüphane Arduino Library Manager'da bulunmaz, GitHub'dan (ESP32Async/ESPAsyncWebServer ve ESP32Async/AsyncTCP) zip indirip 'Include Library > Add .ZIP Library' yoluyla kurmalısınız. Röleler rastgele tetikleniyorsa: harici 5V kaynağın GND'sinin ESP32 GND'siyle ortak olduğundan emin olun, ortak toprak (common ground) bu tür karma güç kaynaklı devrelerde şarttır. Bellek zamanla azalıp cihaz donuyorsa (memory leak): her AsyncWebServerRequest callback'inde mutlaka istek->send() çağrıldığından emin olun, yanıt göndermeyen bir route zamanla bellek sızıntısına yol açar.
Alternatif Çözümler
JSON gövdeli POST istekleri (body) almak için AsyncCallbackJsonWebHandler eklenebilir, böylece query/path parametreleri yerine gerçek bir REST body kullanılır — daha standart ve genişletilebilir bir API tasarımı sağlar. Kimlik doğrulama için server.on() içine Basic Auth (istek->authenticate()) eklenerek yetkisiz erişim engellenebilir. WebSocket (AsyncWebSocket, proje #19'daki gibi) eklenerek durum değişikliklerinin istemcilere anlık push edilmesi sağlanabilir, böylece istemcilerin periyodik olarak /durum'u sorgulaması (polling) gerekmez.
Benzer Projeler
Şifreli BLE ile Kimlik Doğrulamalı Akıllı Kilit
BLE (Bluetooth Low Energy) bonding/pairing ile bir telefonun kimliğini bir kez eşleştirip, sonraki bağlantılarda ESP32'nin donanımsal AES şifrelemesiyle korunan bir kanaldan 'kilidi aç' komutunu doğrulamayı ve bir servo ile fiziksel kilidi açmayı öğretir. ESP32'nin dahili BLE radyosu, Secure Connections (LE Secure Connections) ile şifreli eşleştirme yapabilecek donanımsal kripto desteğine sahiptir. Bu güvenlik düzeyi Arduino Uno'da kurulamaz: kart hiçbir Bluetooth radyosu (Classic veya BLE) içermez ve kripto hızlandırma donanımı da yoktur; harici bir BLE modülü (HM-10 gibi) eklense bile bu modüller genelde bonding/güvenli eşleştirmeyi tam desteklemez.
Fauxmo ile Alexa/Google Home Sesli Kontrol Entegrasyonu
ESP32'yi, Amazon Alexa veya Google Home'un zaten tanıdığı bir Philips Hue/Belkin Wemo akıllı priz gibi taklit ederek (fauxmoESP kütüphanesiyle UPnP/SSDP protokolü üzerinden), herhangi bir bulut hesabı veya geliştirici kaydı olmadan 'Alexa, lambayı aç' gibi sesli komutlarla röle kontrolü yapmayı öğretir. ESP32'nin WiFi ağ yığını, UDP multicast (SSDP keşif) ve yerel bir HTTP sunucusunu aynı anda çalıştırabilecek yeterli RAM ve ağ desteğine sahiptir. Bu senaryo Arduino Uno'da kurulamaz: WiFi donanımı olmadığından ne UDP multicast dinleyebilir ne de Alexa'nın yerel ağ taramasına yanıt verebilecek bir HTTP sunucusu çalıştırabilir.
ESP32-CAM ile Edge AI Yüz/Nesne Tanıma (ESP-WHO)
Kameradan alınan görüntü üzerinde, veriyi buluta göndermeden doğrudan cihaz üzerinde (edge AI) yüz tespiti yapmayı öğretir; Espressif'in ESP-WHO/esp-dl çerçevesi, kamera modülünün PSRAM'ini kullanarak görüntü karelerini tamponlayıp önceden eğitilmiş küçük bir sinir ağı modelini gerçek zamanlı çalıştırır. ESP32-CAM'in özel kamera arayüzü (DVP) ve harici PSRAM çipi, görüntü karelerini ve model ağırlıklarını aynı anda bellekte tutabilecek kapasiteyi sağlar. Bu tür bir işlem Arduino Uno'da gerçekleştirilemez: kamera bağlamak için gereken paralel DVP arayüzü hiç yoktur, PSRAM'i yoktur ve 2KB dahili RAM'i tek bir düşük çözünürlüklü görüntü karesini bile tutmaya yetmez — bir yapay sinir ağı modelini çalıştırmak ise bambaşka bir kapasite gerektirir.