Published on

Communication Module Design (WiFi, MQTT, OTA) yang Tidak Coupled

Authors

πŸ“˜ Artikel 6: Communication Module Design (WiFi, MQTT, OTA) yang Tidak Coupled

Posisi: Artikel 6 dari 8 Domain Keputusan: Communication Architecture Status Lock: πŸ”’ Communication Boundary & State Machine Freeze Entry Context: IndustrialNode/IndustrialNode.ino



1. Problem Reality

Setelah Artikel 5:

  • Dependency sudah terkendali.
  • Memory sudah disiplin.
  • Concurrency sudah dibatasi.
  • Layering sudah terkunci.

Masalah baru muncul saat komunikasi ditambahkan.

Pada Artikel 5, eksekusi firmware sudah dipisahkan menjadi beberapa task:

ControlTask β†’ menjalankan application
CommTask    β†’ menangani komunikasi
ISR         β†’ hanya notify

Struktur layer tetap:

app_ β†’ svc_ β†’ drv_

Namun komunikasi memperkenalkan karakteristik baru:

event asynchronous dari luar sistem
(network, broker, OTA server)

Event ini masuk melalui:

  • MQTT callback
  • WiFi event handler
  • OTA trigger

Semua berjalan di context CommTask / network stack, bukan ControlTask.

Jika tidak dikontrol, maka:

CommTask bisa langsung memanggil svc_ atau bahkan drv_

Artinya:

execution melanggar flow:
Task β†’ app_ β†’ svc_ β†’ drv_

Inilah sumber masalah komunikasi.

Gejala yang sering terjadi:

  • WiFi event handler mengubah state aplikasi langsung.
  • MQTT callback memanggil ActuatorService.
  • OTA logic tersebar di beberapa file.
  • Reconnect logic copy-paste.
  • Credential literal tersebar di banyak file.

Contoh klasik:

void onMqttMessage(...) {
    actuator.setRelay(true);   // - ❌ bypass boundary
}

Atau:

if (!client.connected()) {
    client.connect("node01");  // reconnect logic tersebar - ❌
}

Akibat produksi:

  • Device stuck di reconnect loop.
  • Control terganggu saat TLS handshake.
  • OTA gagal β†’ device brick.
  • Bug hanya muncul saat broker restart.

Masalahnya bukan MQTT.

Masalahnya:

Communication berjalan di task terpisah, tetapi tidak mengikuti boundary layer dan execution yang sudah dikunci.


2. Root Cause Analysis

Akar masalah komunikasi spaghetti biasanya:


2.1 Transport Bercampur Business Logic

MQTT callback langsung memanggil service.

Transport layer tahu detail aplikasi.

Boundary hilang.


2.2 Tidak Ada State Machine Formal

Reconnect dilakukan dengan:

if (!client.connected()) {
    reconnect();
}

State implicit.

Tidak ada mode eksplisit seperti:

  • WIFI_CONNECTING
  • MQTT_CONNECTING
  • ERROR_BACKOFF

Akibatnya:

  • Reconnect loop liar.
  • Hard to debug.

2.3 Credential Tersebar

WiFi.begin("myssid", "mypassword");
client.connect("node01", "user", "pass");

Credential ada di banyak tempat.

Security risk. Hard to rotate.


2.4 Callback Mengontrol Hardware

Dependency menjadi:

MQTT callback β†’ ActuatorService
WiFi event β†’ ControlApp
OTA event β†’ global flag

Communication menjadi pusat coupling baru.


πŸ”Ž Visualisasi Coupling Tanpa Comm Boundary

Image

Image

Tanpa CommManager:

  • Callback bisa menyentuh semua layer.
  • Dependency kembali cyclic.

3. Design Principle (Rule yang Dikunci)

Artikel 6 mengunci arsitektur komunikasi final.


πŸ”’ Rule 1 β€” CommManager Mandatory

Semua komunikasi harus melalui:

svc_CommManager

Tidak boleh:

  • Application publish langsung.
  • Service subscribe langsung.

CommManager adalah satu-satunya gateway.


πŸ”’ Rule 2 β€” Transport β‰  Business Logic

Pisahkan:

  • Transport (WiFi, MQTT client)
  • Protocol parsing
  • Business command handling

CommManager expose interface netral:

void publishTelemetry(const TelemetryData& data);
void registerCommandHandler(ICommandHandler& handler);

App tidak tahu topic detail.


πŸ”’ Rule 3 β€” State Machine Wajib

State minimal:

INIT
β†’ WIFI_CONNECTING
β†’ WIFI_CONNECTED
β†’ MQTT_CONNECTING
β†’ MQTT_CONNECTED
β†’ ERROR_BACKOFF

Tidak boleh implicit state.

Reconnect harus berbasis state machine eksplisit.


πŸ”’ Rule 4 β€” Offline-Safe Policy

Jika WAN down:

  • Control loop tetap berjalan.
  • Telemetry dibuffer (fixed-size).
  • Tidak blocking ControlTask.

πŸ”’ Rule 5 β€” Credential Encapsulation

Credential hanya di satu tempat:

  • sys_Config.h atau secure storage.
  • Tidak literal tersebar.
  • Tidak bisa diakses sembarang layer.

πŸ”’ Rule 6 β€” OTA Guarded Execution

OTA hanya boleh:

  • Di-trigger via CommManager.
  • Diverifikasi.
  • Tidak langsung apply tanpa guard.

Setelah Artikel 6:

  • ❌ Tidak boleh reconnect tersebar
  • ❌ Tidak boleh business logic tahu MQTT detail
  • ❌ Tidak boleh bypass CommManager
  • ❌ Tidak boleh credential tersebar

Communication boundary final.


4. Implementation Pattern (ESP32 Arduino Context)


4.1 CommManager Skeleton

CommManager bukan sekadar class service.

CommManager adalah:

execution unit yang berjalan di CommTask

Artinya:

  • CommManager::run() dipanggil oleh CommTask
  • Bukan oleh ControlTask
  • Bukan oleh ISR

Sehingga:

CommTask β†’ svc_CommManager β†’ (internal state machine)
class CommManager {
public:
    void init();
    void run();
    void publishTelemetry(const TelemetryData& data);
    void registerCommandHandler(ICommandHandler& handler);

private:
    void handleStateMachine();
    void handleMqttMessage(char* topic, byte* payload, unsigned int length);

    ConnectionState state_;
};

State machine dikelola internal.

Namun yang penting:

CommManager tidak boleh langsung mengontrol application

Ia hanya:

mengubah state internal komunikasi
mengirim event / command ke application

4.2 Command Handling Interface

Interface ini adalah boundary antara:

CommTask (communication context)
dan
ControlTask (application context)

Flow lengkap:

MQTT callback (CommTask)
β†’ CommManager parse
β†’ enqueue command
β†’ ControlTask ambil command
β†’ app_ memproses

Yang penting:

callback tidak pernah menyentuh service langsung
class ICommandHandler {
public:
    virtual void onCommand(const Command& cmd) = 0;
};

Flow:

MQTT payload β†’ CommManager parse β†’ Handler di Application layer

Callback tidak menyentuh service langsung.


4.3 Offline Buffer (Fixed Size)

Buffer ini berada di dalam CommManager (CommTask).

Fungsinya:

menyerap ketidakstabilan network
tanpa mengganggu ControlTask

Tanpa ini:

ControlTask bisa terblokir oleh publish

Dengan ini:

ControlTask tetap deterministic
CommTask menangani backlog
#define TELEMETRY_BACKLOG_SIZE 10

TelemetryData backlog[TELEMETRY_BACKLOG_SIZE];
uint8_t backlogHead;
uint8_t backlogCount;

Bounded. No dynamic allocation.

Semua pattern di atas menjaga satu hal:

CommTask tidak boleh mengontrol sistem langsung

Alur final harus tetap:

CommTask β†’ CommManager β†’ (queue/event)
β†’ ControlTask β†’ app_ β†’ svc_ β†’ drv_

Dengan ini:

  • komunikasi tetap asynchronous
  • control tetap deterministic
  • layering tetap utuh

5. Constraint & Embedded Impact

Communication adalah domain paling β€œrakus resource” di ESP32.

Jika tidak dikontrol, ia merusak determinism dan memory discipline.


5.1 RAM Impact

Komponen yang memakan RAM besar:

  • WiFi stack internal
  • TCP/IP buffer
  • TLS (mbedTLS)
  • MQTT buffer

Jika CommManager:

  • Tidak membatasi backlog
  • Menggunakan String
  • Menggunakan buffer dinamis

Maka:

  • Heap minimum turun.
  • TLS handshake gagal.
  • OTA gagal.

Dengan:

  • Fixed-size telemetry backlog
  • Static buffer parsing
  • Tidak ada dynamic allocation

RAM menjadi predictable.


5.2 Stack Impact

Callback MQTT bisa membawa payload besar.

Jika parsing JSON dilakukan di callback dan object lokal besar dibuat:

  • Stack overflow.
  • Crash sporadis.

Dengan design:

  • Callback hanya enqueue raw payload (bounded).
  • Parsing dilakukan di CommTask context.

Stack terisolasi.


5.3 Determinism Impact

Jika reconnect dilakukan di ControlTask:

  • Control jitter.
  • Watchdog reset.

Dengan separation:

  • CommTask handle reconnect.
  • ControlTask tetap periodik.

Determinism tetap terjaga.


5.4 Network Instability Impact

WAN tidak stabil adalah kondisi normal, bukan exception.

Comm architecture harus mengasumsikan:

  • Broker restart
  • WiFi flapping
  • ISP latency spike

Tanpa state machine eksplisit:

  • Reconnect loop liar.
  • Device stuck.

Dengan state machine:

Transisi eksplisit β†’ audit-able.


πŸ”Ž Visualisasi State Machine Connection

Image

Image

State harus eksplisit. Bukan implicit if-else tersebar.


6. Failure Scenario

Communication failure sering muncul hanya di lapangan.


Scenario 1 β€” Broker Restart

Tanpa state machine:

if (!client.connected()) {
    client.connect(...);
}

Reconnect dipanggil terus-menerus.

Akibat:

  • CPU spike
  • Heap churn
  • Device stuck

Dengan state machine:

  • Transisi ke ERROR_BACKOFF
  • Delay exponential
  • Retry terkontrol

Scenario 2 β€” MQTT Callback Mengubah Relay

Callback langsung:

actuator.setRelay(true);

Jika ControlTask juga mengubah relay:

  • Conflict
  • Relay chatter

Dengan architecture freeze:

Callback β†’ CommandHandler β†’ ControlTask Single decision point.


Scenario 3 β€” WAN Down Lama

WiFi down 30 menit.

Jika telemetry publish blocking:

  • ControlTask delay
  • Watchdog reset

Jika backlog growable:

  • RAM habis

Dengan offline-safe policy:

  • Telemetry masuk ring buffer bounded
  • Data lama drop terkontrol
  • Control tetap jalan

Scenario 4 β€” OTA Tanpa Guard

OTA dipanggil langsung dari callback.

Jika firmware corrupt:

  • Device brick
  • Tidak ada rollback

Dengan rule:

  • OTA hanya via CommManager
  • Verify checksum
  • Trigger reboot terkendali

7. Anti-Pattern

Daftar merah Artikel 6:

  • ❌ App publish langsung MQTT client
  • ❌ Service subscribe topic langsung
  • ❌ Reconnect logic copy-paste
  • ❌ Credential literal tersebar
  • ❌ OTA dipanggil dari callback
  • ❌ Callback memodifikasi hardware langsung
  • ❌ Tidak ada state machine eksplisit

Dampak:

  • Coupling tinggi
  • Security risk
  • Redesign mahal
  • Device stuck di lapangan

8. Freeze Point

Setelah Artikel 6, keputusan berikut dianggap final:

  • Semua komunikasi lewat svc_CommManager.
  • State machine eksplisit wajib.
  • Offline buffering bounded dan fixed-size.
  • Credential encapsulated di satu tempat.
  • OTA guarded via CommManager.
  • Tidak ada business logic di transport callback.

Communication architecture tidak boleh berubah.

Artikel 7–8 harus patuh pada boundary ini.


9. Engineering Checklist

Audit sebelum release:

  • Apakah ada publish langsung tanpa CommManager?
  • Apakah reconnect tersebar di banyak file?
  • Apakah credential tersebar literal?
  • Apakah callback menyentuh hardware?
  • Apakah state machine eksplisit?
  • Apakah backlog bounded?

Jika satu saja β€œya” β†’ melanggar Artikel 6.


10. Summary (5 Bullet Maksimal)

  • Communication harus domain terisolasi.
  • CommManager adalah gateway tunggal.
  • State machine eksplisit mencegah reconnect chaos.
  • Offline-safe policy wajib untuk produksi.
  • Communication tidak boleh mengganggu control determinism.

Catatan Penyusunan Artikel ini disusun sebagai materi edukasi dan referensi umum berdasarkan berbagai sumber pustaka, praktik lapangan, serta bantuan alat penulisan. Pembaca disarankan untuk melakukan verifikasi lanjutan dan penyesuaian sesuai dengan kondisi serta kebutuhan masing-masing sistem.