ESP32 ve FreeRTOS ile Kesintisiz MQTT Iletisimi: Asenkron IoT Mimarisi
Nesnelerin Interneti (IoT) projelerinde en sik karsilasilan darbogazlardan biri, mikrodenetleyicinin Wi-Fi baglantisi ve MQTT mesajlarini beklerken ana döngüyü (main loop) bloklamasidir. Özellikle sensör verilerinin (örnegin ortam nemi veya LiDAR ile obje takibi) gerçek zamanli olarak okunup islendigi senaryolarda, delay() fonksiyonlari veya senkron bekleme süreleri tüm sistemin tepki süresini öldürür.
Iste tam bu noktada ESP32'nin çift çekirdekli yapisi ve yerlesik FreeRTOS yetenekleri devreye giriyor. Bu yazida, sensör okuma ve MQTT yayinlama (publish) islemlerini birbirinden izole edilmis görevlere (Task) bölerek nasil asenkron bir mimari kuracagimizi inceleyecegiz.
Neden FreeRTOS Task'lari Kullanmaliyiz?
Standart Arduino loop() döngüsü tek bir thread üzerinde çalisir. Eger MQTT sunucusuna baglanmak 2 saniye sürerse, o 2 saniye boyunca ESP32 baska hiçbir sensörü okuyamaz. FreeRTOS kullanarak:
- Ag islemlerini Core 0'a,
- Donanim/Sensör okuma islemlerini Core 1'e atayabiliriz.
- Bir görev bloklandiginda (örnegin Wi-Fi koptugunda), diger görevler saniyede binlerce kez çalismaya devam eder.
Örnek Uygulama: Asenkron MQTT Publish
Asagidaki kod blogunda, standart loop() fonksiyonunu tamamen bos birakip, MQTT baglantisini ve sensör okuma islemlerini bagimsiz FreeRTOS görevleri olarak tanimladik.
#include <WiFi.h>
#include <PubSubClient.h>
// Ag ve MQTT Ayarlari
const char* ssid = "WIFI_SSID";
const char* password = "WIFI_PASSWORD";
const char* mqtt_server = "broker.hivemq.com";
WiFiClient espClient;
PubSubClient client(espClient);
// Sensör Verisi Için Task Handle
TaskHandle_t SensorTask;
TaskHandle_t MqttTask;
void setup() {
Serial.begin(115200);
// Wi-Fi Baglantisi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
client.setServer(mqtt_server, 1883);
// Core 1 üzerinde Sensör Okuma Görevi olusturuluyor
xTaskCreatePinnedToCore(
readSensorData, /* Çalistirilacak Fonksiyon */
"SensorTask", /* Görev Adi (Debug için) */
10000, /* Stack Boyutu (Byte) */
NULL, /* Parametre */
1, /* Öncelik (Priority) */
&SensorTask, /* Task Handle */
1); /* Hangi Çekirdek (Core 1) */
// Core 0 üzerinde MQTT Görevi olusturuluyor
xTaskCreatePinnedToCore(
handleMqttConnection,
"MqttTask",
10000,
NULL,
2, /* Ag islemleri önceliklidir */
&MqttTask,
0); /* Hangi Çekirdek (Core 0) */
}
void loop() {
// FreeRTOS kullanildigi için loop bos birakilir.
// Geleneksel delay() yerine vTaskDelay kullanilir.
vTaskDelete(NULL);
}
// Donanim Okuma Görevi
void readSensorData( void * pvParameters ){
for(;;){
// Dummy sensör verisi üretimi (Örn: Nem veya Lidar Mesafesi)
float dummyHumidity = random(40, 60);
Serial.printf("Sensör Okundu: %.2f\n", dummyHumidity);
// MQTT üzerinden Publish (Eger baglanti varsa)
if(client.connected()) {
char payload[10];
dtostrf(dummyHumidity, 4, 2, payload);
client.publish("ta2kmk/sensors/humidity", payload);
}
// Diger görevlere nefes aldirmak için FreeRTOS gecikmesi
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
// MQTT Ag Görevi
void handleMqttConnection( void * pvParameters ){
for(;;){
if (!client.connected()) {
Serial.println("MQTT Broker'a baglaniliyor...");
if (client.connect("ESP32_TA2KMK_Client")) {
Serial.println("Baglanti Basarili!");
} else {
Serial.print("Hata, rc=");
Serial.println(client.state());
vTaskDelay(5000 / portTICK_PERIOD_MS); // 5 saniye sonra tekrar dene
}
}
client.loop(); // Gelen mesajlari dinle
vTaskDelay(10 / portTICK_PERIOD_MS); // Watchdog timer'i rahatlat
}
}