IoT Eğitimleri

Gömülü Linux için Paho MQTT C Client Eğitimi

Gömülü Linux için pratik bir Paho MQTT C eğitimi: kütüphaneyi OpenSSL ile derleyin, TLS kullanan bir publisher ve subscriber yazın, ARM için çapraz derleyin ve systemd altında güvenilir şekilde çalıştırın.

Gömülü bir Linux kartında çalışan ve sensör verisini TLS üzerinden bulut MQTT broker'a publish eden Paho MQTT C client
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

Gömülü Linux'ta küçük ve az bağımlılıklı bir MQTT client'a ihtiyacınız olduğunda standart seçim Paho MQTT C kütüphanesidir. Bu eğitimde Paho MQTT C'yi OpenSSL ile derleyecek, bir TLS publisher'ı ve bir subscriber yazacak, bir ARM kart için çapraz derleme yapacak ve sonucu bir bulut broker'a bağlı, dayanıklı bir systemd servisi olarak çalıştıracaksınız.

Gömülü Linux için Neden Paho MQTT C?

Eclipse Paho C, TLS için isteğe bağlı tek bir bağımlılığı (OpenSSL) olan düz C99 kodudur. Raspberry Pi'dan birkaç megabayt RAM'e sahip endüstriyel bir gateway'e kadar her şeyde çalışır, MQTT 3.1, 3.1.1 ve 5'i destekler ve çoğu dağıtım ile Yocto katmanında paket olarak bulunur. Diller ve kütüphaneler arasında seçim yapıyorsanız, MQTT client kütüphanelerine genel bakış yazısı onu bağlamına oturtur; C++ tercih ediyorsanız Paho bu kütüphanenin üzerine bir C++ sarmalayıcı da sunar.

Kütüphane dört varyantla gelir. Bağlama (link) aşamasında doğru olanı seçin:

Kütüphane API tarzı TLS Tipik kullanım
paho-mqtt3c Senkron MQTTClient_* Hayır Güvenilir ağlarda basit araçlar
paho-mqtt3cs Senkron MQTTClient_* Evet Çoğu gömülü publisher ve subscriber
paho-mqtt3a Asenkron MQTTAsync_* Hayır Olay güdümlü uygulamalar
paho-mqtt3as Asenkron MQTTAsync_* Evet TLS kullanan olay güdümlü uygulamalar

Bu eğitim paho-mqtt3cs, yani TLS'li senkron API'yi kullanır. "Senkron", connect ve publish çağrılarının tamamlanana kadar bloklaması anlamına gelir; gelen mesajlar yine de callback'ler aracılığıyla bir arka plan thread'inde ulaşır.

Kütüphaneyi Kurma veya Derleme

Debian, Ubuntu veya Raspberry Pi OS'ta paketlenmiş sürüm genellikle yeterlidir:

sudo apt update
sudo apt install -y build-essential libpaho-mqtt-dev libssl-dev ca-certificates

En son sürümü almak veya çapraz derleme yapmak için kaynaktan derleyin:

git clone https://github.com/eclipse-paho/paho.mqtt.c.git
cd paho.mqtt.c
cmake -B build -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_SAMPLES=FALSE -DPAHO_BUILD_STATIC=FALSE -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"
sudo cmake --install build
sudo ldconfig

Resmi Paho C dokümantasyonu, aşağıda kullanılan her yapıyı ve fonksiyonu belgeler.

TLS Üzerinden Bir Paho MQTT C Publisher'ı

Bu program 8883 portunda broker.connectmq.com'a bağlanır, sunucu sertifikasını sistemin CA paketine göre doğrular ve beş saniyede bir okuma publish eder. Public MQTT test broker'ına anonim olarak çalışır; özel bir broker'a geçtiğinizde kimlik bilgilerini ortam değişkenlerinden okur.

publisher.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include "MQTTClient.h"

#define ADDRESS   "ssl://broker.connectmq.com:8883"
#define CA_BUNDLE "/etc/ssl/certs/ca-certificates.crt"
#define QOS       1
#define TIMEOUT   10000L

static volatile sig_atomic_t running = 1;
static void on_signal(int sig) { (void)sig; running = 0; }

static void connection_lost(void *ctx, char *cause) {
    (void)ctx;
    fprintf(stderr, "connection lost: %s\n", cause ? cause : "unknown");
}

static int message_arrived(void *ctx, char *topic, int len, MQTTClient_message *msg) {
    (void)ctx; (void)len;
    MQTTClient_freeMessage(&msg);
    MQTTClient_free(topic);
    return 1;
}

static int connect_broker(MQTTClient client) {
    MQTTClient_connectOptions opts = MQTTClient_connectOptions_initializer;
    MQTTClient_SSLOptions ssl = MQTTClient_SSLOptions_initializer;

    ssl.trustStore = CA_BUNDLE;
    ssl.enableServerCertAuth = 1;
    ssl.verify = 1;                       /* host adını da kontrol et */
    ssl.sslVersion = MQTT_SSL_VERSION_TLS_1_2;

    opts.keepAliveInterval = 30;
    opts.cleansession = 1;
    opts.ssl = &ssl;
    opts.username = getenv("MQTT_USERNAME");   /* NULL = anonim */
    opts.password = getenv("MQTT_PASSWORD");

    int rc = MQTTClient_connect(client, &opts);
    if (rc != MQTTCLIENT_SUCCESS)
        fprintf(stderr, "connect failed, rc=%d\n", rc);
    return rc;
}

int main(void) {
    const char *client_id = getenv("MQTT_CLIENT_ID");
    const char *prefix = getenv("MQTT_TOPIC_PREFIX");
    char id_buf[64], topic[256], payload[128];

    if (!client_id) {
        snprintf(id_buf, sizeof id_buf, "paho-c-%d", (int)getpid());
        client_id = id_buf;
    }
    if (!prefix) prefix = "connectmq/demo/paho-c-4821";
    snprintf(topic, sizeof topic, "%s/gateway1/temperature", prefix);

    signal(SIGINT, on_signal);
    signal(SIGTERM, on_signal);

    MQTTClient client;
    int rc = MQTTClient_create(&client, ADDRESS, client_id,
                               MQTTCLIENT_PERSISTENCE_NONE, NULL);
    if (rc != MQTTCLIENT_SUCCESS) {
        fprintf(stderr, "create failed, rc=%d\n", rc);
        return EXIT_FAILURE;
    }
    MQTTClient_setCallbacks(client, NULL, connection_lost, message_arrived, NULL);

    int backoff = 1;
    while (running && connect_broker(client) != MQTTCLIENT_SUCCESS) {
        sleep(backoff);
        if (backoff < 60) backoff *= 2;
    }
    printf("connected as %s, publishing to %s\n", client_id, topic);

    srand((unsigned)time(NULL));
    while (running) {
        if (!MQTTClient_isConnected(client)) {
            if (connect_broker(client) != MQTTCLIENT_SUCCESS) { sleep(5); continue; }
        }

        double temp = 20.0 + (rand() % 100) / 10.0;
        int n = snprintf(payload, sizeof payload,
                         "{\"ts\":%ld,\"temperature\":%.1f}", (long)time(NULL), temp);

        MQTTClient_message msg = MQTTClient_message_initializer;
        MQTTClient_deliveryToken token;
        msg.payload = payload;
        msg.payloadlen = n;
        msg.qos = QOS;
        msg.retained = 0;

        rc = MQTTClient_publishMessage(client, topic, &msg, &token);
        if (rc == MQTTCLIENT_SUCCESS)
            rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
        if (rc != MQTTCLIENT_SUCCESS)
            fprintf(stderr, "publish failed, rc=%d\n", rc);
        else
            printf("sent %s\n", payload);

        sleep(5);
    }

    MQTTClient_disconnect(client, TIMEOUT);
    MQTTClient_destroy(&client);
    return EXIT_SUCCESS;
}

Derleyin ve çalıştırın:

gcc -Wall -O2 -o publisher publisher.c -lpaho-mqtt3cs
./publisher

Birkaç satır açıklamayı hak ediyor. ssl.verify = 1, varsayılan olarak kapalı olan host adı doğrulamasını açar; bu olmadan herhangi bir host için geçerli herhangi bir sertifika kabul edilirdi. MQTTClient_setCallbacks çağrısını bağlanmadan önce yapmak client'ı çok thread'li moda alır; böylece kütüphane keep-alive ve alım işlemlerini kendi thread'inde yürütür ve connection_lost bağlantı koptuğunda size haber verir. Publish döngüsü MQTTClient_isConnected ile kontrol yapar ve kendi yeniden deneme mantığıyla yeniden bağlanır, çünkü senkron API otomatik olarak yeniden bağlanmaz.

Callback'li Bir Subscriber

Subscriber aynı bağlantı mantığını yeniden kullanır ve işini message_arrived içinde yapar. main fonksiyonunu bununla değiştirin (yukarıdaki yardımcı fonksiyonları koruyarak):

static int on_message(void *ctx, char *topic, int len, MQTTClient_message *msg) {
    (void)ctx; (void)len;
    printf("%s: %.*s\n", topic, msg->payloadlen, (char *)msg->payload);
    MQTTClient_freeMessage(&msg);
    MQTTClient_free(topic);
    return 1;   /* 1 = mesaj işlendi; 0 kütüphaneden yeniden teslim ister */
}

int main(void) {
    const char *prefix = getenv("MQTT_TOPIC_PREFIX");
    char filter[256];
    if (!prefix) prefix = "connectmq/demo/paho-c-4821";
    snprintf(filter, sizeof filter, "%s/+/temperature", prefix);

    signal(SIGINT, on_signal);
    signal(SIGTERM, on_signal);

    MQTTClient client;
    MQTTClient_create(&client, ADDRESS, "paho-c-sub-4821",
                      MQTTCLIENT_PERSISTENCE_NONE, NULL);
    MQTTClient_setCallbacks(client, NULL, connection_lost, on_message, NULL);

    while (running) {
        if (!MQTTClient_isConnected(client)) {
            if (connect_broker(client) == MQTTCLIENT_SUCCESS) {
                MQTTClient_subscribe(client, filter, QOS);
                printf("subscribed to %s\n", filter);
            } else {
                sleep(5);
                continue;
            }
        }
        sleep(1);
    }
    MQTTClient_disconnect(client, TIMEOUT);
    MQTTClient_destroy(&client);
    return 0;
}

Hem mesajı hem de topic string'ini serbest bırakmak zorunludur; ikisinden birini unutmak Paho C programlarındaki en yaygın bellek sızıntısıdır. Callback'i kısa tutun. İşleme yavaşsa (flash'a yazma, bir HTTP API'sini çağırma), payload'u bir kuyruğa kopyalayıp geri dönün, çünkü callback kütüphanenin alım thread'inde çalışır.

Publisher'ı hiç kod yazmadan da izleyebilirsiniz:

mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-4821 -t "connectmq/demo/paho-c-4821/#" -v

ARM Kart için Çapraz Derleme

Yocto veya Buildroot imajları için Paho C'nin hazır tarifleri vardır. Hızlı bir manuel çapraz derleme için CMake'i araç zincirinize ve çapraz derlenmiş bir OpenSSL'e yönlendirin:

cmake -B build-arm \
  -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
  -DCMAKE_SYSTEM_NAME=Linux \
  -DPAHO_WITH_SSL=TRUE \
  -DOPENSSL_ROOT_DIR=/opt/sysroot/usr \
  -DPAHO_BUILD_SHARED=TRUE -DPAHO_BUILD_SAMPLES=FALSE \
  -DCMAKE_INSTALL_PREFIX=/opt/paho-arm -DCMAKE_INSTALL_LIBDIR=lib
cmake --build build-arm -j"$(nproc)"
cmake --install build-arm

aarch64-linux-gnu-gcc -O2 -o publisher publisher.c \
  -I/opt/paho-arm/include -L/opt/paho-arm/lib -lpaho-mqtt3cs -lssl -lcrypto -lpthread

# Binary'yi ve paylaşımlı kütüphaneyi karta kopyala
scp publisher /opt/paho-arm/lib/libpaho-mqtt3cs.so* root@gateway:/usr/local/lib/

Kartta publisher dosyasını /usr/local/bin dizinine taşıyın ve ldconfig çalıştırın. Ayrı bir .so dağıtmaktan kaçınmak için Paho statik kütüphane olarak da derlenebilir (-DPAHO_BUILD_STATIC=TRUE), ancak o zaman o binary için OpenSSL güncellemeleri sizin sorumluluğunuzdadır. Hedef imajın bir CA paketi içerdiğini kontrol edin; minimal imajlarda çoğu zaman bulunmaz ve kod doğru olsa bile TLS bir sertifika doğrulama hatasıyla başarısız olur. Aynı gateway yerel bir dokunmatik ekranı da yönetiyorsa, Qt MQTT client eğitimi HMI tarafını C++ ile nasıl geliştireceğinizi gösterir; MQTT client'larını test etme yazısı da bu kodu CI altına nasıl alacağınızı ele alır.

QoS, Kalıcılık ve Çevrimdışı Tamponlama

Gateway'ler bağlantıyı kaybeder; bu yüzden bir kesinti sırasında veriye ne olacağına baştan karar verin. Yukarıdaki client, yoldaki QoS 1 mesajlarını yalnızca bellekte tutan MQTTCLIENT_PERSISTENCE_NONE kullanır; süreç teslimatın ortasında yeniden başlarsa bu mesajlar kaybolur. Bunun yerine Paho, yoldaki mesajları diske kalıcı olarak yazabilir: MQTTClient_create çağrısının son iki argümanı olarak MQTTCLIENT_PERSISTENCE_DEFAULT ve bir dizin yolu verin, cleansession = 0 ile bağlanın; kütüphane yeniden bağlandıktan sonra henüz onaylanmamış her şeyi yeniden gönderir. Flash depolamada bunu yazma aşınmasına karşı tartın, çünkü ucuz bir SD kartta her mesajı kalıcı yazmak kartın ömrünü kısaltır.

Kalıcılık yalnızca kütüphaneye zaten teslim edilmiş mesajları kapsar. Örnekte bağlantı kopukken alınan okumalar hiçbir zaman MQTTClient_publishMessage'a ulaşmaz, çünkü döngü yeniden bağlanana kadar publish etmeyi atlar. Her okumaya ihtiyacınız varsa, bunları yerel bir halka tampona (bir dosya veya SQLite veritabanı) yazın ve yeniden bağlandıktan sonra en eskiden başlayarak boşaltın; back end'in her değeri doğru yere koyabilmesi için payload'da orijinal zaman damgasını tutun.

QoS seçimi veriye göre yapılır. Birkaç saniyede bir yenilenen periyodik telemetri QoS 0 veya 1 ile sorunsuzdur; komutlar ve alarmlar QoS 1'i hak eder. QoS 2 ek bir gidiş-dönüş ekler ve gateway'lerde nadiren gerekir. Ücretsiz ConnectMQ Serverless planı QoS 1'e kadar destekler; ücretli planlar QoS 2 ekler. MQTT QoS seviyeleri rehberi teslimat garantilerini ayrıntılı olarak açıklar.

systemd Servisi Olarak Çalıştırma

/etc/systemd/system/mqtt-publisher.service:

[Unit]
Description=Paho MQTT C publisher
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/publisher
Environment=MQTT_CLIENT_ID=YOUR_CLIENT_ID
Environment=MQTT_USERNAME=YOUR_USERNAME
Environment=MQTT_PASSWORD=YOUR_PASSWORD
Environment=MQTT_TOPIC_PREFIX=acme/plant1/gateway1
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now mqtt-publisher
journalctl -u mqtt-publisher -f

Yukarıdaki kimlik bilgileri özel broker'ınızdan gelir. ConnectMQ gibi yönetilen bir MQTT broker ile broker'ı portalda oluşturur ve üretilmiş bir kullanıcı adı, parola ve client ID alırsınız; client ID'ler bu kimlik bilgilerine bağlıdır, bu yüzden her gateway'e kendi setini verin. Birkaç cihazdan fazlası için gizli bilgileri yalnızca root'un okuyabildiği bir EnvironmentFile dosyasına taşıyın.

Dönüş Kodları ve Sorun Giderme

Dönüş kodu / belirti Anlamı Neyi kontrol etmeli
TLS ile bağlanırken -1 (MQTTCLIENT_FAILURE) Handshake veya sertifika hatası CA paketi yolu, sistem saati (tarih yanlışsa TLS başarısız olur), host adı
4 Hatalı kullanıcı adı veya parola Kimlik bilgileri, env dosyasında sondaki satır sonu
5 Yetkisiz Client ID, verilen kimlik bilgileriyle eşleşmiyor
2 Identifier rejected Boş veya yinelenen client ID
8883'te bağlantı askıda kalıyor Firewall portu engelliyor openssl s_client -connect broker.connectmq.com:8883 ile test edin
Bellek günler içinde büyüyor Mesajlar veya topic'ler serbest bırakılmıyor Her callback yolunda MQTTClient_freeMessage ve MQTTClient_free

Saat sorunu gömülü Linux için özellikle vurgulanmalıdır: gerçek zamanlı saati olmayan kartlar 1970 yılında açılır ve NTP senkronize olana kadar her sertifika "henüz geçerli değil" görünür. Bu sorun sizi etkiliyorsa servisinizi time-sync.target sonrasına sıralayın. Diğer TLS hata desenleri MQTT TLS handshake failed yazısında ele alınır.

ConnectMQ ile Başlayın

C gateway'iniz gerçek bir broker'a hazır. app.connectmq.com üzerinde kredi kartı gerekmeden ücretsiz bir Serverless broker oluşturun, üretilen kimlik bilgilerini systemd unit dosyasına ekleyin ve mesajların portalın canlı topic explorer'ında belirdiğini izleyin. Filo büyüdüğünde aynı binary, kod değişikliği olmadan ücretli, dedicated veya BYOC broker'lara bağlanır.

Sık Sorulan Sorular

TLS'li senkron API için paho-mqtt3cs, TLS'li asenkron API için paho-mqtt3as kütüphanesine link edin. Sonunda s olmayan varyantlarda SSL desteği yoktur.

Paho MQTT C client otomatik olarak yeniden bağlanır mı?

Senkron MQTTClient API'si bağlanmaz; bu yüzden MQTTClient_isConnected ile kontrol edip yeniden bağlanmayı kendiniz yapın. Asenkron MQTTAsync API'si bir automaticReconnect seçeneği sunar.

Paho C, MQTT 5'i destekliyor mu?

Evet. Client'ı MQTTClient_createWithOptions ve MQTTVERSION_5 ile oluşturun, ardından MQTTClient_connect5 ve MQTTClient_publish5 fonksiyon ailesini kullanın; ConnectMQ hem MQTT 3.1.1 hem de 5.0'ı kabul eder.

Paho MQTT C ne kadar bellek gerektirir?

Tipik gömülü Linux gateway'leri için yeterince küçüktür, ancak kesin bellek kullanımı derleme seçeneklerinize ve OpenSSL'e bağlıdır. Hedef cihazınızda, gerçekten link ettiğiniz varyantla ölçün.

İlgili yazılar

Test, Betik ve Hata Ayıklama için En İyi MQTT CLI Araçları

mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, MQTT.js CLI, curl ve mqttui'nin uygulamalı karşılaştırması; betik yazma ve hata ayıklama için doğru aracı seçebilmeniz için aynı test broker.connectmq.com üzerinde çalıştırıldı.

7 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.