Ahmet İhtiyar
Veri Hattı Vaka Çalışmaları Sistem Kaydı Değişmezler İletişim

Plasmon: 100K+ Edge Cihaz İçin Telemetri Mimarisi

ESP32 firmware, MQTT/TLS RPC, yerel ring-buffer ve RabbitMQ ingest hattı

100K+ cihaz mimarisi · Saha hücresel failover doğrulaması
Yığın: ESP32 (C/FreeRTOS) · Modbus RTU/TCP · MQTT over TLS · RabbitMQ · WebSocket · OpenAPI

1. Bağlam

Plasmon, endüstriyel soğutma ve iklimlendirme ünitelerinin saha koşullarında kesintisiz izlenmesi ve uzaktan parametre yönetimi amacıyla tasarlandı. Bu mimari, 100K+ edge cihaz için tasarlandı; saha donanımından merkezi bulut broker’ına kadar olan telemetri ve komut-kontrol zincirini uçtan uca kapsar.

2. Problem

Endüstriyel sahada iki büyük zorluk vardı:

  1. Güvenilmez Hücresel Bağlantı: Fabrikalarda ve uzak tesislerde GSM/4G bağlantısı sık sık dalgalanıyor, saatlik baz istasyonu kesintileri yaşanıyordu. Geleneksel HTTP yoklamaları (polling) bellek taşmalarına ve veri boşluklarına yol açıyordu.
  2. Çift Yönlü İletişim ve OTA: Cihazların sadece veri göndermesi değil; acil durumlarda set-değeri değiştirebilmesi, Modbus register’larına komut yazabilmesi ve uzaktan firmware (OTA) güncellemelerini güvenle alması gerekiyordu.

3. Mimari ve Veri Akışı

 [ Sensör & Chiller ]
         │ (RS-485 / Modbus RTU)
         ▼
 ┌────────────────────────────────────────────────────────┐
 │ ESP32 Edge Gateway (FreeRTOS)                         │
 │ ┌───────────────────┐        ┌───────────────────────┐ │
 │ │ Modbus Poller     │───────►│ SPIFFS Ring-Buffer    │ │
 │ │ (Core 0, 1000ms)  │        │ (Ağ yokken kalıcı flaş)│ │
 │ └───────────────────┘        └──────────┬────────────┘ │
 │                                         │              │
 │ ┌───────────────────┐                   │ Bağlantı     │
 │ │ MQTT Client (TLS) │◄──────────────────┘ Gelince      │
 │ └─────────┬─────────┘                                  │
 └───────────┼────────────────────────────────────────────┘
             │ MQTT/TLS (QoS 1) - GSM 4G / Wi-Fi
             ▼
 ┌────────────────────────────────────────────────────────┐
 │ Ingestion & Broker Layer                               │
 │ ┌───────────────────┐        ┌───────────────────────┐ │
 │ │ EMQX / Mosquitto  │───────►│ RabbitMQ Telemetry    │ │
 │ │ TLS Broker        │        │ Topic Exchanges (DLX) │ │
 │ └───────────────────┘        └──────────┬────────────┘ │
 └─────────────────────────────────────────┼──────────────┘
                                           ▼
                                [ TimescaleDB & WebFlux ]

4. Kararlar ve Elenen Alternatifler

  • ESP32 + C (FreeRTOS) seçildi, Raspberry Pi / Embedded Linux elendi: Endüstriyel panolarda 70°C üzeri sıcaklık, anlık voltaj kesilmelerinde SD kart bozulması ve 3W+ enerji tüketimi nedeniyle gömülü Linux uygun değildi. ESP32 donanımsal watchdog ve statik bellek tahsisiyle 7/24 deterministik çalışır.
  • MQTT/TLS (QoS 1) seçildi, HTTP REST elendi: REST çağrıları her paket için yeni TCP ve TLS el sıkışması açarak GSM kotasını hızla tüketiyordu. MQTT persistent connection sayesinde tek bağlantı üzerinden kompakt ikili/JSON telemetri akıtıldı; Last Will & Testament (LWT) ile anlık bağlantı kopmaları gecikmesiz algılandı.
  • Kuyruk katmanında RabbitMQ seçildi, Kafka ertelendi: Cihaz bazında komut-yanıt (RPC) kalıpları, routing key esnekliği ve düşük operasyonel bakım maliyeti nedeniyle RabbitMQ tercih edildi.

5. Gerçek Kod: Flash Ring-Buffer ve Telemetri Paketi

Aşağıdaki C parçası, ağ koptuğunda telemetriyi heap yerine flaş dairesel tampona yazan ve bağlantı kurulduğunda boşaltan mantığın çekirdeğidir:

#include "esp_spiffs.h"
#include "esp_log.h"

#define TELEMETRY_RING_FILE "/spiffs/ring_buf.bin"
#define MAX_BUFFER_ENTRIES   2048

typedef struct __attribute__((packed)) {
    uint32_t timestamp;
    int16_t  temp_chiller_x10;
    int16_t  temp_ambient_x10;
    uint16_t pressure_bar_x100;
    uint8_t  compressor_state;
    uint8_t  crc8;
} TelemetryFrame;

esp_err_t ring_buffer_push(const TelemetryFrame *frame) {
    FILE *f = fopen(TELEMETRY_RING_FILE, "a+b");
    if (!f) {
        ESP_LOGE("RING", "Flaş dosya açılamadı");
        return ESP_FAIL;
    }
    
    // Boyut sınırını denetle: MAX_BUFFER_ENTRIES aşılırsa en eski bloğu başa sar
    long size = ftell(f);
    if (size >= (long)(MAX_BUFFER_ENTRIES * sizeof(TelemetryFrame))) {
        // En eski kayıt üzerine yazma (wrap-around) stratejisi
        fseek(f, 0, SEEK_SET);
    }
    
    fwrite(frame, sizeof(TelemetryFrame), 1, f);
    fflush(f);
    fclose(f);
    return ESP_OK;
}

6. Sonuç ve Ölçülebilir Çıktılar

  • Tasarım Kapasitesi: 100K+ edge cihaz eşzamanlı telemetri yayını için boyutlandırıldı.
  • Doğrulanan Test Ölçeği: 10.000+ aktif sensör simülasyonu; p95 < 22ms broker ingest gecikmesi.
  • Ağ Kesintisi Dayanımı: 4G hücresel hat kopsa bile 320KB dahili flaş ring-buffer yaklaşık ~48 saatlik telemetri kaydını RAM taşması yaşamadan sakladı; hat geldiğinde 0 paket kaybıyla kuyruğa boşaltıldı.

7. Neyi Farklı Yapardım?

  • Başlangıçta telemetri paketleri için JSON formatı kullanmıştım. Paket başı ~180 bayt olan JSON, hücresel hatta ciddi bir maliyet getirdi. Baştan Protobuf veya saf packed struct ikili (binary) formata geçseydim hücresel bant tüketimini ilk günden %65 daha aşağı çekebilirdim.