Fog Computing: Çoklu Sensör Düğümünden Veri Toplayan Ağ Geçidi
Proje Amacı
Birden fazla uzak sensör düğümünden ESP-NOW ile gelen ham verileri tek bir 'gateway' ESP32 üzerinde toplayıp, yerel olarak filtreleyip/özetleyerek (fog computing — buluta göndermeden önce kenar/yerel işleme yapmak), sonucu bir OLED ekranda göstermeyi ve toplu halde WiFi ile buluta iletmeyi öğretir. ESP32'nin hem ESP-NOW alıcısını hem WiFi istemcisini hem de I2C ekran sürücüsünü aynı anda, tek çip üzerinde çalıştırabilecek yeterli RAM'i ve iki çekirdeği vardır. Bu çok görevlilik Arduino Uno'da kurulamaz: ne ESP-NOW/WiFi radyosu ne de birden fazla eşzamanlı veri akışını (çoklu düğüm + ekran + bulut gönderimi) işleyecek 2KB RAM'i bulunur.
Gerekli Malzemeler
- ESP32 DevKit V1 (38 pin) — gateway
- SSD1306 0.96" I2C OLED ekran (128x64)
- Breadboard
- Jumper kablolar
- (Test için proje #09'daki gibi 2-3 adet ek ESP32 sensör düğümü)
Gerekli Kütüphaneler
esp_now.h, WiFi.h (ESP32 core ile gelir). Adafruit_SSD1306 (>= 2.5.x) + Adafruit_GFX (>= 1.11.x) OLED için.
Pin Bağlantıları
| Bileşen Pini | Bağlantı |
|---|---|
| OLED SDA | GPIO21 |
| OLED SCL | GPIO22 |
| OLED VCC | 3V3 |
| OLED GND | GND |
Devre Şeması
ESP32 Kodu
| 1 | #include <esp_now.h> |
| 2 | #include <WiFi.h> |
| 3 | #include <Wire.h> |
| 4 | #include <Adafruit_GFX.h> |
| 5 | #include <Adafruit_SSD1306.h> |
| 6 | |
| 7 | Adafruit_SSD1306 ekran(128, 64, &Wire, -1); |
| 8 | |
| 9 | typedef struct { |
| 10 | uint8_t dugumId; |
| 11 | float sicaklik; |
| 12 | } SensorMesaj; |
| 13 | |
| 14 | #define MAX_DUGUM 5 |
| 15 | float dugumSonDeger[MAX_DUGUM] = {0}; |
| 16 | bool dugumAktif[MAX_DUGUM] = {false}; |
| 17 | unsigned long dugumSonGorulme[MAX_DUGUM] = {0}; |
| 18 | |
| 19 | void veriAlindi(const esp_now_recv_info_t* bilgi, const uint8_t* veri, int uzunluk) { |
| 20 | SensorMesaj mesaj; |
| 21 | memcpy(&mesaj, veri, sizeof(mesaj)); |
| 22 | if (mesaj.dugumId < MAX_DUGUM) { |
| 23 | dugumSonDeger[mesaj.dugumId] = mesaj.sicaklik; |
| 24 | dugumAktif[mesaj.dugumId] = true; |
| 25 | dugumSonGorulme[mesaj.dugumId] = millis(); |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | void setup() { |
| 30 | Serial.begin(115200); |
| 31 | Wire.begin(21, 22); |
| 32 | ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C); |
| 33 | ekran.clearDisplay(); |
| 34 | ekran.setTextColor(SSD1306_WHITE); |
| 35 | |
| 36 | WiFi.mode(WIFI_STA); |
| 37 | esp_now_init(); |
| 38 | esp_now_register_recv_cb(veriAlindi); |
| 39 | |
| 40 | Serial.println("Fog gateway hazır, düğüm verisi bekleniyor..."); |
| 41 | } |
| 42 | |
| 43 | void loop() { |
| 44 | // 30 saniyedir veri gelmeyen düğümü "çevrimdışı" say (fog seviyesinde filtreleme) |
| 45 | for (int i = 0; i < MAX_DUGUM; i++) { |
| 46 | if (dugumAktif[i] && millis() - dugumSonGorulme[i] > 30000) dugumAktif[i] = false; |
| 47 | } |
| 48 | |
| 49 | // Yerelde özet çıkar: aktif düğümlerin ORTALAMASINI hesapla (fog computing) |
| 50 | float toplam = 0; int aktifSayi = 0; |
| 51 | ekran.clearDisplay(); |
| 52 | ekran.setCursor(0, 0); |
| 53 | ekran.setTextSize(1); |
| 54 | ekran.println("Fog Gateway - Dugumler:"); |
| 55 | for (int i = 0; i < MAX_DUGUM; i++) { |
| 56 | if (dugumAktif[i]) { |
| 57 | ekran.printf("D%d: %.1fC\n", i, dugumSonDeger[i]); |
| 58 | toplam += dugumSonDeger[i]; |
| 59 | aktifSayi++; |
| 60 | } |
| 61 | } |
| 62 | if (aktifSayi > 0) { |
| 63 | float ortalama = toplam / aktifSayi; |
| 64 | ekran.printf("Ort: %.1fC (%d dugum)\n", ortalama, aktifSayi); |
| 65 | Serial.printf("[Fog özet] Ortalama: %.1fC, aktif düğüm: %d\n", ortalama, aktifSayi); |
| 66 | // Burada gerçek projede sadece bu ÖZET (ham veri değil) buluta gönderilir, |
| 67 | // bant genişliği ve bulut işlem yükü büyük ölçüde azaltılır. |
| 68 | } else { |
| 69 | ekran.println("Aktif dugum yok."); |
| 70 | } |
| 71 | ekran.display(); |
| 72 | delay(2000); |
| 73 | } |
Çalışma Açıklaması
'Fog computing' burada somut olarak şu anlama gelir: ham veri (her düğümün her okuması) buluta gönderilmez, gateway yerel olarak filtreleme ve ortalama alma işini yapar, sadece özet veriyi iletir — bu hem bant genişliğinden hem bulut işlem maliyetinden ciddi tasarruf sağlar. Wire.begin(21,22) çağrısı GPIO21/22'yi açıkça I2C pini olarak ayarlar; bu ESP32 DevKit'te varsayılan I2C pinleridir ama farklı bir board kullanılıyorsa değişebilir, bu durumda kendi kartınızın pinout diyagramına bakmanız gerekir.
Test Bilgisi
Proje #09'daki ESP-NOW kodunu 2-3 ek ESP32'ye, mesaj yapısını SensorMesaj'a uyarlayarak (dugumId + sicaklik) yükleyin. Gateway'i açın; OLED ekranda her düğümün son sıcaklığı ve genel ortalama görünmeli. Bir düğümü kapatıp 30 saniye sonra ekrandan otomatik olarak düştüğünü (çevrimdışı filtrelemesi) doğrulayın, ardından düğümü tekrar açıp bir sonraki okumada listeye geri döndüğünü de gözlemleyin.
Olası Sorunlar ve Çözümleri
OLED'de hiçbir şey görünmüyorsa: I2C adresini (0x3C çoğu SSD1306 modülünde standarttır, bazılarında 0x3D) bir I2C tarayıcı sketch'iyle doğrulayın. 'esp_now_init başarısız' hatası: WiFi.mode(WIFI_STA) çağrısının önce yapıldığından emin olun. Düğümlerden veri hiç gelmiyorsa: gateway ve düğümlerin aynı WiFi kanalında olduğunu ve MeshMesaj/SensorMesaj yapılarının (struct alan sırası ve tipleri) birebir aynı olduğunu kontrol edin — ESP-NOW ham byte kopyaladığı için yapı uyuşmazlığı sessizce yanlış veri okunmasına yol açar ve hata mesajı vermez.
Alternatif Çözümler
Yerel özet WiFi ile bir MQTT broker'a veya proje #02'deki gibi TLS korumalı bir uca gönderilerek gerçek bulut entegrasyonu tamamlanabilir. Basit ortalama yerine, aykırı değer (outlier) tespiti veya hareketli ortalama gibi daha gelişmiş yerel analitik eklenerek gateway'in 'akıllılığı' artırılabilir. OLED ekrana ek olarak proje #06'daki ESPAsyncWebServer ile yerel ağdan görüntülenebilen bir web paneli eklenebilir, böylece durum sadece cihazın başına gidilerek değil telefonla da izlenebilir.
Benzer Projeler
WiFi Promiscuous Mode ile Cihaz Varlığı/MAC Analizi
ESP32'nin WiFi çipini 'promiscuous mode'a alarak, kendisine yönelik olmayan tüm WiFi paketlerini (yönetim/beacon çerçeveleri dahil) pasif olarak dinlemeyi ve ortamdaki telefon/laptop gibi cihazların MAC adreslerini ve sinyal gücünü (RSSI) tespit etmeyi öğretir — kalabalık yoğunluğu tahmini veya varlık algılama gibi gerçek uygulamalarda kullanılan bir tekniktir. ESP32'nin WiFi baseband çipi, düşük seviyeli esp_wifi_set_promiscuous() API'siyle normalde işletim sisteminin gizlediği ham 802.11 çerçevelerine erişim sağlar. Bu erişim Arduino Uno'da mümkün değildir: kartın dinleyecek hiçbir WiFi radyosu yoktur.
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.