ESP8266 D1 Mini MQTT - Der Start mit MQTT
Nick
Inhaltsverzeichnis [Verbergen]
Was ist MQTT
MQTT steht für Message Queuing Telemetry Transport und ist ein Nachrichtenprotokoll, welches geringe Anforderungen an das Netzwerk und die Hardware stellt. Die Entwicklerseite beschreibt das Protokoll als ein machine-to-machine oder IoT Verbindungsprotokoll.
MQTT bietet ein sehr gutes Error-Handling, integriert QoS und bietet Funktionen um die Übertragung einer Nachricht zu versichern. Die Liste an Möglichkeiten ist groß. Deshalb werden wir uns zuerst mit den wichtigsten Dingen beschäftigen und einen Vergleich zu HTTP aufstellen.
HTTP vs MQTT
Wo besteht der Vorteil in der Nutzung von MQTT im Vergleich zu HTTP?
Wie oben bereits erwähnt, ist MQTT ein sehr schlankes Protokoll, welches gut im Bereich von Netzwerken mit wenig Bandbreite genutzt werden kann. Der kleinste mögliche MQTT-Header hat eine Größe von gerade mal 2 Byte. So ist der Overhead an Daten, die durch das Netzwerk fließen, im Gegensatz zur HTTP sehr gering.
Oft wird MQTT im Bereich von batteriebetriebenen Sensoren im IoT-Bereich eingesetzt, da das Protokoll wenig Ressourcenanforderungen an die Hardware stellt und es somit nur einen geringen Stromverbrauch gibt.
HTTP basiert auf einer Request-, Response-Architekturen (Client-Server-Modell). Dadurch ist es nicht ohne weiteres möglich eine Nachricht vom Server an den Client zu pushen. MQTT hingegen arbeitet bidirektional und es können sowohl Anfragen an den Server gesendet als auch empfangen werden (ohne vorherigen Request).
Unter MQTT ist es möglich, im Gegensatz zur 1:1-Kommunikation von HTTP, eine Nachricht an mehrere Empfänger zu schicken (1:n).
Es gibt noch weitere Vorteile, wie z.B. die 3 Arten von QoS. Allerdings sollen diese Informationen für den Einstieg reichen.
Kleiner Fakt: Wofür eignet sich ein Protokoll, welches vom Server zum Client pushen kann und gleichzeitig in Netzwerken mit geringen Bandbreite funktioniert? Instant Messaging… und genau das hat sich Facebook in ihrem Messanger zu Nutze gemacht.
Grundlegende Funktionsweise
MQTT ist ein Client-Server-Protokoll und basiert auf einer Publish / Subscribe Architektur. Der Server wird in diesem Fall Broker genannt und ist zuständig für die Koordination der Nachrichten. Der Broker empfängt Nachrichten und gibt diese an den richtigen Empfänger weiter. Jede Nachricht in einer MQTT-Kommunikation enthält eine Topic, also quasi ein Thema. Die Topics sind hierarchisch, wie in einer Baumstruktur, aufgebaut.
Hierzu ein Beispiel:
makesmart/forum/user/findo1
makesmart/forum/artikel/id5
Im Makesmart-Forum gibt es also verschiedene Benutzer und verschiedene Artikel. Ein Client kann nun an eine dieser Topics eine Nachricht schicken. Unter MQTT nennt man das eine PUBLISH. Alle Clients die dieses Topic nun SUBSCRIBED haben empfangen die Nachricht.
Außerdem ist es möglich Wilcards zu SUBSCRIBEN. makesmart/forum/artikel/# bedeutet, dass der Client alle Artikel aus dem makesmart-Forum empfängt. makesmart/+/artikel/id5 bedeutet, dass der Client alle Artikel mit der ID 5 aus allen verfügbaren makesmart-Foren empfängt.
Integration im D1 Mini
Um MQTT in Kombination mit dem ESP8266 nutzen zu können muss in der Aruino IDE zuerst eine passende MQTT-Libary eingebunden werden. Hier gibt es eine große Auswahl. Ich nutze für diesen Artikel die "PubSubClient"-Libary, die ihr einfach über den Bibliotheksverwalter Werkzeuge > Bibliotheken verwalten... hinzufügen könnt. Die hinzugefügte Libary wird oben im eurem Quellcode mit einem #include <PubSubClient.h> hinzugefügt.
Verbindung zu einem WLAN und MQTT-Broker
- #include <ESP8266WiFi.h>
- #include <PubSubClient.h>
- const char* SSID = "WIFI_SSID";
- const char* PSK = "WIFI_KEY";
- const char* MQTT_BROKER = "test.mosquitto.org";
- WiFiClient espClient;
- PubSubClient client(espClient);
- void setup() {
- Serial.begin(115200);
- setup_wifi();
- client.setServer(MQTT_BROKER, 1883);
- }
- void setup_wifi() {
- WiFi.begin(SSID, PSK);
- while (WiFi.status() != WL_CONNECTED) {
- delay(100);
- }
- Serial.println(WiFi.localIP());
- }
- void loop() {
- if (!client.connected()) {
- while (!client.connected()) {
- client.connect("ESP8266Client");
- delay(100);
- }
- }
- client.loop();
- }
Wie ich die Verbindung zu dem WLAN-Netzwerk aufbaue, werde ich hier nicht genauer erläutern, da es dazu schon gute Videos gibt.
In Zeile 6 wird eine weitere Konstante deklariert, in der die IP-Adresse oder die URL des Broker gespeichert wird.
Achtung: Der angegebene Broker exestiert wirklich und ist ein öffentlicher Test-Broker! Alles was ihr an diesen Server PUBLISHED, kann mitgelesen werden. Also eventuell keine Login-Kombi im Klartext senden.
In der Setup-Schleife wird dann definiert, zu welchem Broker sich der Client verbinden soll und unter welchen Port er dies machen soll. Sobald eine WIFI-Verbindung vom ESP zu eurem WLAN hergestellt wurde und die Loop-Schleife ausgeführt wird, beginnt der eigentliche Verbindungsaufbau zum Broker.
Erst wird überprüft, ob der Client schon verbunden ist. Wenn er das nicht ist, dann wird versucht eine Verbindung aufzubauen, solange bis diese letzen Endes steht. Beim nächsten Durchgang der Loop-Schleife wird dieser Teil dann ignoriert, sofern die Verbindung noch Aktiv ist.
Wichtig ist, dass wir uns den Befehl client.connect("ESP8266Client"); mal genauer ansehen:
Der Wert in der Klammer ist wichtig für die Kommunikation mit einem MQTT-Broker! Es handelt sich hierbei um eine eindeutige Client-ID. Jeder Client, der sich beim Broker meldet, muss für den Server eindeutig identifizierbar sein. Oft werden diese IDs automatisch generiert, in diesem Fall müsst ihr euch selbst was aussuchen.
An dieser Stelle gibt es sicher viele Möglichkeiten den Code sauberer zu gestalten, da es hier auch viele zusätzliche Funktionen gibt um mögliche Fehler auszumerzen und sich Error-Meldungen anzusehen. Aber für den ersten Code soll das reichen.
PUBLISH
Der PUBLISH ist sehr simple. Dazu fügt ihr nur unter den client.loop(); Befehl folgendes ein:
client.publish("/home/data", "Hello World");
Das wars! Der erste Wert ist die Topic, an die GEPUBLISHED wird. Der Zweite Wert ist der Payload bzw. der Wert, den ihr schicken wollt. Wenn ihr hier angekommen seid, dann könnt ihr zum Beispiel schon einen Temperatursensor mit dem D1 Mini basteln, der jede Minute den Wert an den MQTT-Broker sendet.
SUBSCRIBE
Der SUBSCRIBE ist etwas "komplexer", allerdings auch kein Akt.
Zuerst muss in der Setup-Funktion client.setCallback(callback); hinzugefügt werden. Damit wird angegeben, welche Funktion ausgeführt werden soll, wenn eine Nachricht empfangen wird.
Die Funktion heißt in dem Fall callback und hat von mir folgenden Inhalt bekommen:
Alle nötigen Informationen werden der Funktion übergeben.
Die empfangene Nachricht wird in payload übergeben. Der Payload ist ein Array voller Bytes, also Zahlen von 0-255, die erst zu einem Zusammenhängenden String umkonvertiert werden müssen. Dazu wird eine Schleife so oft durchlaufen, wie es Werte in dem Array payload[] gibt. Bei jedem Schleifendurchlauf wird der aktuelle Wert des Arrays von einem Byte in einen Char umgewandelt und an die vorher deklarierte Stringvariable msg angehängt. Nachdem die Schleife vollständig durchlaufen , steht die Nachricht im Klartext als String in der Variable msg.
Zum Schluss müssen wir noch irgendwo angeben, welche Topic eigentlich SUBSCRIBED werden soll.
Dazu fügen wir dem unter dem client.connect("ESP8266Client"); ein client.subscribe("test/test/baum");.
Tipps
Im ganzen Tutorial wird das Thema nur von einer Seite betrachtet. Sobald es zu den ersten Tests kommt, merkt man schnell , dass man immer eine Gegenseite benötigt: Sprich: Wenn ich meinen ESP8266 D1 Mini ein Topic SUBSCRIBEN lasse, dann brauche ich auch einen weitern Client, der auf dieser TOPIC etwas PUBLISHED. Hierzu möchte ich euch zwei Tools ans Herz legen.
- MQTT.fx [GUI-Tool unter Windows und Linux]
- mosquitto_client [Kommandozeilen-Tool unter Linux]
Beide Tools empfehle ich hier nur, weil ich damit positive Erfahrungen gemacht habe und es zu beiden Tools gute Dokumentationen und viele Internetbeiträge gibt. Selbstverständlich sind beide Tools kostenfrei.
Codeschnipsel
MQTT Publish
- #include <ESP8266WiFi.h>
- #include <PubSubClient.h>
- const char* SSID = "WIFI_SSID";
- const char* PSK = "WIFI_KEY";
- const char* MQTT_BROKER = "test.mosquitto.org";
- WiFiClient espClient;
- PubSubClient client(espClient);
- void setup() {
- Serial.begin(115200);
- setup_wifi();
- client.setServer(MQTT_BROKER, 1883);
- }
- void setup_wifi() {
- WiFi.begin(SSID, PSK);
- while (WiFi.status() != WL_CONNECTED) {
- delay(100);
- }
- Serial.println(WiFi.localIP());
- }
- void loop() {
- if (!client.connected()) {
- while (!client.connected()) {
- client.connect("ESP8266Client");
- delay(100);
- }
- }
- client.loop();
- client.publish("/home/data", "Hello World");
- }
MQTT Subscribe
- #include <ESP8266WiFi.h>
- #include <PubSubClient.h>
- const char* SSID = "WIFI_SSID";
- const char* PSK = "WIFI_KEY";
- const char* MQTT_BROKER = "test.mosquitto.org";
- WiFiClient espClient;
- PubSubClient client(espClient);
- void setup() {
- Serial.begin(115200);
- setup_wifi();
- client.setServer(MQTT_BROKER, 1883);
- client.setCallback(callback);
- }
- void setup_wifi() {
- WiFi.begin(SSID, PSK);
- while (WiFi.status() != WL_CONNECTED) {
- delay(100);
- }
- Serial.println(WiFi.localIP());
- }
- void loop() {
- if (!client.connected()) {
- while (!client.connected()) {
- client.connect("ESP8266Client");
- client.subscribe("test/test/baum");
- delay(100);
- }
- }
- client.loop();
- }
- void callback(char* topic, byte* payload, unsigned int length) {
- String msg;
- for (byte i = 0; i < length; i++) {
- char tmp = char(payload[i]);
- msg += tmp;
- }
- Serial.println(msg);
- }
Und damit: Viel Spaß beim smart maken mit makesmart...
Gruß
Nick