Arduino-kompatible MQTT-Bibliotheken mit QoS-Unterstützung

Einleitung

Bei Getting delayed readings from ESP8266/MQTT sind wir darüber gestolpert, dass wir für Telemetriecode wie

if (mqtt_publisher.publish(payload)) {
  Serial.println("mqtt: success");
} else {
  Serial.println("mqtt: failure");
}

gerne bessere Zuverlässigkeit erreichen würden.

MQTT QoS

Ab dem MQTT quality-of-service level 1 wird so etwas möglich:

QoS 1

QoS level 1 guarantees that a message is delivered at least one time to the receiver. The sender stores the message until it gets a PUBACK packet from the receiver that acknowledges receipt of the message. It is possible for a message to be sent or delivered multiple times.

What is MQTT Quality of Service (QoS) 0,1, & 2? – MQTT Essentials: Part 6

Korrekt, so etwas würde u.U. nur mit einem Acknowledgement Verfahren funktionieren, wie es MQTT QoS level 1 und 2 bietet, zumindest bis zum MQTT Broker.

Dabei wertet der Client/Publisher im günstigsten Fall die vom MQTT Broker versendete PUBACK Antwort erfolgreich aus, bevor er im Code “success” signalisiert.

1 Like

Schauen wir erstmal auf die…

Adafruit MQTT Library

QoS konfigurieren

Da hier der Adafruit_MQTT_Publisher verwendet wird, könnte man einmal folgendes versuchen, um die Situation zu verbessern:

// Let's get real.
#define MQTT_QOS 1

// Setup MQTT publishing handler.
Adafruit_MQTT_Publish mqtt_publisher = Adafruit_MQTT_Publish(&mqtt, MQTT_TOPIC, MQTT_QOS);

Unter der Haube I

Ich bin gespannt, wie dabei das Laufzeitverhalten im Erfolgs- bzw. v.a. im Fehlerfall auf dem ESP8266 aussieht bzw. ob das einfach so tut. Der Code von Adafruit_MQTT::publish(...) sieht aber schonmal gut aus: Wenn dort im Falle von qos > 0 der Ruf nach

readFullPacket(buffer, MAXBUFFERSIZE, PUBLISH_TIMEOUT_MS);

so lange blockierend wartet wie über PUBLISH_TIMEOUT_MS angegeben wird, dann sollte man summa summarum über

if (mqtt_publisher.publish(payload)) {
  Serial.println("mqtt: success");
} else {
  Serial.println("mqtt: error or timeout");
}

hoffentlich zuverlässigere Ergebnisse bekommen, was erfolgreiche Übertragungen und deren Bestätigungen angeht.

P.S.: Dies ist der voreingestellte Wert für PUBLISH_TIMEOUT_MS, der aber selbstverständlich jederzeit verändert werden können sollte:

Unter der Haube II

Der Code von Adafruit_MQTT_Client::readPacket(...) pollt im Intervall MQTT_CLIENT_READINTERVAL_MS nach neuen Daten bis der Timeout erreicht ist. Hier wird also wie erwartet blockierend auf die PUBACK Antwort des MQTT Brokers gewartet.

Auch dieser Wert könnte ggf. angepasst werden.

Fazit

Das sieht vom Code her anständig aus. Nebenläufigkeit im userspace code kann dadurch zwar vermutlich nicht erzielt werden, da die Funktion delay(MQTT_CLIENT_READINTERVAL_MS); aus Sicht des Sketchs blockiert…

delay(ms) pauses the sketch for a given number of milliseconds and allows WiFi, TCP/IP [and other] tasks to run.

ESP8266 Arduino Core Reference: Timing and delays

… aber für unsere bescheidenen Anwendungen müsste das in Ordnung sein.

pubsubclient: Arduino Client for MQTT

Hintergrund

Für andere Anwendungen mit entsprechenden Anforderungen gibt es auch (angeblich?) asynchron arbeitende MQTT Bibliotheken für Arduino, z.B. eben diese MQTT Bibliothek von Nick O’Leary: https://pubsubclient.knolleary.net/.

Wir wollten unsere Firmwares immer mal verbessern und genau diese Bibliothek dafür verwenden, weil es gerüchtete, sie wäre besser als die von Adafruit.

QoS?

Hier steht allerdings gleich schon zu Beginn des README dran:

Limitations

  • It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.

Schade!

Fazit

Wir sollten hier unsere Überlegungen also nocheinmal überdenken. Die MQTT Bibliothek von Adafruit hat durchaus während des letzten Jahres ein paar updates erhalten, so dass man sie nicht von vornherein mehr unbedingt als “verwaist” abtun sollte. Gerade wenn das mit QoS level 1 gut klappen sollte, sollten wir sie aktiv beibehalten wollen.

Gut, dass wir das rechtzeitig herausgefunden haben ;]. Danke an @wtf und @weef für die Impulse.

Nächster Halt: ESP

Beide oben genannten Bibliotheken sind insofern “voll” Arduino-kompatibel, als dass man sie auch auf Microchip neé Atmel AVR MCUs einsetzen kann, also anything that supports Arduino’s Client interface (like Ethernet shield).

Hier können wir auch langsam mal über den Tellerrand hinaus schauen und tatsächlich bessere/modernere Bibliotheken verwenden, die primär auf die aktuell populäreren Targets ESP8266 sowie ESP32 abzielen.

Dabei nimmt man zwar in Kauf, nicht mehr Arduino/AVR-kompatibel zu sein, aber in der Praxis fliegt sowieso niemand diese (teuren) Ethernet Shields im Feld, richtig?

Native MQTT client library for ESP8266

Hierunter versteht man die “native” Implementierung gegen das ESP SDK. Den Anfang machte hier die bekannte Native MQTT client library for ESP8266 von Tuan.

ESP32 MQTT Library

Mittlerweile hat sie es als GitHub - espressif/esp-mqtt: ESP32 mqtt component direkt ins “latest” ESP SDK (ab IDFv3.2 geschafft): https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/protocols/mqtt.html.

Das ist eine grandiose Sache, weil zu erwarten ist, dass diese Bibliothek rock-solid sein wird, wenn Espressif sie schon direkt mitliefert.

Allerdings Obacht

Daran sieht man, dass sie noch nicht unbedingt reif für den Praxiseinsatz sein könnte. Auch eine Suche in den GitHub issues nach “MQTT” verheißt noch nichts Gutes: Gerade mit dem ESP32 sind dort auffällig viele Fehlermeldungen zu “Guru Meditation” zu finden, welche auch beim ESP einen core panic signalisiert.

Bewertung

Die (ESP32) Maschine hat halt doch zwei Cores, da muss man auf OS Ebene halt doch gleich threadsafe sein, sonst wird das alles nichts ;]. Wenn man also nicht direkt am SDK mit anpacken will, muss man also vermutlich noch ein wenig warten, bis da die notwendigen Verbesserungen von Upstream kommen. Trotzdem halte ich die Entwicklung auch jetzt schon für vielversprechend.

P.S.: Wäre spannend, hier mal zu schauen, welche MQTT Implementierungen Pycom und CircuitPython hier auf das ESP-IDF aufsetzen, falls es dort bereits welche gibt.

1 Like

Async MQTT client for ESP8266 and ESP32

Der gute Marvin Roger, der der Welt sowohl die The Homie convention als auch das GitHub - homieiot/homie-esp8266: 💡 ESP8266 framework for Homie, a lightweight MQTT convention for the IoT geschenkt hat, hat die darin verwendete MQTT Implementierung schon längst als unabhängige Bibliothek ausgekoppelt:

Doku: async-mqtt-client :: viewdocs.io

Features

  • Compliant with the 3.1.1 version of the protocol
  • Fully asynchronous
  • Subscribe at QoS 0, 1 and 2
  • Publish at QoS 0, 1 and 2
  • SSL/TLS support

Unter der Haube

Die beiden Bibliotheken ESPAsyncTCP (für ESP8266) sowie AsyncTCP (für ESP32) bedienen ihrerseits recht ohne Umschweife die entsprechenden Funktionen des ESP SDKs, dessen IP Implementierung allem Anschein nach lwIP - Wikipedia ist.

Bewertung

Das hört sich wirklich vielversprechend an und vor dem Hintergrund, dass das per Homie schon auf ein paar zig Geräte ausgerollt und in der Praxis getestet sein wird, kann man davon ausgehen, dass das Teil auch gut rennt - und zwar wie versprochen auf beiden Plattformen (ESP8266 und ESP32). Da ich das oben beschriebene Featureset so noch nirgendwo in Kombination gesehen habe, wäre das bis auf weiteres mein persönlicher Favorit für zukünftige Firmwareversionen, sofern keine anderen Gründe dagegen sprechen.

Was interessiert das Geschwätz von gestern ;]. Auch hier scheint es gerade im Kontext des ESP32 noch Probleme mit der Nebenläufigkeit durch den asynchronen Betrieb der Bibliothek auf den zwei Cores zu geben:

Fazit

Funktioniert bestimmt in den meisten Fällen gut und ist bestimmt auch gut ausgereift. Allerdings sind scheinbar noch nicht alle race conditions ausgemerzt und können in der Praxis unter bestimmten Umständen zu panic errors führen.

Todo

  • Unter die Haube schauen, um einen Blick auf das Laufzeitverhalten zu werfen.
  • Prüfen, ob sich der Eumel auch gut mit sleep/deepsleep verträgt.
1 Like

About

This library bundles the lwmqtt MQTT 3.1.1 client and adds a thin wrapper to get an Arduino like API.

Kompatibilitätsliste mit Beispielen

Bewertung

495 commits, 42 releases, 10 contributors - warum ist uns diese Bibliothek nicht schon früher aufgefallen?
Danke, @weef!

1 Like

Jene Bibliotheken, die speziell für MQTT-over-GSM mit SIM800 und QoS 0-2 geeignet sind (GPLv3) , sind an dieser Stelle ebenfalls erwähnenswert:


Nochmals danke @weef!

Hier gibt es vielversprechende Neuigkeiten in Form dieser kürzlich eingegangenen commits:

1 Like

Hab hier noch eine MQTT Library gefunden:

wolfMQTT
Open source GPLv2
Version: 1.6.0
Release Date: 04/24/2020

Leider keine platform.io lib vorhanden und getestet hab ich sie auch nicht.

Highlights

  • Supports MQTT specs v3.1.1 and v5.0
  • QoS Levels 0-2
  • Support for TCP or TLS
  • Message integrity, security are still available (via the wolfSSL library)

Lightweight

  • Small size: 3.6kB
  • Able to leverage lightweight wolfSSL embedded SSL/TLS library

Portable

  • Simple API
  • Written in native C89
  • Minimal external dependencies
  • Less than 1200 lines of code
1 Like

Auch https://gitlab.com/librespacefoundation/satnogs/satnogs-tiny/-/merge_requests/5 zeigt, dass diese Bibliothek von anderen favorisiert wird. Per 256dpi/MQTT ist sie auch komfortabel via PlatformIO zu bekommen.

1 Like