ESP-NOW ile Kendini Onaran (Self-Healing) Mesh Ağı
Proje Amacı
ESP32'nin WiFi çipi üzerinde çalışan, yönlendiriciye (router) hiç ihtiyaç duymayan özel ve düşük gecikmeli P2P protokolü ESP-NOW ile, bir düğüm arızalanırsa diğer düğümlerin otomatik olarak birbirinin üzerinden veri yönlendirdiği (mesh) bir ağ kurmayı öğretir — router'ın menzili dışındaki alanlara veri ulaştırmak isteyen sensör ağları için pratik ve dayanıklı bir çözümdür. ESP-NOW, Espressif'in WiFi radyosuna gömülü, bağlantısız (connectionless) özel bir protokoldür ve her cihaz aynı anda hem alıcı hem verici (peer) olabilir. Bu iletişim yöntemi Arduino Uno'da kullanılamaz: ESP-NOW, ESP32/ESP8266'nın WiFi radyo çipine gömülü donanımsal bir özelliktir; Uno'da hiçbir radyo çipi bulunmadığından mesh kurmak bir yana, tek bir ESP-NOW paketi bile gönderilip alınamaz.
Gerekli Malzemeler
- 3x ESP32 DevKit V1 (38 pin) — mesh'in en az 3 düğümden oluşması gerekir
- Her düğüm için durum LED'i
- 220Ω direnç (her düğüm için)
- Breadboard
- Jumper kablolar
Gerekli Kütüphaneler
esp_now.h, WiFi.h (ikisi de ESP32 core ile gelir, ek kurulum gerekmez). ÖNEMLİ: ESP-NOW, WiFi kanalının tüm düğümlerde AYNI olmasını gerektirir.
Pin Bağlantıları
| Bileşen Pini | Bağlantı |
|---|---|
| Durum LED anot (220Ω, her düğümde) | GPIO4 |
| LED katot (her düğümde) | GND |
Devre Şeması
ESP32 Kodu
| 1 | #include <esp_now.h> |
| 2 | #include <WiFi.h> |
| 3 | |
| 4 | const int LED = 4; |
| 5 | |
| 6 | // Basit mesaj yapısı: her düğüm kendi ID'sini ve bir "canlı" sayacını yayınlar. |
| 7 | typedef struct { |
| 8 | uint8_t dugumId; |
| 9 | uint32_t sayac; |
| 10 | uint8_t atlamaSayisi; // kaç düğüm üzerinden geldi (hop count) |
| 11 | } MeshMesaj; |
| 12 | |
| 13 | uint8_t broadcastAdresi[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // tüm düğümlere yayın |
| 14 | const uint8_t BU_DUGUM_ID = 1; // her karta farklı bir ID (1, 2, 3...) yazılmalı |
| 15 | |
| 16 | uint32_t sonGorulenSayac[10] = {0}; // basit tekrar-önleme (duplicate) tablosu |
| 17 | |
| 18 | void veriAlindi(const esp_now_recv_info_t* bilgi, const uint8_t* veri, int uzunluk) { |
| 19 | MeshMesaj mesaj; |
| 20 | memcpy(&mesaj, veri, sizeof(mesaj)); |
| 21 | |
| 22 | // Aynı mesajı daha önce gördüysek yeniden yayınlama (döngüyü engeller) |
| 23 | if (mesaj.sayac <= sonGorulenSayac[mesaj.dugumId]) return; |
| 24 | sonGorulenSayac[mesaj.dugumId] = mesaj.sayac; |
| 25 | |
| 26 | Serial.printf("Düğüm %d'den mesaj alındı (sayaç %u, %d atlama)\n", |
| 27 | mesaj.dugumId, mesaj.sayac, mesaj.atlamaSayisi); |
| 28 | digitalWrite(LED, HIGH); delay(50); digitalWrite(LED, LOW); // görsel geri bildirim |
| 29 | |
| 30 | // KENDİNİ ONARMA: mesajı komşulara yeniden yayınla (atlama sayısını artırarak). |
| 31 | // Bu sayede kaynak düğüm ile hedef arasında doğrudan menzil olmasa bile, |
| 32 | // aradaki düğümler mesajı taşıyarak ağın "onarılmasını" sağlar. |
| 33 | if (mesaj.atlamaSayisi < 5) { // sonsuz döngüyü önlemek için max atlama sınırı |
| 34 | mesaj.atlamaSayisi++; |
| 35 | esp_now_send(broadcastAdresi, (uint8_t*)&mesaj, sizeof(mesaj)); |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | void setup() { |
| 40 | Serial.begin(115200); |
| 41 | pinMode(LED, OUTPUT); |
| 42 | |
| 43 | WiFi.mode(WIFI_STA); |
| 44 | WiFi.disconnect(); // ESP-NOW için WiFi'nin bir ağa bağlı OLMAMASI gerekir |
| 45 | |
| 46 | if (esp_now_init() != ESP_OK) { |
| 47 | Serial.println("ESP-NOW başlatılamadı!"); |
| 48 | return; |
| 49 | } |
| 50 | esp_now_register_recv_cb(veriAlindi); |
| 51 | |
| 52 | esp_now_peer_info_t peer = {}; |
| 53 | memcpy(peer.peer_addr, broadcastAdresi, 6); |
| 54 | peer.channel = 0; // mevcut WiFi kanalını kullan |
| 55 | peer.encrypt = false; |
| 56 | esp_now_add_peer(&peer); |
| 57 | |
| 58 | Serial.printf("ESP-NOW mesh hazır. Bu düğüm ID: %d\n", BU_DUGUM_ID); |
| 59 | } |
| 60 | |
| 61 | void loop() { |
| 62 | static uint32_t sayac = 0; |
| 63 | MeshMesaj mesaj = { BU_DUGUM_ID, ++sayac, 0 }; |
| 64 | esp_now_send(broadcastAdresi, (uint8_t*)&mesaj, sizeof(mesaj)); |
| 65 | Serial.printf("Yayınlandı: düğüm %d, sayaç %u\n", BU_DUGUM_ID, sayac); |
| 66 | delay(3000); |
| 67 | } |
Çalışma Açıklaması
ESP-NOW'da WiFi.disconnect() önemlidir — cihaz bir router'a bağlıyken de ESP-NOW çalışabilir ama kanal uyuşmazlığı sorunlarını önlemek için bu örnekte bağımsız STA modu tercih edilmiştir. sonGorulenSayac dizisi basit bir tekrar-önleme mekanizmasıdır; gerçek üretimde 10'dan fazla düğüm için dinamik bir yapı (map/dictionary benzeri) kullanılmalıdır, aksi halde dizi boyutu düğüm sayısıyla birlikte manuel olarak büyütülmelidir.
Test Bilgisi
3 ayrı ESP32'ye aynı kodu, sadece BU_DUGUM_ID değerini (1, 2, 3) değiştirerek yükleyin. Hepsini açıp Seri Monitör'lerini yan yana izleyin; her düğüm diğerlerinden gelen mesajları görmeli. Düğüm 1'i kapatıp Düğüm 2 ve 3'ün birbirini görmeye devam ettiğini doğrulayın (ağ kendini onarmış olur). Düğüm 1 ile Düğüm 3'ü fiziksel olarak birbirinden uzaklaştırıp aradaki Düğüm 2'nin mesajı taşıdığını (atlama sayısı 1'den büyük mesajlarla) gözlemleyerek mesh'in gerçekten çalıştığını kanıtlayın.
Olası Sorunlar ve Çözümleri
Düğümler birbirini hiç görmüyorsa: hepsinin aynı WiFi kanalında olduğundan emin olun (varsayılan kanal, router'a bağlıyken WiFi.channel() ile kontrol edilebilir). 'esp_now_init başarısız' hatası alıyorsanız: WiFi.mode(WIFI_STA) çağrısının esp_now_init()'ten önce yapıldığından emin olun. Mesajlar sonsuz döngüye girip ağı tıkıyorsa: atlamaSayisi sınırının (bu örnekte 5) çok yüksek tutulmadığından ve sonGorulenSayac kontrolünün doğru çalıştığından emin olun, aksi halde ağ trafiği katlanarak artabilir.
Alternatif Çözümler
peer.encrypt = true ve esp_now_set_pmk() ile mesajlar şifrelenerek proje #12'deki gibi bir güvenlik katmanı eklenebilir. Basit broadcast yerine, her düğümün komşu listesini tutup en kısa yola göre yönlendirme yapan gerçek bir mesh protokolü (painlessMesh kütüphanesi) kullanılarak daha büyük ölçekli ağlar kurulabilir. ESP-NOW mesh'i bir 'gateway' düğüm üzerinden WiFi/MQTT'ye köprüleyerek proje #10'daki fog computing mimarisiyle birleştirmek, mesh içindeki verinin buluta da ulaşmasını sağlar.
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.
FreeRTOS Dual-Core Görev Paylaşımlı Veri Toplama
xTaskCreatePinnedToCore() ile iki farklı sensör okuma görevini ESP32'nin iki fiziksel çekirdeğine (Core 0 ve Core 1) sabitleyerek tamamen paralel çalıştırmayı öğretir — biri yavaş ve bloklayıcı DHT22'yi okurken diğeri hızlı analog LDR okumaya kesintisiz devam eder, ikisi birbirini beklemez. ESP32, iki bağımsız Xtensa LX6 çekirdeğine sahiptir; Arduino sketch'i normalde sadece Core 1'de çalışır, Core 0 ise bilinçli olarak görev atanarak devreye alınabilir. Bu paralellik Arduino Uno'da kurulamaz: ATmega328P tek çekirdekli 8-bit bir mikrodenetleyicidir, ikinci bir çekirdeğe görev atamak donanımsal olarak mümkün değildir ve kart FreeRTOS gibi bir RTOS da çalıştıramaz.
ESPAsyncWebServer ile Bloklamayan Çoklu İstemci REST API
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.