Wenn publish() den Wert false liefert oder eingehende Nachrichten nie Ihren Callback erreichen, ist die PubSubClient-Puffergröße die wahrscheinlichste Ursache. Der Standard-Paketpuffer der Bibliothek umfasst nur 256 Bytes, und alles Größere scheitert ohne Fehlermeldung. Dieser Leitfaden erklärt genau, was der Puffer aufnehmen muss, wie Sie ihn mit setBufferSize() dimensionieren und wie Sie große JSON-Payloads streamen, ganz ohne große Puffer anzulegen.
Die Symptome
Pufferprobleme sind verwirrend, weil nichts abstürzt und die Verbindung bestehen bleibt. Achten Sie auf diese Anzeichen:
mqtt.publish()liefert bei manchen Nachrichtenfalse, bei anderen nicht, typischerweise bei den längeren.- Kleine Testnachrichten kommen in Ihrem Callback an, das echte JSON aus Ihrem Backend aber nie.
- Ein zusätzliches Feld im JSON-Dokument stoppt plötzlich die Telemetrie.
- Retained Konfigurationsnachrichten auf einem Topic sind in anderen Clients sichtbar, erreichen das Gerät aber nie.
All das erklärt eine einzige Regel: Das gesamte MQTT-Paket, nicht nur die Payload, muss in den internen Puffer von PubSubClient passen.
Was der Puffer aufnehmen muss
PubSubClient verwendet einen einzigen Puffer für ausgehende und eingehende Pakete. Bei einem PUBLISH-Paket enthält dieser Puffer:
| Bestandteil | Größe |
|---|---|
| Fester Header (Pakettyp und Flags) | 1 Byte |
| Feld Remaining Length | 1 bis 4 Bytes (2 Bytes für die meisten Nachrichten bis 16 KB) |
| Längenpräfix des Topics | 2 Bytes |
| Topic-String | Länge Ihres Topics |
| Paket-ID | 2 Bytes, nur bei eingehenden QoS-1-Nachrichten |
| Payload | Länge Ihrer Nachricht |
Die Bibliothek reserviert beim Aufbau des Pakets einige Bytes für den Header, in der Praxis brauchen Sie also Topic-Länge + Payload-Länge + etwa 7 Bytes, bei eingehenden QoS-1-Nachrichten 2 weitere. Mit dem 256-Byte-Standard und einem Topic wie connectmq/demo/sensor-4d2a/telemetry (36 Zeichen) muss Ihre Payload unter etwa 210 Bytes bleiben. Ein JSON-Dokument mit zehn Messwerten und Zeitstempeln überschreitet das mühelos.
Dieselbe Grenze gilt für andere Pakete, die die Bibliothek im Puffer aufbaut, darunter CONNECT (Client ID, Zugangsdaten und Will Message) und SUBSCRIBE. Ein sehr langes Passwort plus eine lange Will Message können sogar das CONNECT-Paket über 256 Bytes treiben, was sich als fehlgeschlagenes connect() zeigt.
Lösung 1: Den Puffer mit setBufferSize() vergrößern
Seit PubSubClient 2.8 können Sie den Puffer zur Laufzeit in der Größe ändern. Ältere Tutorials empfehlen, MQTT_MAX_PACKET_SIZE in PubSubClient.h zu ändern; das funktioniert noch, ist aber fragil, weil Bibliotheksupdates die Datei überschreiben und die Änderung stillschweigend verschwindet.
#include <WiFi.h>
#include <PubSubClient.h>
WiFiClient net;
PubSubClient mqtt(net);
void setup() {
Serial.begin(115200);
// ... WLAN verbinden ...
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());
}
Die wichtigsten Punkte:
setBufferSize()erwartet einenuint16_t, die theoretische Obergrenze liegt also bei 65.535 Bytes.- Die Funktion alloziert den Speicher neu und liefert
false, wenn die Allokation scheitert; der bisherige Puffer bleibt dann erhalten. Prüfen Sie immer den Rückgabewert. - Rufen Sie sie vor
connect()auf. Eine Größenänderung während der Verbindung funktioniert, ist aber verschwenderisch. getBufferSize()bestätigt die aktuelle Größe.
Wie groß sollte er sein? Wenden Sie die obige Formel auf Ihre größte erwartete Nachricht an und rechnen Sie 10 bis 20 Prozent Reserve hinzu. Setzen Sie auf einem ESP8266 nicht "für alle Fälle" 16 KB: Dieser RAM fehlt dann für TLS, und der nächste Handshake kann scheitern. Auf einem ESP32 sind ein paar Kilobyte kein Problem.
Lösung 2: Große Payloads mit beginPublish() streamen
Einen größeren Puffer zu setzen ist für gelegentliche große Nachrichten in Ordnung, Streaming vermeidet die RAM-Kosten jedoch vollständig. beginPublish() schreibt Header und Topic und lässt Sie dann die Payload stückweise direkt in den Netzwerk-Client schreiben. Nur der Header läuft durch den Puffer, sodass ein 256-Byte-Puffer eine Payload von vielen Kilobytes senden kann.
Der Haken: Sie müssen die Payload-Länge im Voraus kennen, denn MQTT trägt sie in den Header ein. ArduinoJson macht das mit measureJson() einfach.
#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 ist ein Print, daher wird gestreamt
return mqtt.endPublish() == 1;
}
Bei 200 Float-Werten ist die Payload weit größer als 256 Bytes, und doch reicht der Standardpuffer aus. beginPublish(), write()/print() und endPublish() stehen ab PubSubClient 2.7 zur Verfügung.
Beachten Sie, dass der Broker trotzdem eine einzelne MQTT-Nachricht sieht, die innerhalb seines Limits liegen muss. Auf dem öffentlichen MQTT-Broker unter broker.connectmq.com, der für Tests und Prototypen gedacht ist, liegt das Limit bei 64 KB pro Nachricht.
Große Nachrichten empfangen
Streaming hilft nur bei ausgehenden Nachrichten. Eingehende Nachrichten liest PubSubClient vollständig in den Puffer, bevor Ihr Callback aufgerufen wird. Ist das Paket größer als der Puffer, liest die Bibliothek es und verwirft es, und Ihr Callback wird nie aufgerufen. Es gibt weder einen Fehler noch einen Logeintrag.
Für Geräte, die große Konfigurationsdokumente oder Firmware-Manifeste empfangen, gilt daher:
- Setzen Sie den Puffer auf die größte eingehende Nachricht plus Topic plus Overhead.
- Halten Sie eingehende Nachrichten kompakt. Senden Sie nur die Felder, die das Gerät braucht, statt eines kompletten Backend-Objekts.
- Teilen Sie große Datenmengen auf mehrere Topics auf, etwa
config/networkundconfig/sensors, jeweils deutlich unter dem Limit. - Für wirklich große Payloads wie Firmware-Images veröffentlichen Sie per MQTT eine URL und laden die Datei über HTTPS herunter.
Testen Sie, was mit einer absichtlich zu großen Nachricht passiert:
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')"
Mit dem Standardpuffer ignoriert das Gerät sie. Nach setBufferSize(1024) wird der Callback mit einer Länge von 400 ausgelöst.
Checkliste zum Debuggen
Gehen Sie diese Liste durch, wenn Nachrichten verloren gehen:
- Geben Sie nach dem Setup
mqtt.getBufferSize()aus, um zu bestätigen, dass die gewünschte Größe gesetzt ist. - Protokollieren Sie vor jedem Publish die Länge von Topic und Payload:
Serial.printf("%u + %u\n", strlen(topic), strlen(payload));. - Prüfen Sie den Rückgabewert von
publish()und geben Sie bei einem Fehlermqtt.connected()aus, um Größenprobleme von Verbindungsproblemen zu unterscheiden. - Beobachten Sie auf dem ESP8266
ESP.getMaxFreeBlockSize(). Ein großer Puffer plus TLS kann den zusammenhängenden Speicher erschöpfen. - Vergleichen Sie mit einem Desktop-Client: Empfängt
mosquitto_subdie Nachricht, das Gerät aber nicht, ist der Puffer zu klein. - Stellen Sie sicher, dass Ihre Nachricht sowohl innerhalb des Broker-Limits als auch innerhalb des Gerätepuffers liegt.
Scheitert schon die Verbindung, liegt die Ursache woanders; siehe PubSubClient rc=-2 und weitere State-Codes. Eine vollständige Einführung in die Bibliothek bietet unser PubSubClient-Tutorial. Brauchen Sie eine Bibliothek, die auch eingehende Nachrichten streamt, zeigt der ArduinoMqttClient-Leitfaden einen alternativen Ansatz, und das ESP32-IoT-Tutorial verbindet diese Techniken zu einem kompletten Projekt.
Nachrichtenlimits des Brokers
Der Gerätepuffer ist nur die halbe Wahrheit. Jeder Broker begrenzt ebenfalls die Nachrichtengröße, und Nachrichten über dem Limit werden abgelehnt oder führen zu einem Verbindungsabbruch:
| Umgebung | Maximale Nachrichtengröße |
|---|---|
| Öffentlicher MQTT-Broker von ConnectMQ (broker.connectmq.com) | 64 KB |
| ConnectMQ Serverless (Free) | 256 KB |
| ConnectMQ Serverless (Paid) | 1 MB |
Für Embedded-Geräte ist es ohnehin gute Praxis, im niedrigen Kilobyte-Bereich zu bleiben. Das gilt auch für ESP32-basierte Retrofit-Sensoren an Bestandsmaschinen: Statt großer Sammeldokumente lieber einzelne Werte auf sprechende Topics publizieren, etwa nach der ISA-95-Struktur eines Unified Namespace (UNS) wie acme/werk-muenchen/presswerk/linie-2/presse-1/temperatur. Kleinere Nachrichten bedeuten weniger RAM, schnellere TLS-Records und weniger Neuübertragungen bei schwachem WLAN.
Größere Payloads mit ConnectMQ senden
Ist Ihre Telemetrie dem öffentlichen Broker entwachsen, bietet Ihnen ein verwalteter MQTT-Broker private Zugangsdaten, TLS und höhere Nachrichtenlimits. ConnectMQ ist ein Cloud-MQTT-Broker mit MQTT 3.1.1 und 5.0, einem MQTT-Client im Browser und einem Live-Topic-Explorer, in dem Sie genau prüfen können, was Ihre Geräte senden. Erstellen Sie einen kostenlosen Serverless Broker unter app.connectmq.com, ohne Kreditkarte.
Häufig gestellte Fragen
Wie groß ist der Standardpuffer von PubSubClient?
256 Bytes, definiert als MQTT_MAX_PACKET_SIZE. Er umfasst das gesamte Paket einschließlich Topic und Headern, die nutzbare Payload ist also kleiner.
Sollte ich MQTT_MAX_PACKET_SIZE ändern oder setBufferSize() verwenden?
Verwenden Sie setBufferSize() ab PubSubClient 2.8. Die Einstellung übersteht Bibliotheksupdates, und Sie können prüfen, ob die Allokation erfolgreich war.
Warum liefert publish() bei einer großen Nachricht false?
Topic plus Payload plus Header überschreiten den Puffer. Vergrößern Sie den Puffer oder streamen Sie die Payload mit beginPublish().
Kann PubSubClient Nachrichten empfangen, die größer als sein Puffer sind?
Nein. Übergroße eingehende Nachrichten werden verworfen, bevor Ihr Callback läuft. Vergrößern Sie den Puffer oder halten Sie eingehende Nachrichten kleiner.



