Kurzanleitung: ESP8266 mit MQTT an FHEM - selbstprogrammiert

Begonnen von betateilchen, 18 Juni 2023, 20:51:44

Vorheriges Thema - Nächstes Thema

betateilchen

(Vermutlich gibt es das alles schonmal irgendwo im Forum, aber aus aktuellem Anlass habe ich heute versucht, den kompletten Zusammenhang darzustellen)



Im Forum kam die Frage auf, wie man auf einen ESP8266 mit FHEM verbindet, ohne dafür spezielle Firmwares wie Tasmota, ESPeasy o.ä. zu verwenden. Also quasi "was muss ich in der Arduino IDE tun?"

Im Anhang findet sich ein kleiner Arduino Sketch, der dabei helfen soll, das Grundverständnis zu dem Thema zu schaffen.

Darüber hinaus gibt es noch zwei FHEM devices (MQTT2_SERVER und MQTT2_DEVICE), die für die Kommunikation zwischen FHEM und dem ESP8266 erforderlich sind.



Der Arduino Sketch

Die Verwendung der Arduino IDE inkl. der Anzeige des seriellen Monitors setze ich als bekannt voraus.

Der Arduino Sketch espdemo.ino erwartet eine Anpassung der drei Parameter für WLAN SSID, WLAN Password und MQTT Serveradresse. Danach sollte der Sketch mittels IDE auf den ESP8266 übertragen werden.

  • Es wird ein subscribe auf das topic "espdemo/in" ausgeführt
  • Für ein publish verwendet der ESP8266 das topic "espdemo/out"



Der MQTT2_SERVER

In meinem lokalen FHEM habe ich einen Server wie folgt angelegt.

defmod mqtt_test_server MQTT2_SERVER 11883 global
Der Server läuft ohne Benutzer und Passwort und hört auf port 11883,
das sollte aber für das Verständnis kein Problem darstellen.



Das MQTT2_DEVICE

defmod mqtt_test_device MQTT2_DEVICE
attr mqtt_test_device IODev mqtt_test_server
attr mqtt_test_device readingList espdemo/out:.* received
attr mqtt_test_device setList message espdemo/in
attr mqtt_test_device setStateList ignore

  • Eingehende Nachrichten werden in das reading "received" geschrieben. Dort sollte nach dem Start des ESP8266 sein "Name" auftauchen, der sich bei jedem Start ändert.

setstate mqtt_test_device 2023-06-18 20:24:19 received ESP-2ee8 connected
  • Mit "set mqtt2_test_device message huhu" kann eine Nachricht an den ESP8266 geschickt werden, die dort im seriellen Monitor auftaucht:

20:24:27.097 -> Message arrived [espdemo/in] huhu


Der nächste Schritt ist dann, die Aktionen zu programmieren, die der ESP8266 ausführen soll, wenn bestimmte Nachrichten auftauchen.

Um verschiedene Nachrichten in Richtung FHEM zu schicken, muss man im Code die entsprechend unterschiedlichen publish einbauen. Auch das habe ich jetzt erstmal weggelassen, weil das Grundverständnis der Kommunikation wichtiger ist.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

betateilchen

#1
Einen Button abfragen und an FHEM melden

Der Einfachheit halber verwende ich eine nodeMCU, da ist nämlich schon ein Button drauf, der an pin 0 angeschlossen ist und eigentlich für das flashen vorgesehen ist.

In setup() wird der Button mittels pinMode() hinzugefügt:

void setup() {
  Serial.begin(115200);
  setup_wifi();

// vorhandenen Button der nodeMCU als Input definieren
  pinMode(0, INPUT_PULLUP);
}

In loop() wird die Abfrage des buttons und das publish an FHEM eingebaut:

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

// Button abfragen und per publish melden
  if(!digitalRead(0)) {
    Serial.println("button pressed");
    client.publish(TOPIC_OUT,"button pressed");
  }
  delay(100);
}

Wird nun der Button gedrückt (Achtung - nur kurz drücken, die loop verzögert nur 0,1 Sekunden) erscheint in FHEM die Nachricht

setstate mqtt_test_device 2023-06-18 21:01:24 received button pressed
Cool, darauf könnte man zum Beispiel mittels notify reagieren :)
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

betateilchen

Man ahnt vielleicht schon, was als Nächstes kommt...

Eine LED an der nodeMCU von FHEM aus ein- und ausschalten

Die nodeMCU hat ja nicht nur einen Taster, den wir zum Testen und Lernen als GPIO-Input verwenden können, sondern auch noch eine LED, die ein prima GPIO-Output abbildet. Diese LED wollen wir nun von FHEM aus ein- und ausschalten können.

Zuerst erweitern wir die setList im MQTT2_DEVICE um einen neuen Eintrag für die LED

attr mqtt_test_device setList message espdemo/in\
led:on,off espdemo/in led $EVTPART1

Dann machen wir die notwendigen Änderungen im Arduino Sketch.

In setup() wird die LED definiert und beim Start ausgeschaltet.

void setup() {
  Serial.begin(115200);
  setup_wifi();

// vorhandenen Button der nodeMCU als Input definieren
  pinMode(0, INPUT_PULLUP);

// vorhandene LED der nodeMCU als Output definieren und ausschalten
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN,HIGH);
}

Dann legen wir VOR (wichtig!) der Funktion callback() eine neue Funktion namens "setMode" an.
In dieser Funktion wollen wir künftig die eingehenden Nachrichten auswerten und die entsprechenden Aktionen im ESP veranlassen.
Für den Moment werden wir einfach nur vergleichen, ob wir die Nachricht "led on" oder "led off" erhalten haben und schalten die LED entsprechend.

void setMode(String payload){
  Serial.println("setmode: "+payload);
  if (payload == "led on") {
    digitalWrite(LED_BUILTIN, LOW);
  }
  if (payload == "led off") {
    digitalWrite(LED_BUILTIN, HIGH);
  }
}

Damit die Funktion überhaupt in Aktion treten kann, müssen wir sie noch irgendwo aufrufen und ihr die Nachricht übergeben. Das passiert in der callback() Funktion, die immer ausgeführt wird, wenn eine MQTT Nachricht im ESP ankommt.

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  payload[length] = '\0';
  if (strcmp(topic,TOPIC_IN)==0){
    // wir wandeln die payload in was einfacheres
    String payload_str = (char *) payload;
    // und übergeben das an die Auswertung
    setMode(payload_str);
  }
}

Das war schon alles.

Jetzt kann man im FHEM Frontend im MQTT2_DEVICE aus der Setter-Liste die LED schalten.
Auf ESP-Seite sieht das im seriellen Monitor so aus:

21:29:12.537 -> Message arrived [espdemo/in] led on
21:29:12.537 -> setmode: led on
21:29:17.551 -> Message arrived [espdemo/in] led off
21:29:17.551 -> setmode: led off
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

betateilchen

Damit sind die Grundfunktionen für den Umgang mit GPIO sowohl für Eingang wie Ausgang hoffentlich verständlich beschrieben.

Hoffe, das hilft dem Einen oder Anderen ein bisschen weiter, wenn es um das Grundverständnis des Zusammanspiels von FHEM, ESP8266 und MQTT geht.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

ingo46

Zitat von: betateilchen am 18 Juni 2023, 20:51:44Mit "set mqtt2_test_device message huhu" kann eine Nachricht an den ESP8266 geschickt werden, die dort im seriellen Monitor auftaucht:

Wie kann ich nun aber eine Variable übergeben?

Ich möchte jede Minute diesen Wert "MQTT2_Strom_01:SML_Watt_Summe" an den ESP übergeben

define at_STROM at +*00:01:00 set MQTT2_Daten message {MQTT2_Strom_01:SML_Watt_Summe}
so geht es nicht!!

Deine Anleitung ist Klasse - alles hat sofort ohne Probleme funktioniert.

Ingo


rudolfkoenig

define at_STROM at +*00:01:00 set MQTT2_Daten message {(ReadingsVal("MQTT2_Strom_01", "SML_Watt_Summe", 0))}
oder
define at_STROM at +*00:01:00 set MQTT2_Daten message {([MQTT2_Strom_01:SML_Watt_Summe])}

ingo46

Danke - so funktioniert es! Ich hoffe, ich lerne das mit den "Klammern" nochmal!

Ingo

xenos1984

Zitat von: rudolfkoenig am 25 Juli 2023, 10:46:49define at_STROM at +*00:01:00 set MQTT2_Daten message {([MQTT2_Strom_01:SML_Watt_Summe])}

Braucht es dafür denn den Gang über die Perl-Ebene oder reicht nicht einfach set magic?

define at_STROM at +*00:01:00 set MQTT2_Daten message [MQTT2_Strom_01:SML_Watt_Summe]

ingo46

Hallo,

Zitat von: xenos1984 am 25 Juli 2023, 14:57:17Braucht es dafür denn den Gang über die Perl-Ebene oder reicht nicht einfach set magic?

define at_STROM at +*00:01:00 set MQTT2_Daten message [MQTT2_Strom_01:SML_Watt_Summe]

Es funktioniert so auch!