(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 SketchDie 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_SERVERIn 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_DEVICEdefmod 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.
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 :)
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
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.
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
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])}
Danke - so funktioniert es! Ich hoffe, ich lerne das mit den "Klammern" nochmal!
Ingo
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]
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!