ESP32 ve Arduino

PubSubClient Buffer Boyutu: Büyük Mesaj Hatalarını Çözmek

ESP32 veya ESP8266'nızda büyük MQTT mesajları sessizce mi kayboluyor? PubSubClient buffer'ının nasıl çalıştığını, setBufferSize ile nasıl boyutlandırılacağını ve büyük JSON payload'ları RAM israf etmeden nasıl akıtacağınızı öğrenin.

Bir mikrodenetleyicide küçük PubSubClient buffer boyutunu taşıran bir MQTT paketinin diyagramı
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

publish() false döndürüyorsa veya gelen mesajlar callback'inize hiç ulaşmıyorsa en olası neden PubSubClient buffer boyutudur. Kütüphanenin varsayılan paket buffer'ı yalnızca 256 bayttır ve bundan büyük her şey hata mesajı vermeden başarısız olur. Bu rehber buffer'ın tam olarak neyi tutması gerektiğini, setBufferSize() ile nasıl boyutlandırılacağını ve büyük JSON payload'larını hiç büyük buffer ayırmadan nasıl akıtacağınızı açıklar.

Belirtiler

Buffer sorunları kafa karıştırıcıdır, çünkü hiçbir şey çökmez ve bağlantı açık kalır. Şu işaretlere dikkat edin:

  • mqtt.publish() bazı mesajlar için false döndürür, diğerleri için döndürmez; genellikle uzun olanlar için.
  • Küçük test mesajları callback'inize ulaşır, ancak backend'inizden gelen gerçek JSON asla ulaşmaz.
  • JSON belgenize bir alan daha eklemek telemetriyi aniden durdurur.
  • Bir topic'teki retained yapılandırma mesajları diğer client'larda görünür ama cihaza hiç ulaşmaz.

Bunların hepsi tek bir kuralla açıklanır: yalnızca payload değil, MQTT paketinin tamamı PubSubClient'ın dahili buffer'ına sığmalıdır.

Buffer Neyi Tutmalı?

PubSubClient hem giden hem de gelen paketler için tek bir buffer kullanır. Bir PUBLISH paketi için bu buffer şunları tutar:

Bölüm Boyut
Sabit başlık (paket türü ve bayraklar) 1 bayt
Kalan uzunluk alanı 1 ila 4 bayt (16 KB'a kadar olan çoğu mesaj için 2 bayt)
Topic uzunluk öneki 2 bayt
Topic string'i Topic'inizin uzunluğu
Paket tanımlayıcı 2 bayt, yalnızca gelen QoS 1 mesajlar için
Payload Mesajınızın uzunluğu

Kütüphane paketi oluştururken birkaç baytlık başlık alanı ayırır; bu yüzden pratikte topic uzunluğu + payload uzunluğu + yaklaşık 7 bayt, gelen QoS 1 mesajlar için de 2 bayt daha gerekir. 256 baytlık varsayılan ve connectmq/demo/sensor-4d2a/telemetry (36 karakter) gibi bir topic ile payload'unuz kabaca 210 baytın altında kalmalıdır. On okuma ve zaman damgası içeren bir JSON belgesi bunu kolayca aşar.

Aynı limit, kütüphanenin buffer içinde oluşturduğu CONNECT (client ID, kimlik bilgileri ve will mesajı) ve SUBSCRIBE gibi diğer paketler için de geçerlidir. Çok uzun bir parola ile uzun bir will mesajı, CONNECT paketini bile 256 baytın üzerine çıkarabilir; bu da başarısız bir connect() olarak görünür.

Çözüm 1: setBufferSize() ile Buffer'ı Büyütün

PubSubClient 2.8'den itibaren buffer'ı çalışma zamanında yeniden boyutlandırabilirsiniz. Eski eğitimler PubSubClient.h içindeki MQTT_MAX_PACKET_SIZE değerini düzenlemenizi söyler; bu hâlâ çalışır ama kırılgandır, çünkü kütüphane güncellemeleri dosyanın üzerine yazar ve değişiklik sessizce kaybolur.

#include <WiFi.h>
#include <PubSubClient.h>

WiFiClient net;
PubSubClient mqtt(net);

void setup() {
  Serial.begin(115200);
  // ... Wi-Fi'ye bağlanın ...

  mqtt.setServer("broker.connectmq.com", 1883);
  if (!mqtt.setBufferSize(2048)) {
    Serial.println("Could not allocate MQTT buffer");
  }
  Serial.printf("MQTT buffer size: %u\n", mqtt.getBufferSize());
}

Önemli noktalar:

  1. setBufferSize() bir uint16_t alır, bu yüzden teorik üst sınır 65.535 bayttır.
  2. Belleği yeniden ayırır ve ayırma başarısız olursa önceki buffer'ı yerinde bırakarak false döndürür. Dönüş değerini her zaman kontrol edin.
  3. connect() öncesinde çağırın. Bağlıyken yeniden boyutlandırmak çalışır ama israftır.
  4. getBufferSize() mevcut boyutu doğrular.

Ne kadar büyük olmalı? Beklediğiniz en büyük mesaj için yukarıdaki formülü kullanın, ardından yüzde 10 ila 20 pay ekleyin. ESP8266'da "ne olur ne olmaz" diye 16 KB ayarlamayın: o RAM bu durumda TLS için kullanılamaz ve bir sonraki el sıkışma başarısız olabilir. ESP32'de birkaç kilobayt sorun değildir.

Çözüm 2: beginPublish() ile Büyük Payload'ları Akıtın

Buffer'ı büyütmek ara sıra gelen büyük mesajlar için uygundur, ancak akış (streaming) RAM maliyetini tamamen ortadan kaldırır. beginPublish() başlığı ve topic'i yazar, ardından payload'u parçalar halinde doğrudan ağ client'ına yazmanıza izin verir. Buffer'dan yalnızca başlık geçer; böylece 256 baytlık bir buffer kilobaytlarca büyüklükte bir payload gönderebilir.

Tek şart: MQTT payload uzunluğunu başlığa koyduğu için onu önceden bilmeniz gerekir. ArduinoJson bunu measureJson() ile kolaylaştırır.

#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

const char* TOPIC = "connectmq/demo/sensor-4d2a/batch";

WiFiClient net;
PubSubClient mqtt(net);

bool publishBatch(const float* values, size_t count) {
  JsonDocument doc;                 // ArduinoJson 7
  doc["device"] = "sensor-4d2a";
  doc["uptime"] = millis() / 1000;
  JsonArray arr = doc["values"].to<JsonArray>();
  for (size_t i = 0; i < count; i++) arr.add(values[i]);

  size_t len = measureJson(doc);
  if (!mqtt.beginPublish(TOPIC, len, false)) {
    Serial.println("beginPublish failed");
    return false;
  }
  serializeJson(doc, mqtt);         // PubSubClient bir Print olduğu için bu akış halinde yazar
  return mqtt.endPublish() == 1;
}

200 float değerle payload 256 bayttan çok daha büyüktür, ancak varsayılan buffer yeterlidir. beginPublish(), write()/print() ve endPublish() PubSubClient 2.7 ve sonrasında mevcuttur.

Broker'ın yine tek bir MQTT mesajı gördüğünü ve bu mesajın broker limitinin içinde olması gerektiğini unutmayın. broker.connectmq.com adresindeki herkese açık MQTT test broker'ında limit mesaj başına 64 KB'tır.

Büyük Mesajları Almak

Akış yalnızca giden mesajlarda işe yarar. Gelen mesajlarda PubSubClient, callback'inizi çağırmadan önce paketin tamamını buffer'a okur. Paket buffer'dan büyükse kütüphane onu okur ve atar; callback'iniz hiç çağrılmaz. Hata da yoktur, log da.

Bu nedenle büyük yapılandırma belgeleri veya firmware manifestoları alan cihazlar için:

  • Buffer'ı en büyük gelen mesaj artı topic artı ek yük kadar ayarlayın.
  • Gelen mesajları kompakt tutun. Tam bir backend nesnesi yerine yalnızca cihazın ihtiyaç duyduğu alanları gönderin.
  • Büyük verileri, her biri limitin rahatça altında kalacak şekilde birkaç topic'e bölün; örneğin config/network ve config/sensors.
  • Firmware imajları gibi gerçekten büyük payload'lar için MQTT üzerinden bir URL yayınlayın ve dosyayı HTTPS üzerinden indirin.

Bilerek fazla büyük bir mesajla ne olduğunu test edin:

mosquitto_pub -h broker.connectmq.com -p 1883 -i sizetest-4d2a \
  -t "connectmq/demo/sensor-4d2a/cmd" \
  -m "$(head -c 400 /dev/zero | tr '\0' 'x')"

Varsayılan buffer ile cihaz bunu yok sayar. setBufferSize(1024) sonrasında callback 400 uzunluğuyla tetiklenir.

Hata Ayıklama Kontrol Listesi

Mesajlar kaybolduğunda bu listeyi izleyin:

  1. Ayarladığınızı düşündüğünüz boyutu doğrulamak için kurulumdan sonra mqtt.getBufferSize() yazdırın.
  2. Her yayından önce topic ve payload uzunluğunu loglayın: Serial.printf("%u + %u\n", strlen(topic), strlen(payload));.
  3. publish() dönüş değerini kontrol edin ve başarısız olduğunda boyut sorunlarını bağlantı sorunlarından ayırmak için mqtt.connected() yazdırın.
  4. ESP8266'da ESP.getMaxFreeBlockSize() değerini izleyin. Büyük bir buffer ile TLS birlikte bitişik belleği tüketebilir.
  5. Bir masaüstü client'ıyla karşılaştırın: mosquitto_sub mesajı alıyor ama cihaz almıyorsa buffer çok küçüktür.
  6. Mesajınızın cihaz buffer'ının yanı sıra broker'ınızın limiti içinde de olduğundan emin olun.

Bağlantının kendisi başarısız oluyorsa neden başka yerdedir; bkz. PubSubClient rc=-2 ve diğer durum kodları. Kütüphanenin eksiksiz anlatımı için PubSubClient eğitimimizi okuyun. Gelen mesajları da akış halinde işleyen bir kütüphaneye ihtiyacınız varsa ArduinoMqttClient rehberi alternatif bir yaklaşım gösterir; ESP32 IoT eğitimi ise bu teknikleri eksiksiz bir projede birleştirir.

Broker Mesaj Limitleri

Cihaz buffer'ı işin yalnızca yarısıdır. Her broker mesaj boyutunu da sınırlar ve limitin üzerindeki mesajlar reddedilir veya bağlantının kopmasına neden olur:

Ortam Maksimum mesaj boyutu
ConnectMQ herkese açık broker (broker.connectmq.com) 64 KB
ConnectMQ Serverless (Free) 256 KB
ConnectMQ Serverless (Paid) 1 MB

Gömülü cihazlar için düşük kilobaytlarda kalmak zaten iyi bir uygulamadır. Daha küçük mesajlar daha az RAM, daha hızlı TLS kayıtları ve zayıf Wi-Fi'de daha az yeniden iletim demektir.

ConnectMQ ile Daha Büyük Payload'lar Gönderin

Telemetriniz herkese açık broker'ı aştıysa yönetilen bir MQTT broker size özel kimlik bilgileri, TLS ve daha yüksek mesaj limitleri sağlar. ConnectMQ; MQTT 3.1.1 ve 5.0, tarayıcı içi bir MQTT client'ı ve cihazlarınızın tam olarak ne gönderdiğini inceleyebileceğiniz canlı bir topic gezgini sunan bir bulut MQTT broker'dır. app.connectmq.com adresinde ücretsiz bir Serverless broker oluşturun, kredi kartı gerekmez.

Sık Sorulan Sorular

Varsayılan PubSubClient buffer boyutu nedir?

MQTT_MAX_PACKET_SIZE olarak tanımlanan 256 bayt. Topic ve başlıklar dahil paketin tamamını kapsar, bu yüzden kullanılabilir payload daha küçüktür.

MQTT_MAX_PACKET_SIZE'ı mı düzenlemeliyim, yoksa setBufferSize() mı kullanmalıyım?

PubSubClient 2.8 veya sonrasında setBufferSize() kullanın. Kütüphane güncellemelerinden etkilenmez ve bellek ayırmanın başarılı olup olmadığını kontrol etmenizi sağlar.

publish() büyük bir mesajda neden false döndürüyor?

Topic, payload ve başlık toplamı buffer'ı aşıyor. Buffer'ı büyütün veya payload'u akıtmak için beginPublish() kullanın.

PubSubClient buffer'ından büyük mesajları alabilir mi?

Hayır. Aşırı büyük gelen mesajlar callback'iniz çalışmadan önce atılır. Buffer'ı büyütün veya gelen mesajları küçük tutun.

İlgili yazılar

ESP32 NTP MQTT Zaman Damgası: Her Mesaja Doğru Zaman Ekleyin

ESP32 saatini NTP ile eşitleyin, ISO 8601 ve epoch milisaniye zaman damgalarını biçimlendirin ve bunları MQTT payload'larına ekleyin; böylece ölçümler geç yayınlansa bile gerçek alınma zamanlarını korur.

6 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.