FreeRTOS Dual-Core Görev Paylaşımlı Veri Toplama
Proje Amacı
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.
Gerekli Malzemeler
- ESP32 DevKit V1 (38 pin)
- DHT22 sıcaklık/nem sensörü
- 10kΩ direnç (DHT22 pull-up)
- LDR (fotodirenç)
- 10kΩ direnç (LDR gerilim bölücü)
- Breadboard
- Jumper kablolar
Gerekli Kütüphaneler
DHT sensor library (Adafruit, >= 1.4.4) + Adafruit Unified Sensor. freertos/FreeRTOS.h ve freertos/task.h ESP32 core ile birlikte gelir, ayrı kurulum gerekmez.
Pin Bağlantıları
| Bileşen Pini | Bağlantı |
|---|---|
| DHT22 VCC | 3V3 |
| DHT22 GND | GND |
| DHT22 DATA (10kΩ pull-up ile 3V3'e) | GPIO27 |
| LDR uç 1 | 3V3 |
| LDR uç 2 (10kΩ ile GND, ortak nokta ADC) | GPIO34Sadece giriş |
Devre Şeması
ESP32 Kodu
| 1 | #include <DHT.h> |
| 2 | |
| 3 | #define DHTPIN 27 |
| 4 | #define DHTTYPE DHT22 |
| 5 | #define LDRPIN 34 // ADC1, giriş-only — WiFi aktifken bile güvenilir analog okuma |
| 6 | |
| 7 | DHT dht(DHTPIN, DHTTYPE); |
| 8 | |
| 9 | volatile float sonSicaklik = 0, sonNem = 0; |
| 10 | volatile int sonIsik = 0; |
| 11 | |
| 12 | // Core 0'da çalışacak görev: yavaş/bloklayıcı DHT22 okuma |
| 13 | void dhtGorevi(void* parametre) { |
| 14 | for (;;) { |
| 15 | float s = dht.readTemperature(); |
| 16 | float n = dht.readHumidity(); |
| 17 | if (!isnan(s) && !isnan(n)) { |
| 18 | sonSicaklik = s; |
| 19 | sonNem = n; |
| 20 | } |
| 21 | vTaskDelay(pdMS_TO_TICKS(2000)); // DHT22 zaten 2sn'den sık okunamaz |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | // Core 1'de (varsayılan Arduino çekirdeği) çalışacak görev: hızlı analog okuma |
| 26 | void ldrGorevi(void* parametre) { |
| 27 | for (;;) { |
| 28 | sonIsik = analogRead(LDRPIN); |
| 29 | vTaskDelay(pdMS_TO_TICKS(50)); // 20 Hz — DHT22'den çok daha hızlı, birbirini beklemez |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | void setup() { |
| 34 | Serial.begin(115200); |
| 35 | dht.begin(); |
| 36 | |
| 37 | xTaskCreatePinnedToCore( |
| 38 | dhtGorevi, "DHT_Gorevi", 4096, NULL, 1, NULL, 0 // 0 = Core 0'a sabitle |
| 39 | ); |
| 40 | xTaskCreatePinnedToCore( |
| 41 | ldrGorevi, "LDR_Gorevi", 2048, NULL, 1, NULL, 1 // 1 = Core 1'e sabitle |
| 42 | ); |
| 43 | } |
| 44 | |
| 45 | void loop() { |
| 46 | // Ana döngü sadece son değerleri raporlar, sensör okuma işini bloklamaz |
| 47 | Serial.printf("[Core %d rapor] Sıcaklık: %.1f°C Nem: %%%.1f Işık(ADC): %d\n", |
| 48 | xPortGetCoreID(), sonSicaklik, sonNem, sonIsik); |
| 49 | delay(1000); |
| 50 | } |
Çalışma Açıklaması
GPIO34 sadece giriş olarak çalışır (dahili pull-up/pull-down yoktur); bu projede harici 10kΩ gerilim bölücü kullanıldığı için bu bir sorun teşkil etmez. GPIO34, ADC1 kanalındadır — bu bilinçli bir tercihtir, çünkü ADC2 kanalındaki pinler (0,2,4,12-15,25-27) WiFi radyosu aktifken analogRead() için güvenilir sonuç vermez; bu proje WiFi kullanmasa da ileride kolayca eklenebilmesi için baştan ADC1 tercih edilmiştir.
Test Bilgisi
Kodu yükleyin, Seri Monitör'ü (115200 baud) açın. DHT22'yi elinizle ısıtırken aynı anda LDR'yi kapatıp açın; her iki değerin de birbirini beklemeden, kendi hızında (LDR ~20Hz, DHT22 ~0.5Hz) güncellendiğini gözlemleyin. Serial.printf içine xPortGetCoreID() ekleyerek DHT görevinin gerçekten Core 0'da, LDR görevinin Core 1'de çalıştığını doğrulayabilir, iki görevin birbirini yavaşlatmadığını gözle görebilirsiniz.
Olası Sorunlar ve Çözümleri
'Guru Meditation Error' veya kernel panik alıyorsanız: görev yığın boyutunu (xTaskCreatePinnedToCore çağrısındaki 4096/2048 değerleri) artırın, özellikle Serial.printf gibi fonksiyonlar beklenenden fazla stack kullanabilir. DHT22 sürekli NaN döndürüyorsa: 10kΩ pull-up direncinin gerçekten DATA hattında olduğundan ve kablonun 20cm'den kısa olduğundan emin olun. İki görev de aynı çekirdekte çalışıyor gibi görünüyorsa (performans kazancı gözlenmiyorsa): xTaskCreatePinnedToCore'un son parametresinin (0 ve 1) doğru girildiğinden emin olun, yanlışlıkla tskNO_AFFINITY (-1) girilmiş olabilir.
Alternatif Çözümler
Paylaşılan değişkenler (sonSicaklik, sonIsik) yerine daha güvenli bir yöntem olarak FreeRTOS queue (xQueueCreate) veya mutex (xSemaphoreCreateMutex) kullanılabilir — mevcut kod basitlik için volatile değişken kullanır, üretim ortamında veri yarışı (race condition) riskine karşı queue kullanımı önerilir. Üçüncü bir görev eklenerek WiFi/MQTT gönderimi de ayrı bir çekirdek görevine taşınabilir, böylece ağ gecikmesi sensör okumasını hiç etkilemez. Görevler arası senkronizasyon için xTaskNotify kullanılarak, LDR ani bir eşiği geçtiğinde DHT görevini 'hemen oku' diye tetikleyen olay tabanlı (event-driven) bir mimari kurulabilir.
Benzer Projeler
Firebase Cloud Functions ile Sunucusuz Otomasyon Mantığı
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.
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.