Firebase Cloud Functions ile Sunucusuz Otomasyon Mantığı
Proje Amacı
ESP32'nin sadece ham sensör verisini (ışık seviyesi) HTTPS ile Firebase Realtime Database'e yazıp, 'ışık şu eşiğin altına düşerse röleyi aç' gibi karar mantığını cihaz üzerinde değil, bulutta çalışan bir Firebase Cloud Function'da (sunucusuz/serverless) tutmayı öğretir — cihaz koduna hiç dokunmadan bulut tarafındaki otomasyon kuralını değiştirebilme esnekliği sunar. ESP32'nin donanımsal kripto hızlandırması ve yeterli RAM'i, Firebase'in gerektirdiği TLS/HTTPS bağlantısını ve JSON işlemeyi sorunsuz yürütür. Bu tür bir bulut entegrasyonu Arduino Uno'da kurulamaz: WiFi yoktur; olsaydı bile TLS handshake için gereken kripto işlemi ve JSON ayrıştırma belleği 2KB SRAM'e hiç sığmaz.
Gerekli Malzemeler
- ESP32 DevKit V1 (38 pin)
- LDR (fotodirenç)
- 10kΩ direnç (gerilim bölücü)
- Röle modülü (5V, optokuplörlü)
- Breadboard
- Jumper kablolar
Gerekli Kütüphaneler
Firebase ESP Client by mobizt (Library Manager'dan kurun, sürüm >= 4.4 — 'FirebaseESP32' adlı eski/farklı bir kütüphaneyle karıştırmayın, API'leri uyumsuzdur).
Pin Bağlantıları
| Bileşen Pini | Bağlantı |
|---|---|
| LDR uç 1 | 3V3 |
| LDR uç 2 (10kΩ ile GND, ortak nokta ADC) | GPIO34Sadece giriş |
| Röle IN | GPIO4 |
| Röle VCC | 5V (VIN) |
| Röle GND | GND |
Devre Şeması
ESP32 Kodu
| 1 | #include <WiFi.h> |
| 2 | #include <Firebase_ESP_Client.h> |
| 3 | |
| 4 | #define LDR_PIN 34 |
| 5 | #define ROLE_PIN 4 |
| 6 | |
| 7 | const char* ssid = "WIFI_ADINIZ"; |
| 8 | const char* password = "WIFI_SIFRENIZ"; |
| 9 | #define FIREBASE_HOST "PROJE-ADINIZ-default-rtdb.firebaseio.com" |
| 10 | #define FIREBASE_AUTH "FIREBASE_VERITABANI_GIZLI_ANAHTARI" |
| 11 | |
| 12 | FirebaseData firebaseVeri; |
| 13 | FirebaseAuth firebaseAuth; |
| 14 | FirebaseConfig firebaseConfig; |
| 15 | |
| 16 | void setup() { |
| 17 | Serial.begin(115200); |
| 18 | pinMode(ROLE_PIN, OUTPUT); |
| 19 | |
| 20 | WiFi.begin(ssid, password); |
| 21 | while (WiFi.status() != WL_CONNECTED) { delay(400); Serial.print("."); } |
| 22 | Serial.println("\nBağlandı: " + WiFi.localIP().toString()); |
| 23 | |
| 24 | firebaseConfig.host = FIREBASE_HOST; |
| 25 | firebaseConfig.signer.tokens.legacy_token = FIREBASE_AUTH; |
| 26 | Firebase.begin(&firebaseConfig, &firebaseAuth); |
| 27 | Firebase.reconnectWiFi(true); |
| 28 | |
| 29 | Serial.println("Firebase'e bağlanıldı. Karar mantığı BULUTTAKİ Cloud Function'da çalışacak."); |
| 30 | } |
| 31 | |
| 32 | void loop() { |
| 33 | int isikDegeri = analogRead(LDR_PIN); // 0-4095 (ESP32 12-bit ADC) |
| 34 | |
| 35 | // ESP32 SADECE ham veriyi yazar; "kararı" (röleyi aç/kapat) burada VERMEZ. |
| 36 | if (Firebase.RTDB.setInt(&firebaseVeri, "/sensorler/isik", isikDegeri)) { |
| 37 | Serial.printf("Firebase'e yazıldı: isik=%d\n", isikDegeri); |
| 38 | } else { |
| 39 | Serial.println("Firebase yazma hatası: " + firebaseVeri.errorReason()); |
| 40 | } |
| 41 | |
| 42 | // Cloud Function, "/sensorler/isik" değişimini dinleyip kendi kararını |
| 43 | // "/komutlar/role" yoluna yazar (örnek Cloud Function mantığı aşağıda |
| 44 | // yorum olarak verilmiştir). Cihaz sadece bu komut yolunu OKUR ve uygular. |
| 45 | bool roleKomutu = false; |
| 46 | if (Firebase.RTDB.getBool(&firebaseVeri, "/komutlar/role")) { |
| 47 | roleKomutu = firebaseVeri.boolData(); |
| 48 | digitalWrite(ROLE_PIN, roleKomutu ? HIGH : LOW); |
| 49 | } |
| 50 | |
| 51 | delay(3000); |
| 52 | } |
| 53 | |
| 54 | /* ---- Firebase Cloud Function örneği (bulut tarafında, cihaza YÜKLENMEZ) ---- |
| 55 | * // functions/index.js |
| 56 | * const functions = require("firebase-functions"); |
| 57 | * const admin = require("firebase-admin"); |
| 58 | * admin.initializeApp(); |
| 59 | * |
| 60 | * exports.isikKontrol = functions.database.ref("/sensorler/isik") |
| 61 | * .onWrite(async (degisiklik) => { |
| 62 | * const isik = degisiklik.after.val(); |
| 63 | * const roleAcik = isik < 800; // KARAR MANTIĞI BURADA, cihazda DEĞİL |
| 64 | * await admin.database().ref("/komutlar/role").set(roleAcik); |
| 65 | * }); |
| 66 | * ------------------------------------------------------------------------ */ |
Çalışma Açıklaması
Bu mimarinin can alıcı noktası şudur: cihaz kodu sadece veri okur/yazar, 'ışık 800'ün altına düşerse aç' gibi iş kuralı tamamen bulut tarafındaki Cloud Function'da yaşar — eşik değerini değiştirmek için cihaza yeniden kod yüklemeye (OTA'ya bile) gerek kalmaz, sadece bulut fonksiyonu güncellenir. legacy_token kimlik doğrulama en basit yöntemdir; üretimde Firebase'in önerdiği güvenli kimlik doğrulama (kullanıcı bazlı auth veya servis hesabı) tercih edilmelidir.
Test Bilgisi
Firebase konsolunda bir Realtime Database projesi oluşturup FIREBASE_HOST/FIREBASE_AUTH değerlerini girin. Kodu yükleyip Seri Monitör'de düzenli 'Firebase'e yazıldı' loglarını doğrulayın; Firebase konsolunda /sensorler/isik yolunun canlı güncellendiğini görün. Cloud Function'ı deploy edip (yorum içindeki örnek), LDR'yi karartıp /komutlar/role yolunun otomatik true olduğunu ve röleyi tetiklediğini doğrulayın; ardından fonksiyondaki eşik değerini değiştirip cihaza hiç dokunmadan davranışın değiştiğini gözlemleyin.
Olası Sorunlar ve Çözümleri
'Firebase yazma hatası: permission denied' alıyorsanız: Firebase Realtime Database kurallarınızın (Rules) legacy_token veya kullandığınız kimlik doğrulama yöntemiyle yazmaya izin verdiğinden emin olun, varsayılan kurallar genelde tüm erişimi reddeder. Bağlantı sürekli kopuyorsa: Firebase.reconnectWiFi(true) satırının setup()'ta çağrıldığından emin olun. Cloud Function tetiklenmiyorsa: fonksiyonun gerçekten deploy edildiğini (firebase deploy --only functions) ve veritabanı yolunun (/sensorler/isik) kod ile Cloud Function'da birebir aynı yazıldığını kontrol edin.
Alternatif Çözümler
Realtime Database yerine Firestore kullanılarak daha yapılandırılmış ve sorgulanabilir bir veri modeli kurulabilir. Tek bir sabit eşik yerine, Cloud Function içinde zamana göre değişen (gece/gündüz farklı eşik) daha karmaşık bir mantık eklenebilir — cihaz kodu hiç değişmeden bu esneklik kazanılır. Firebase Cloud Messaging (FCM) eklenerek röle her tetiklendiğinde kullanıcının telefonuna anlık bildirim gönderilebilir.
Benzer Projeler
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.
SD Kart + Bulut Senkronizasyonlu Kesintisiz Veri Kayıt Sistemi
Sensör verisini sürekli olarak SPI üzerinden bir SD karta yazıp, WiFi bağlantısı müsait olduğunda kaydedilmiş verileri toplu halde buluta senkronize etmeyi öğretir — bağlantı kesilse bile veri kaybı olmadan çalışan, sahadaki gerçek IoT sistemlerinde aranan bir kayıt mimarisidir. ESP32'nin iki çekirdeği, bir tarafta SD karta yazma (SPI, göreceli yavaş) işlemini yaparken diğer tarafta WiFi/HTTP senkronizasyonunu eşzamanlı ve birbirini bloklamadan yürütebilir. Bu mimari Arduino Uno'da kurulamaz: SD kartı SPI ile teorik olarak bağlamak mümkün olsa da WiFi donanımı olmadığından buluta senkronizasyon yapılamaz; ayrıca 2KB RAM, SD dosya sistemi arabellekleriyle birlikte bir ağ yığınını aynı anda çalıştırmaya yetmez.
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.