Pendahuluan — Kenapa FreeRTOS?
Di Seri 1, sketch ESP32 biasanya satu loop() besar: baca sensor, reconnect WiFi, publish MQTT, handle web server — semuanya berjalan berurutan. Itu cukup untuk belajar, tapi saat proyek membesar (seperti gabungan DHT22 + relay (#9) atau dashboard capstone (#10)), satu operasi blocking bisa menggagalkan yang lain.
FreeRTOS sudah built-in di ESP32 Arduino core. Artikel penutup Jalur E ini memecah firmware jadi task paralel: task sensor, task MQTT/WiFi, dan antrean data di antaranya — pola yang dipakai firmware produksi sebelum kamu masuk Tier 2 Seri 2.
Prasyarat: Paham MQTT dasar (#7), sudah punya sketch gabungan DHT22 + MQTT (#9), dan nyaman dengan broker Mosquitto (#16). Familiar PlatformIO (#29) membantu tapi opsional.
Masalah loop() Tunggal
| Skenario | Tanpa FreeRTOS | Dengan FreeRTOS |
|---|---|---|
delay(2000) tunggu DHT22 | MQTT loop() tidak jalan | Task MQTT tetap jalan di core lain |
| Reconnect WiFi 10 detik | Sensor tidak terbaca | Task sensor tetap periodic |
| Publish JSON besar | Subscribe relay telat | Task kontrol terpisah (opsional) |
Artikel ini fokus pada sensor → queue → MQTT — fondasi sebelum kamu menambah relay subscribe seperti di #9 atau cloud bridge Firebase (#30).
Arsitektur Task
┌─────────────────┐ xQueueSend ┌──────────────────┐
│ Task Sensor │ ──────────────────► │ Task MQTT │
│ (Core 1) │ struct SensorData │ (Core 0) │
│ DHT22 @ GPIO4 │ │ WiFi + publish │
└─────────────────┘ └────────┬─────────┘
│
▼
Broker 192.168.1.50
topic .../dht22/data
ESP32 punya dua core. Pola umum: networking di Core 0, pembacaan sensor di Core 1 — mengurangi kontensi saat TLS atau reconnect WiFi.
Core 0 vs Core 1 — Panduan Singkat
Arduino-ESP32 menjalankan WiFi stack dan sebagian driver di core tertentu. Meskipun detail internal bisa berubah antar versi core, aturan praktis untuk proyek Koding Indonesia:
- Core 0 — WiFi, MQTT client, reconnect broker, opsional HTTPS/Firebase
- Core 1 — pembacaan DHT22, debounce GPIO, sampling ADC (#35 nanti)
- Hindari dua task memanggil
WiFi.disconnect()bersamaan — race condition
Jika kamu hanya punya satu task berat (mis. TLS Firebase #30), pertimbangkan stack 12–16 KB dan monitor heap secara berkala di Serial.
Struktur Data & Queue
typedef struct {
float temperature;
float humidity;
uint32_t unix_ts;
} SensorReading;
QueueHandle_t sensorQueue;
void sensorTask(void* param) {
for (;;) {
SensorReading r;
r.temperature = dht.readTemperature();
r.humidity = dht.readHumidity();
if (!isnan(r.temperature) && !isnan(r.humidity)) {
r.unix_ts = 1782977400; // produksi: NTP #34
xQueueSend(sensorQueue, &r, pdMS_TO_TICKS(100));
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
Queue memisahkan produksi data (sensor) dari konsumsi (MQTT). Jika broker lambat, queue bisa menampung beberapa sampel — atau drop jika penuh (kebijakan tim).
Task MQTT & WiFi
void mqttTask(void* param) {
WiFi.begin("GANTI_NAMA_WIFI", "GANTI_PASSWORD_WIFI");
while (WiFi.status() != WL_CONNECTED) {
vTaskDelay(pdMS_TO_TICKS(500));
}
mqttClient.setServer("192.168.1.50", 1883);
mqttClient.setBufferSize(512);
for (;;) {
if (!mqttClient.connected()) {
String clientId = "esp32-" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str(), "kindo_esp32", "GANTI_PASSWORD_MQTT")) {
Serial.println("MQTT OK");
}
}
mqttClient.loop();
SensorReading r;
if (xQueueReceive(sensorQueue, &r, pdMS_TO_TICKS(200)) == pdTRUE) {
String json = "{\"temperature\":" + String(r.temperature, 1) +
",\"humidity\":" + String(r.humidity, 1) +
",\"unix\":" + String(r.unix_ts) +
",\"iso\":\"2026-07-02T14:30:00\"" +
",\"source\":\"esp32\"}";
mqttClient.publish("kodingindonesia/esp32/dht22/data", json.c_str());
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
Topic kodingindonesia/esp32/dht22/data konsisten dengan seluruh Seri 2. Verifikasi dengan:
mosquitto_sub -h 192.168.1.50 -u kindo_esp32 -P GANTI_PASSWORD_MQTT \
-t kodingindonesia/esp32/dht22/data -v
setup() — Spawn Task
void setup() {
Serial.begin(115200);
dht.begin();
delay(2000);
sensorQueue = xQueueCreate(5, sizeof(SensorReading));
xTaskCreatePinnedToCore(sensorTask, "Sensor", 4096, NULL, 1, NULL, 1);
xTaskCreatePinnedToCore(mqttTask, "MQTT", 8192, NULL, 1, NULL, 0);
}
void loop() {
vTaskDelay(portMAX_DELAY); // kerja di task, loop kosong
}
Pro tip: Stack size
8192untuk task MQTT — SSL/Firebase butuh lebih besar; untuk MQTT plain 4096–8192 biasanya cukup. PantauuxTaskGetStackHighWaterMark()saat debug.
Membandingkan dengan Sketch #9
Di artikel #9, loop() memanggil dht.read, mqttClient.loop(), dan callback subscribe relay secara serial. Itu valid untuk satu node. FreeRTOS menjadi penting ketika:
- Interval sensor ketat (mis. setiap 2 detik) tapi MQTT harus responsif untuk relay
- Ada task ketiga: OLED (#14), OTA check (#15), atau NTP sync (#34)
- Gateway LoRa (#28) menerima packet sementara WiFi reconnect
Kamu bisa migrasi bertahap: pertahankan logika #9, pindahkan hanya bagian sensor ke task pertama — tanpa rewrite total.
Rencana Migrasi dari Sketch #9 (Langkah demi Langkah)
- Salin sketch #9 ke project baru — pastikan MQTT ke broker
192.168.1.50masih jalan - Ekstrak fungsi baca DHT22 ke
sensorTask— interval 5 detik denganvTaskDelay - Pindahkan WiFi + mqttClient.loop + publish ke
mqttTask - Tambahkan queue di antara keduanya — mulai depth 3–5
- Kosongkan loop() — hanya
vTaskDelay(portMAX_DELAY) - Uji subscribe relay — jika masih dipakai dari #9, tambahkan task ketiga atau gabung di mqttTask dengan prioritas lebih tinggi
Setiap langkah di atas bisa di-commit terpisah di Git — memudahkan rollback jika stack overflow muncul di tengah migrasi.
Prioritas, Mutex & Watchdog
FreeRTOS menyediakan primitif lain untuk produksi:
- Mutex — lindungi bus I2C (#13) jika OLED dan BME280 dibaca dari task berbeda
- Semaphore — signal event (tombol GPIO interrupt → task MQTT)
- Task watchdog (TWDT) — reset ESP32 jika task macet; wajib di firmware lapangan
Untuk lab, watchdog bisa dimatikan dulu. Sebelum deploy ke kebun/ greenhouse capstone (#39), aktifkan TWDT di task yang paling kritis.
Stack Size — Titik Awal yang Aman
| Task | Stack (word) | Catatan |
|---|---|---|
| Sensor DHT22 | 4096 | Cukup untuk float + queue send |
| MQTT plain | 8192 | Naikkan jika PubSubClient + String JSON |
| MQTT + TLS | 12288+ | Lihat #17 — uji di lapangan |
| Firebase HTTPS | 16384+ | Pisah task cloud dari MQTT lokal (#30) |
Gunakan uxTaskGetStackHighWaterMark(NULL) setelah beberapa menit jalan untuk melihat sisa stack terendah per task — naikkan parameter stack di xTaskCreatePinnedToCore jika margin tipis.
PlatformIO vs Arduino IDE
FreeRTOS berjalan di kedua workflow. Di PlatformIO (#29), pecah file:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
knolleary/PubSubClient @ ^2.8
adafruit/DHT sensor library @ ^1.4
src/main.cpp— setup + spawn tasksrc/sensor_task.cpp— implementasi sensorTasksrc/mqtt_task.cpp— WiFi + MQTT
Debugging Multi-task
- Beri nama task jelas — muncul di panic backtrace
- Log dengan prefix:
[SENSOR],[MQTT] - Cek heap:
ESP.getFreeHeap()setelah spawn task - Jika crash Guru Meditation — naikkan stack task yang bersangkutan
- Pastikan hanya satu task yang memanggil
WiFi/mqttClient(hindari race)
Untuk timestamp live, ganti contoh statis 1782977400 dengan sinkronisasi NTP (#34) di task MQTT sebelum publish.
Hybrid: FreeRTOS + Firebase
Task MQTT bisa publish ke broker lokal dan push ke Firebase (#30) — tapi jangan blokir task sensor menunggu HTTPS. Pola aman: sensor → queue → task cloud terpisah dengan stack besar (SSL).
Untuk kebanyakan proyek rumahan, pilih satu saluran cloud dulu; FreeRTOS memudahkan penambahan saluran kedua nanti tanpa merombak task sensor.
Di tim yang sudah memakai subscriber Python → MySQL (#18), task MQTT tidak perlu tahu soal database — cukup publish JSON konsisten; worker Python tetap jalan di server terpisah tanpa blocking firmware.
Keamanan & Produksi
- Jangan commit
GANTI_PASSWORD_MQTT— pakai NVS (#12) ataubuild_flags - Broker produksi: aktifkan MQTT TLS (#17)
- Batasi ukuran queue agar RAM tidak habis saat broker down berjam-jam
- Log stack watermark sebelum rilis firmware
Estimasi Biaya
| Item | Biaya |
|---|---|
| FreeRTOS (built-in ESP32) | Rp 0 |
| Hardware (ESP32 + DHT22 dari seri sebelumnya) | Rp 0 tambahan |
| Waktu belajar abstraksi task | ±2–4 jam setelah #9 menguasai |
Checklist Sebelum Go-Live
- Hanya satu task mengelola WiFi/MQTT client?
- Stack size cukup (cek high water mark)?
- Queue depth sesuai interval publish?
- Topic dan JSON konsisten dengan Grafana (#19)?
- Watchdog dipertimbangkan untuk node lapangan?
Uji Coba
- Upload sketch multi-task — Serial: WiFi OK + MQTT OK
mosquitto_submenerima JSON setiap ~5 detik- Matikan broker 1 menit — queue penuh, task sensor tidak crash
- Nyalakan broker — publish lanjut otomatis
- Cek heap stabil setelah 30 menit
FAQ Singkat
- FreeRTOS wajib di ESP32?
- Arduino core sudah memakainya di balik layar; artikel ini membuat task eksplisit untuk kontrol kamu.
- Bisa tetap pakai loop()?
- Ya untuk proyek kecil; refactor ke task saat blocking jadi masalah.
- Core 0 vs Core 1?
- WiFi stack biasanya di Core 0 — ikuti pola pin task di contoh.
- Queue penuh — data hilang?
xQueueSenddengan timeout pendek bisa gagal — log di Serial atau naikkan depth queue jika broker sering down.- Bisa pakai FreeRTOS di ESP8266?
- ESP8266 single-core — pola task tetap ada tapi tanpa pin core; lihat perbandingan ESP32 (#1) sebelum porting.
Tips & Troubleshooting
- Guru Meditation Error: Stack overflow — naikkan parameter stack di
xTaskCreatePinnedToCore - MQTT putus acak: Pastikan
mqttClient.loop()dipanggil rutin di task MQTT - DHT22 nan: Hanya task sensor yang memanggil
dht.read; delay 2 detik setelahdht.begin() - Data duplikat di queue: Kurangi frekuensi sensor atau naikkan interval publish
- Heap menurun: Hindari
Stringbesar di loop task — pakai buffer tetap - Task tidak start: Cek parameter stack tidak 0; pastikan
sensorQueuedibuat sebelumxTaskCreate
Saat debugging di PlatformIO (#29), filter log per task dengan prefix — lebih mudah dibanding satu aliran Serial panjang dari loop tunggal. Simpan log panic backtrace jika Guru Meditation muncul — biasanya menunjuk task mana yang kehabisan stack.
Langkah Selanjutnya — Tier 2 Seri 2
Dengan Jalur E (#29–#31) selesai, Tier 1 inti Seri 2 lengkap — total 22 artikel live setelah deploy artikel ini, termasuk deep sleep (#11), broker sendiri (#16), dan capstone dashboard (#10). Lanjut ke pelengkap Tier 2:
- Bluetooth BLE (#32): kirim data sensor ke smartphone tanpa WiFi
- NTP (#34) — timestamp live di payload JSON
- Deep sleep (#11) — kombinasikan dengan task ringan pre-sleep
- Grafana (#19) — visualisasi histori dari topic yang sama
- Capstone greenhouse (#39)
FreeRTOS membuka pintu ke firmware ESP32 yang lebih andal dan siap skala tim — lanjutkan perjalanan di halaman artikel Koding Indonesia.