MQTT Nachrichten mit dem Arduino empfangen & senden

Nachdem MQTT läuft, habe ich angefangen einen Arduino an MQTT anzubinden, um Nachrichten zu Empfangen und zu Senden. Als Test benutze ich einen Temperatursensor um Daten an MQTT zu senden. Um den Empfang zu testen nutze ich die Serielle Konsole von der Arduino GUI, um die Abonnierten Daten auszugeben. Der Aufbau des Arduinos ist eigentlich wie beim abfragen der DS18B20 Sensoren. In openHAB habe ich mir einen Schlater definiert, der seinen Status an MQTT übermittelt. Für die Darstellung der Temperatur habe ich ein Numerisches Feld angelegt.

MQTT-Arduino
image-319

Für den Arduino gibt es eine Library für MQTT. Diese Funktioniert mit dem Originalem Arduino Ethernet Shield. Es soll auch mit den Kompatiblen Boards funktionieren aber ich will bei meinen ersten Tests nichts riskieren. Nachher suche ich Fehler die nur durch ein falsches Shield herrühren. Ich teste es also vorsichtshalber mit meinem UNO und dem Originalem Ethernet Shield. Die originale Library ist auf der Seite des Entwicklers zu finden.

Es gibt auch einige angepasste Bibliotheken. Da ich demnächst auch mal ein CC3000 WIFI Modul testen will bin ich auf den CC3000 PubSubClient aufmerksam geworden.

openHAB

Wenn MQTT gestartet ist kann man die Konfiguration von openHAB erstellen.

openhab.cfg

In der Konfigurationsdatei muss die MQTT Bindung konfiguriert sein. Für die reine MQTT Anbindung reicht ein einzelner Eintrag.

mqtt:localbroker.url=tcp://localhost:1883

Weitere Einträge wie mqtt-eventbus sind nur notwendig, wen alle Statusänderung der Items an MQTT gesendet werden soll. Dies wird dann zentral in der openhab.cfg festgelegt. Da ich aber nur einen einzelnen Schalter verwenden möchte muss die MQTT-Bindung auf dem Item selber erfolgen.

test.items

Die Item Definition sieht wie folgt aus:

Group All

Switch Test_Switch1 "Schalter 1" (All) {mqtt=">[localbroker:/openHAB/Test_Switch1:command:ON:1],>[localbroker:/openHAB/Test_Switch1:command:OFF:0]"}
Switch Test_Switch2 "Schalter 2" (All)
Switch Test_Switch3 "Schalter 3" (All)
Number Test_Temp1 "Temperatur [%.2f] °C" (All) {mqtt="<[localbroker:/openHAB/Test_Temp1:state:default]"}

Wichtig sind hier die Bindungseinstellungen. Nur wenn der Topic 100% gleich ist wird dieser auch empfangen. Und das auf beiden Seiten. Auch die Gross- und Kleinschreibung ist wichtig. Ich hatte bei meinen ersten Versuchen nie eine Nachricht empfangen. Dies lag an einem Großbuchstaben. Eine genaue Beschreibung ist im openHAB Wiki zu finden.

default.sitemap

Zum Schluss noch die Sitemap:


sitemap default label="Test Menu"
{
   Frame {
      Switch item=Test_Switch1
      Switch item=Test_Switch2
      Switch item=Test_Switch3
      Text item=Test_Temp1
   }
}

 

Arduino Sketch

Ich habe diesmal alles im Sourcecode beschrieben. Ich hoffe das meiste ist verständlich.

</pre>
<pre>#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h> // http://knolleary.net/arduino-client-for-mqtt/

#include <OneWire.h> // http://www.arduino.cc/playground/Learning/OneWire
#include <DallasTemperature.h> // http://milesburton.com/index.php?title=Dallas_Temperature_Control_Library
#define ONE_WIRE_BUS 7

// MAC Adresse des Ethernet Shields
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
// IP des MQTT Servers
byte server[] = { 192, 168, 100, 33 };
// Ethernet Client zur Kommunikation des MQTT Clients
EthernetClient ethClient;
// MQTT Client zur Kommunikation mit dem Server
// Server - Variable des Types byte mit Serveradresse
// 1883 - Ist der Standard TCP Port
// callback - Function wird aufgerufen wen MQTT Nachrichten eintreffen. Am ende des Sketches
// ethClient - Angabe des Ethernet Clients
PubSubClient mqttClient(server, 1883, callback, ethClient);

// Einrichten des OneWire Bus um die Daten der Temperaturfühler abzurufen
OneWire oneWire(ONE_WIRE_BUS);
// Bindung der Sensoren an den OneWire Bus
DallasTemperature TempSensors(&oneWire);
// Variablen für die Temperatur
float tempC;
// Hilfsvariablen für die Convertierung der Temperatur in ein String
char tempBuffer[100];

void setup()
{
 // Initialisierung der Seriellen Schnittstelle
 // zur Ausgabe der empfangenen Nachrichten
 Serial.begin(9600);
 // Initialisierung des Ethernets
 Ethernet.begin(mac);
 // Initialisierung der Temperaur Sensoren
 TempSensors.begin();
}

void loop()
{
 // Aufbau der Verbindung mit MQTT falls diese nicht offen ist.
 if (!mqttClient.connected()) {
 mqttClient.connect("arduinoClient");
 // Abonieren von Nachrichten mit dem angegebenen Topic
 mqttClient.subscribe("/openHAB/Test_Switch1/#");
 }

 // Auslesen der Temperatur
 TempSensors.requestTemperatures();
 // Bei Änderungen zum vorherigen Wert Publizieren
 if (tempC != TempSensors.getTempCByIndex(0) ) {
 tempC = TempSensors.getTempCByIndex(0);
 // Publizierung des Wertes. Vorher Converierung vn float zu String.
 mqttClient.publish("/openHAB/Test_Temp1",dtostrf(tempC, 1, 2, tempBuffer));
 }
 mqttClient.loop(); // Schleife für MQTT
}

// ===========================================================
// Callback Funktion von MQTT. Die Funktion wird aufgerufen
// wenn ein Wert empfangen wurde.
// ===========================================================
void callback(char* topic, byte* payload, unsigned int length) {
 // Zähler
 int i = 0;
 // Hilfsvariablen für die Convertierung der Nachricht in ein String
 char message_buff[100];

 Serial.println("Message arrived: topic: " + String(topic));
 Serial.println("Length: " + String(length,DEC));

 // Kopieren der Nachricht und erstellen eines Bytes mit abschließender \0
 for(i=0; i<length; i++) {
 message_buff[i] = payload[i];
 }
 message_buff[i] = '\0';

 // Konvertierung der nachricht in ein String
 String msgString = String(message_buff);
 Serial.println("Payload: " + msgString);

}

Es ist übrigens empfehlenswert die Genauigkeit der DS18B20 herunter zu stellen. Somit entstehen nicht jede Sekunden mehrere Änderungen. Dies kann man natürlich auch im Programm unterbinden. Ich habe die Genauigkeit einfach auf 10 bit gesetzt.

Das Ergebnis sieht dann wie auf den Screenshots aus.

 

7 Comments

  1. Fabian

    Danke nochmals für die schnelle Antwort.

    Hätte aber noch ne Frage. Kann man auch mehrere Topics abonnieren. Oder wird in der CallBack Void nur das oben definierte Topic ausgegeben.

    Vielen Dank

  2. blueAdmin

    Nein du kannst mehrere Topics Abonnieren. Es wird zwar immer die selbe Callback-Funktion aufgerufen aber dort kannst du ja nach den Topics filtern.
    Zum Beispiel:
    if (String(topic) == “/openHAB/Nachtlicht/RED”) LEDredValue = round(msgString.toInt() * 2.55);
    if (String(topic) == “/openHAB/Nachtlicht/GREEN”) LEDgreenValue = round(msgString.toInt() * 2.55);
    if (String(topic) == “/openHAB/Nachtlicht/BLUE”) LEDblueValue = round(msgString.toInt() * 2.55)

    Bei den Subscription kannst du natürlich statt
    mqttClient.subscribe(“/openHAB/Nachtlicht/RED/#”);
    mqttClient.subscribe(“/openHAB/Nachtlicht/GREEN/#”);
    mqttClient.subscribe(“/openHAB/Nachtlicht/BLUE/#”);
    auch
    mqttClient.subscribe(“/openHAB/Nachtlicht/#”);
    abonnieren. Das Ergebniss wäre bei mir das selbe.
    Ich habe auch kein Performance unterschied, zwischen einem und mehreren Subscription bemerkt.

  3. Henning

    Hey interessanter Beitrag.
    Ich habe vor die Funkcodes einer 433MHz Fernbedienung (die bei Funksteckdosen beiliegt) mittels Arduino an openhab zu übertragen. Wäre das mit mqtt möglich oder ist die Übertragungszeit zu hoch? Habe im Netz irgendwo mal was von 15 sek gelesen. Es soll ja natürlich nicht zu lange dauern das nach einen Tastendruck etwas passiert.
    Gruß

    1. blueAdmin

      Hallo Henning,
      ja das geht. Genau das habe ich bei mir im Wohnzimmer gemacht. Ich hatte noch Funksteckdosen aus den Baumarkt übrig und hatte mir bei exp-tech ein 433 Mhz Funkmodul gekauft (http://blue-pc.net/2014/07/25/exptech-lieferung/). Das war bei mir mit einem Arduino auch schon länger im Einsatz. Ich hatte das über Web-Request gesteuert. Manchmal hing der Request aber und dann musste ich den Arduino neustarten. Man konnte es nicht wirklich verwenden. Am letzten Wochenende hatte ich mal per openHAB und MQTT die Funksteckdosen geschaltet. Das funktionierte bei mir sofort und jetzt gibt es auch keine Hänger mehr. MQTT ist dort viel robuster.
      Nur wenn mal wieder die Reichweite zu groß ist schaltet er natürlich nicht. Für einen tipp um eine bessere Antenne zu bauen wäre ich sehr dankbar. Im Moment habe ich ein 17cm Kupferdraht.
      Ich komme im Moment nur an meine openHAB config. Vielleicht schaffe ich es nächstes Wochenende mal einen Artikel zu schrieben. Der Sketch war auch noch sehr experimental programmiert.

      Viele Grüße,
      Jörg

  4. Thomas

    Danke erst einmal für die super Anleitung! Hat auf Anhieb funktioniert. Meine Frage, ist der Sketch schon für mehrere DS1820Bs geeignet und wenn ja, wie können diesen dann unterschiedliche mqtt-Topics zugewiesen werden?

    Viele Grüsse
    Thomas

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.