FreeRTOS dual-core di ESP32 — pelajaran dari proyek IoT
Proyek IoT Room Safety Dashboard awalnya berjalan di single task — satu loop besar yang membaca sensor, mem-publish ke MQTT, dan menangani notifikasi Telegram secara berurutan. Ia bekerja. Sampai tidak.
Masalah yang muncul
Saat koneksi MQTT terputus, reconnect bisa memakan waktu 2-5 detik. Selama itu, loop utama berhenti — sensor tidak dibaca, aktuator tidak diperbarui, alarm tidak berbunyi. Untuk sistem keamanan ruangan, ini tidak bisa diterima.
FreeRTOS dual-core
ESP32 punya dua core Xtensa LX6. FreeRTOS yang tertanam di ESP32 Arduino framework memungkinkan kita pin task ke core tertentu:
// Core 0: sensor polling + aktuator — tidak boleh diblokir network
xTaskCreatePinnedToCore(
sensorTask, // fungsi task
"sensors", // nama (untuk debugging)
4096, // stack size
NULL, // parameter
1, // prioritas
NULL, // handle (tidak perlu)
0 // core ID
);
// Core 1: MQTT + Telegram + OTA update
xTaskCreatePinnedToCore(
networkTask,
"network",
8192, // lebih besar karena TLS overhead
NULL,
1,
NULL,
1
);Berbagi data antar core
Dua task yang berjalan paralel tidak bisa baca-tulis variabel global sembarangan. Race condition di embedded system lebih susah di-debug daripada di server — tidak ada stack trace yang mudah dibaca.
Solusinya: SemaphoreHandle_t untuk mutual exclusion, atau lebih sederhana,
QueueHandle_t untuk passing data searah dari sensor ke network task.
QueueHandle_t sensorQueue = xQueueCreate(10, sizeof(SensorData));
// Di sensorTask:
SensorData data = readAllSensors();
xQueueSend(sensorQueue, &data, 0); // non-blocking
// Di networkTask:
SensorData data;
if (xQueueReceive(sensorQueue, &data, pdMS_TO_TICKS(100))) {
publishToMQTT(data);
}Hasilnya
Sensor tetap dibaca setiap 500ms meski network task sedang reconnect. Alarm berbunyi tepat waktu. Buzzer tidak pernah terlambat lagi karena menunggu acknowledgement dari HiveMQ.
Pelajarannya bukan tentang optimisasi — ini tentang memisahkan concern yang memang punya kebutuhan timing berbeda.