Published on

ARCHITECTURE_RULES - Production Firmware Discipline — ESP32 (Arduino Core)

Authors

📜 ARCHITECTURE_RULES: Production Firmware Discipline — ESP32 (Arduino Core)



⚠️ Status Dokumen

Dokumen ini adalah ringkasan aturan arsitektur yang dikunci pada Artikel 2–7.

Aturan di bawah ini tidak boleh dilanggar tanpa redesign formal.

Jika satu saja dilanggar, firmware berisiko kembali menjadi spaghetti.


1️⃣ Dependency Discipline (Artikel 2 Freeze)

🔒 R1 — No Global Mutable State

Dilarang:

bool globalMode;
int relayState;

Diperbolehkan:

  • constexpr
  • compile-time config
  • immutable constant

Semua state runtime harus berada di dalam class.


🔒 R2 — No Singleton

Dilarang:

Logger::instance();
WiFiManager::getInstance();

Instance dibuat hanya di:

IndustrialNode.ino


🔒 R3 — Explicit Dependency Injection

Semua dependency diberikan melalui constructor.

Tidak boleh mengambil dependency dari global.


🔒 R4 — Composition > Inheritance

Inheritance hanya jika benar-benar diperlukan. Default approach adalah composition.


2️⃣ Memory Discipline (Artikel 3 Freeze)

🔒 R5 — Static-First Allocation

Semua object utama firmware harus:

  • Dideklarasikan statik di IndustrialNode.ino
  • Lifetime = lifetime firmware

Dilarang membuat object besar di dalam loop().


🔒 R6 — Heap Restricted Zone

Heap hanya boleh digunakan untuk:

  • TLS internal
  • MQTT internal buffer
  • OTA buffer

Selain itu dilarang.


🔒 R7 — No Allocation in Control Loop

Dilarang:

  • new
  • delete
  • String
  • container growable

di path kontrol utama.


🔒 R8 — No Allocation in ISR

ISR hanya boleh:

  • Set flag
  • Notify task
  • Push ke queue fixed-size

🔒 R9 — Destructor Must Be Trivial

Destructor tidak boleh:

  • Blocking
  • Publish
  • Logging berat
  • Network operation

3️⃣ Layering & Architecture Freeze (Artikel 4)

🔒 R10 — 3-Layer Model Final

Prefix wajib:

  • app_ → Application
  • svc_ → Service
  • drv_ → Driver
  • sys_ → System

Arduino flat structure → prefix adalah boundary enforcement.


🔒 R11 — Dependency Direction Strict (DAG)

Allowed:

app_ → svc_ → drv_ → sys_

Forbidden:

  • drv* include svc*
  • svc* include app*
  • circular include

🔒 R12 — Composition Root Only One

Semua instance dibuat di:

IndustrialNode.ino

Tidak boleh instansiasi tersebar.


4️⃣ Concurrency Boundary (Artikel 5)

🔒 R13 — ISR Only Notify

ISR tidak boleh:

  • Call service
  • Allocate
  • Publish
  • Logging berat

🔒 R14 — ControlTask Non-Blocking

ControlTask tidak boleh:

  • Publish network blocking
  • Logging blocking
  • Delay panjang

🔒 R15 — Queue Must Be Bounded

Semua komunikasi antar task:

  • Fixed-size
  • Tidak growable
  • Tidak dynamic

🔒 R16 — No Shared Mutable Without Owner

Jika state digunakan lintas task:

  • Harus punya owner tunggal
  • Task lain hanya kirim request

5️⃣ Communication Freeze (Artikel 6)

🔒 R17 — CommManager Mandatory

Semua komunikasi harus melalui:

svc_CommManager

Tidak boleh publish langsung dari App/Service.


🔒 R18 — Explicit State Machine Required

State minimal:

INIT
WIFI_CONNECTING
WIFI_CONNECTED
MQTT_CONNECTING
MQTT_CONNECTED
ERROR_BACKOFF

Reconnect tidak boleh implicit.


🔒 R19 — Offline-Safe Policy

Jika WAN down:

  • Control tetap jalan
  • Telemetry dibuffer bounded
  • Tidak blocking ControlTask

🔒 R20 — Credential Encapsulation

Credential hanya di satu tempat (sys_Config.h atau secure storage). Tidak literal tersebar.


🔒 R21 — OTA Guarded

OTA hanya:

  • Via CommManager
  • Diverifikasi
  • Tidak dieksekusi langsung dari callback

6️⃣ Reliability & Error Model (Artikel 7)

🔒 R22 — Explicit Error Propagation

Semua fungsi kritikal harus return:

enum class Status { ... };

Tidak boleh silent failure.


🔒 R23 — No Exception

Exception dilarang.


🔒 R24 — Watchdog Encapsulated

Watchdog hanya di-manage oleh:

svc_HealthService

Tidak boleh di-feed sembarang tempat.


🔒 R25 — Fail-Safe Mandatory

Jika error kritikal:

  • System masuk state FAILSAFE
  • Relay ke safe state
  • Tidak fail-open

🔒 R26 — Logging via Interface

Tidak boleh Serial.println liar.

Logging harus:

  • Via interface
  • Non-blocking
  • Bounded queue

🔒 R27 — Health Telemetry Mandatory

Minimal harus ada:

  • reset_reason
  • heap_min_free
  • backlog_depth
  • wifi_state
  • error_code

7️⃣ Reference Implementation (Minimal Project)

Bagian ini menunjukkan bentuk minimal firmware yang mematuhi seluruh rule Artikel 2–6.

Tujuannya bukan fitur lengkap, tetapi:

melihat bagaimana rule diterapkan secara nyata

7.1 Struktur File (Arduino Flat Constraint)

IndustrialNode/

IndustrialNode.ino

app_control.cpp
app_control.h

svc_sensor.cpp
svc_sensor.h
svc_actuator.cpp
svc_actuator.h
svc_comm.cpp
svc_comm.h

drv_relay.cpp
drv_relay.h

sys_config.h
sys_types.h

Semua file mengikuti:

prefix = boundary layer

7.2 Composition Root (R3, R5, R12)

// IndustrialNode.ino

#include "app_control.h"
#include "svc_sensor.h"
#include "svc_actuator.h"
#include "svc_comm.h"
#include "drv_relay.h"

// Driver
RelayDriver relay(5);

// Service
ActuatorService actuator(relay);
SensorService sensor;
CommManager comm;

// Application
ControlApp app(sensor, actuator, comm);

// Task Wrapper
ControlTask controlTask(app);
CommTask commTask(comm);

void setup() {
    controlTask.start();
    commTask.start();
}

void loop() {
    // tidak digunakan
}

Memenuhi:

R3  → dependency injection
R5  → static allocation
R12 → single composition root

7.3 Application Layer (app_)

// app_control.cpp

void ControlApp::run()
{
    if (mode_ == AUTO)
    {
        float temp = sensor_.getTemperature();

        if (temp > threshold_)
            actuator_.on();
        else
            actuator_.off();
    }
}

Ciri:

✔ decision system
✔ tidak tahu hardware
✔ tidak tahu MQTT

7.4 Service Layer (svc_)

// svc_actuator.cpp

void ActuatorService::on()
{
    relay_.set(true);
}

void ActuatorService::off()
{
    relay_.set(false);
}

Ciri:

✔ domain behavior
✔ tidak tahu mode AUTO/MANUAL
✔ tidak tahu task

7.5 Driver Layer (drv_)

// drv_relay.cpp

void RelayDriver::set(bool state)
{
    digitalWrite(pin_, state ? HIGH : LOW);
}

Ciri:

✔ pure hardware
✔ tidak tahu logic
✔ tidak tahu service

7.6 Task Implementation (Artikel 5 → R13–R16)

ControlTask

void ControlTask::run(void* param)
{
    while (true)
    {
        app_.run();
        vTaskDelay(10);
    }
}

CommTask

void CommTask::run(void* param)
{
    while (true)
    {
        comm_.run();
        vTaskDelay(10);
    }
}

7.7 ISR Pattern (R13)

volatile bool buttonPressed = false;

void IRAM_ATTR buttonISR()
{
    buttonPressed = true;
}

Diproses di ControlTask:

if (buttonPressed)
{
    buttonPressed = false;
    app.handleButton();
}

7.8 Inter-Task Communication (R15, R16)

struct Command {
    bool relayOn;
};

QueueHandle_t commandQueue;

CommTask:

xQueueSend(commandQueue, &cmd, 0);

ControlTask:

xQueueReceive(commandQueue, &cmd, 0);
app.handleCommand(cmd);

7.9 Communication Boundary (Artikel 6 → R17–R21)

// svc_comm.cpp

void CommManager::run()
{
    handleStateMachine();

    if (mqttMessageAvailable())
    {
        Command cmd = parseMessage();
        enqueueCommand(cmd);
    }
}

Tidak pernah:

❌ actuator.setRelay()
❌ akses service lain langsung

7.10 Execution Flow Final

ControlTask → app_ → svc_ → drv_

CommTask → svc_CommManager → queue → ControlTask

7.11 Rule Coverage Mapping

RuleImplementasi
R1–R4dependency via constructor
R5–R9static object di .ino
R10–R12prefix + DAG
R13–R16ISR notify + queue
R17–R21CommManager

7.12 Inti yang Harus Terlihat

Project ini menunjukkan:

tidak ada global mutable
tidak ada singleton
tidak ada task liar
tidak ada callback bypass layer
tidak ada dynamic allocation di control path

🛡 Final Enforcement

Jika salah satu aturan di atas dilanggar:

  • Firmware berisiko kehilangan determinism.
  • Refactor menjadi mahal.
  • Debug menjadi sulit.
  • Uptime jangka panjang menurun.

Aturan ini dibuat untuk menjaga firmware:

Stabil, deterministik, audit-able, dan maintainable selama 3–5 tahun.


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.