DIYTechAdventure: ESP32 Webserver zur Steuerung eines Shelly Relais mit dem Smartphone
In diesem Adventure „Smartphone schaltet Shelly“ geht es darum, den ESP32 Mikrocontroller als Webserver zu verwenden, um ein Shelly Relais zu steuern. Dabei wird eine einfache Webseite erstellt, auf der die Heizung ein- oder ausgeschaltet werden kann. Dieses Projekt richtet sich an Bastler und Einsteiger, die lernen möchten, wie man den ESP32 mit einem WLAN-Netzwerk verbindet, HTTP-Anfragen verwendet und eine interaktive Weboberfläche erstellt.
Aufgabenstellung
Die Aufgabe heißt Smartphone schaltet Shelly. Dazu ist es erste einmal nötig den ESP32 mit dem Shelly-AP zu verbinden. Mithilfe eines Webservers, der auf dem ESP32 betrieben wird, sollen wir die Möglichkeit haben, die Heizung per Smartphone oder Computer ein- oder auszuschalten. Ziel ist es, die Steuerung über eine einfache Weboberfläche mit zwei Buttons („Ein“ und „Aus“) zu ermöglichen. Zudem sollen wir auf der Webseite direkt die aktuelle Statusmeldung vom Shelly angezeigt bekommen.
Die Programmteile
Der Quellcode des Projekts ist in mehrere Hauptteile unterteilt:
- WLAN-Verbindung herstellen
Zuerst stellt der ESP32 die Verbindung zum WLAN des Shelly her. Hierzu verwenden wir die Funktionbegin_wlan_client()
, in der die SSID und das Passwort definiert sind. Der Verbindungsaufbau wird in der seriellen Konsole angezeigt, damit wir überprüfen können, ob die Verbindung erfolgreich ist.
Wie du eine WLAN-Verbindung herstellen kannst, ist in diesem Beitrag auch ausführlicher erklärt. - HTTP-Anfragen an den Shelly
Die Funktionhttp_Post_Request()
sendet eine HTTP GET-Anfrage an das Shelly-Relais, um es ein- oder auszuschalten. Je nach angeforderter URL („/relay/0?turn=on“ oder „/relay/0?turn=off“) wird das Relais geschaltet. Die Antwort wird in der seriellen Konsole ausgegeben, damit wir den Status des Shelly prüfen können.
In diesem Beitrag habe ich nur den Code für die http-Requests ausführlich erklärt. - Heizung ein- und ausschalten
Die beiden Funktionenheizung_ein()
undheizung_aus()
steuern das Relais direkt und geben eine Statusmeldung zurück, ob die Aktion erfolgreich war. Hier wird der Payload der HTTP-Antwort analysiert, um sicherzustellen, dass der Shelly den Befehl korrekt ausgeführt hat. - Webserver einrichten
Der Webserver wird in der Funktionsetup()
gestartet. Über den Webserver können wir die Steuerung über das lokale Netzwerk ermöglichen. Der Webserver stellt mehrere Endpunkte zur Verfügung, wie „/“ (Startseite), „/ein“ (Heizung einschalten) und „/aus“ (Heizung ausschalten). - Weboberfläche zur Steuerung
Die Funktionhandle_root()
erstellt eine einfache HTML-Webseite. Diese enthält eine Überschrift sowie zwei Buttons für das Ein- und Ausschalten der Heizung. Die Buttons sind größer und zentriert dargestellt, damit sie auf mobilen Geräten leicht bedienbar sind. Die Rückmeldung vom Shelly (ob die Heizung ein- oder ausgeschaltet ist) wird ebenfalls in der Mitte der Seite angezeigt, sodass der Nutzer sofort sieht, was passiert ist.
// Erforderliche Bibliotheken einbinden #include <Arduino.h> #include <WiFi.h> #include <HTTPClient.h> #include <WebServer.h> //WIFI Name und Passwort des Shelly AP festlegen const char ssid[] = "shelly1-xxxx"; const char password[] = "xxxxxx"; const char schalter_on[] = "http://192.168.33.1/relay/0?turn=on"; const char schalter_off[] = "http://192.168.33.1/relay/0?turn=off"; WebServer server(80); // Funktionsdeklarationen hier einfügen: void begin_wlan_client(); String http_Post_Request(const char* url); void heizung_ein(); void heizung_aus(); void handle_root(); void handle_heizung_ein(); void handle_heizung_aus(); void setup() { begin_wlan_client(); // WLAN-Verbindung starten // Webserver-Endpunkte definieren server.on("/", handle_root); server.on("/ein", handle_heizung_ein); server.on("/aus", handle_heizung_aus); server.begin(); Serial.println("Webserver gestartet."); } void loop() { server.handleClient(); } // Funktionsdefinitionen hier einfügen: void begin_wlan_client() { WiFi.begin(ssid, password); Serial.begin(115200); // Zum Debuggen in der Konsole Serial.println("Verbindung zum WLAN herstellen..."); // Wartet, bis der ESP32 mit dem Shelly-WLAN verbunden ist while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Verbunden mit IP: "); Serial.println(WiFi.localIP()); } String http_Post_Request(const char* url) { HTTPClient http; http.begin(url); // Starte die HTTP-Verbindung int httpResponseCode = http.GET(); // Sende HTTP GET-Anfrage String payload = ""; // Leeres Payload für die Antwort if (httpResponseCode > 0) { payload = http.getString(); // Erhalte Antwort als String und in der Seriellen ausgeben Serial.print("Antwort: "); Serial.println(payload); } else { Serial.print("Fehler bei der Anfrage, Fehlercode: "); Serial.println(httpResponseCode); } http.end(); // Beende die HTTP-Verbindung return payload; } void heizung_ein() { // Funktion zum Einschalten der Heizung Serial.println("Schalte Heizung ein..."); String payload = http_Post_Request(schalter_on); int pos = payload.indexOf(':'); // Erstes ':' suchen, z.B. {"ison":true, int pos2 = payload.indexOf(','); String result = payload.substring(pos + 1, pos2); Serial.print("Antwort vom Shelly: "); Serial.println(result); if (result == "true") { Serial.println("Heizung erfolgreich eingeschaltet!"); Serial.println(); } else { Serial.println("Fehler beim Einschalten der Heizung."); Serial.println(); } } void heizung_aus() { // Funktion zum Ausschalten der Heizung Serial.println("Schalte Heizung aus..."); String payload = http_Post_Request(schalter_off); int pos = payload.indexOf(':'); int pos2 = payload.indexOf(','); String result = payload.substring(pos + 1, pos2); Serial.print("Antwort vom Shelly: "); Serial.println(result); if (result == "false") { Serial.println("Heizung erfolgreich ausgeschaltet!"); Serial.println(); } else { Serial.println("Fehler beim Ausschalten der Heizung."); Serial.println(); } } void handle_root() { String html = "<html><body style='text-align: center; display: flex; flex-direction: column; justify-content: center; height: 100vh;'>"; html += "<div style='margin-top: 10%;'>"; html += "<h1 style='text-align: center;'>ESP schaltet Shelly</h1>"; html += "<button style='font-size: 24px; width: 200px; height: 100px; background-color: green; color: white; margin: 20px;' onclick=\"location.href='/ein'\">Ein</button>"; html += "<button style='font-size: 24px; width: 200px; height: 100px; background-color: red; color: white; margin: 20px;' onclick=\"location.href='/aus'\">Aus</button>"; html += "<p id='status' style='font-size: 24px; margin-top: 40px;'></p>"; html += "</div>"; html += "</body></html>"; server.send(200, "text/html", html); } void handle_heizung_ein() { heizung_ein(); server.send(200, "text/html", "<html><body style='text-align: center; display: flex; flex-direction: column; justify-content: center; height: 100vh;'><div style='margin-top: 10%;'><h1>ESP schaltet Shelly</h1><p style='font-size: 24px; margin-top: 40px;'>Shelly Relais eingeschaltet</p><a href='/'>Zurück</a></div></body></html>"); } void handle_heizung_aus() { heizung_aus(); server.send(200, "text/html", "<html><body style='text-align: center; display: flex; flex-direction: column; justify-content: center; height: 100vh;'><div style='margin-top: 10%;'><h1>ESP schaltet Shelly</h1><p style='font-size: 24px; margin-top: 40px;'>Shelly Relais ist ausgeschaltet</p><a href='/'>Zurück</a></div></body></html>"); }
// Erforderliche Bibliotheken einbinden #include <Arduino.h> #include <WiFi.h> #include <HTTPClient.h> #include <WebServer.h> const char ssid[] = "xxxxx"; //dein WLAN Name const char password[] = "xxxxxxxxxxxxx"; // dein WLAN Passwort const char shelly_ip[] = "192.xxx.x.xxx"; // Beispiel IP-Adresse des Shelly im Heimnetzwerk String schalter_on = "/relay/0?turn=on"; String schalter_off = "/relay/0?turn=off"; WebServer server(80); // put function declarations here: void begin_wlan_client(); String http_Post_Request(const String& url); void heizung_ein(); void heizung_aus(); void handle_root(); void handle_heizung_ein(); void handle_heizung_aus(); void setup() { begin_wlan_client(); // WLAN-Verbindung starten // Webserver-Endpunkte definieren server.on("/", handle_root); server.on("/ein", handle_heizung_ein); server.on("/aus", handle_heizung_aus); server.begin(); Serial.println("Webserver gestartet."); } void loop() { server.handleClient(); } // put function definitions here: void begin_wlan_client() { WiFi.begin(ssid, password); Serial.begin(115200); // Zum Debuggen in der Konsole Serial.println("Verbindung zum WLAN herstellen..."); // Wartet, bis der ESP32 mit dem Heim-WLAN verbunden ist while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Verbunden mit IP: "); Serial.println(WiFi.localIP()); } String http_Post_Request(const String& url) { HTTPClient http; String fullUrl = "http://" + String(shelly_ip) + url; http.begin(fullUrl); // Starte die HTTP-Verbindung int httpResponseCode = http.GET(); // Sende HTTP GET-Anfrage String payload = ""; // Leeres Payload für die Antwort if (httpResponseCode > 0) { payload = http.getString(); // Erhalte Antwort als String Serial.print("Antwort: "); Serial.println(payload); } else { Serial.print("Fehler bei der Anfrage, Fehlercode: "); Serial.println(httpResponseCode); } http.end(); // Beende die HTTP-Verbindung return payload; } void heizung_ein() { // Funktion zum Einschalten der Heizung Serial.println("Schalte Heizung ein..."); String payload = http_Post_Request(schalter_on); int pos = payload.indexOf(':'); // Erstes ':' suchen, z.B. {"ison":true, int pos2 = payload.indexOf(','); String result = payload.substring(pos + 1, pos2); Serial.print("Antwort vom Shelly: "); Serial.println(result); if (result == "true") { Serial.println("Heizung erfolgreich eingeschaltet!"); Serial.println(); } else { Serial.println("Fehler beim Einschalten der Heizung."); Serial.println(); } } void heizung_aus() { // Funktion zum Ausschalten der Heizung Serial.println("Schalte Heizung aus..."); String payload = http_Post_Request(schalter_off); int pos = payload.indexOf(':'); int pos2 = payload.indexOf(','); String result = payload.substring(pos + 1, pos2); Serial.print("Antwort vom Shelly: "); Serial.println(result); if (result == "false") { Serial.println("Heizung erfolgreich ausgeschaltet!"); Serial.println(); } else { Serial.println("Fehler beim Ausschalten der Heizung."); Serial.println(); } } void handle_root() { String html = "<html><body style='text-align: center; display: flex; flex-direction: column; justify-content: center; height: 100vh;'>"; html += "<div style='margin-top: 10%;'>"; html += "<h1 style='text-align: center;'>ESP schaltet Shelly</h1>"; html += "<button style='font-size: 24px; width: 200px; height: 100px; background-color: green; color: white; margin: 20px;' onclick=\"location.href='/ein'\">Ein</button>"; html += "<button style='font-size: 24px; width: 200px; height: 100px; background-color: red; color: white; margin: 20px;' onclick=\"location.href='/aus'\">Aus</button>"; html += "<p id='status' style='font-size: 24px; margin-top: 40px;'></p>"; html += "</div>"; html += "</body></html>"; server.send(200, "text/html", html); } void handle_heizung_ein() { heizung_ein(); server.send(200, "text/html", "<html><body style='text-align: center; display: flex; flex-direction: column; justify-content: center; height: 100vh;'><div style='margin-top: 10%;'><h1>ESP schaltet Shelly</h1><p style='font-size: 24px; margin-top: 40px;'>Shelly Relais eingeschaltet</p><a href='/'>Zurück</a></div></body></html>"); } void handle_heizung_aus() { heizung_aus(); server.send(200, "text/html", "<html><body style='text-align: center; display: flex; flex-direction: column; justify-content: center; height: 100vh;'><div style='margin-top: 10%;'><h1>ESP schaltet Shelly</h1><p style='font-size: 24px; margin-top: 40px;'>Shelly Relais ist ausgeschaltet</p><a href='/'>Zurück</a></div></body></html>"); }
Oben stehenden Code habe ich zweimal abgebildet. Linkes Register->Shelly abreitet als Access Point / rechtes Register -> Shelly ist im heimischen WLAN eingeloggt.
Für den Test des Shelly-AP beachte bitte! Das Smartphone muss sich am Shelly-Access Point unter 192.168.33.1 eingeloggt haben. Vergleiche den Beitrag hier!
Wie in der Ausgabe der seriellen Schnittstelle zu sehen ist, verbindet sich der ESP32 mit dem Shelly auf der
IP-Adresse 192.168.33.2. Diese IP-Adresse sollte auch als URL in deinen Browser des Smartphones eingegeben werden. Das sollte bei dir dann auch so aussehen.
Für den Test, wenn der Shelly im heimischen WLAN eingeloggt ist, benötigst du die IP des ESP32, die du in der Ausgabe des seriellen Monitors findest.
Verbunden mit IP: 192.168.2.149
Schalte Heizung ein…
Antwort: {„ison“:true,“has_timer“:true,“timer_started“:0,“timer_duration“:3600,“timer_remaining“:3600,“source“:“http“}
Antwort vom Shelly: true
Heizung erfolgreich eingeschaltet!
Mit deinem Smartphone gibst du diese IP dann ein und landest auf der Weboberfläche deines Webservers im ESP32.
Ergebnis
Das Ergebnis ist ein ESP32-basierter Webserver, der eine einfache und intuitive Weboberfläche bietet, um das Shelly Relais zu steuern. Der Nutzer kann mit seinem Smartphone oder Computer im selben Netzwerk die Webseite des ESP32 aufrufen und durch Drücken der entsprechenden Buttons die Heizung ein- oder ausschalten. Zusätzlich gibt der Shelly eine Statusmeldung zurück, die ebenfalls auf der Webseite angezeigt wird.
Dies ist eine hervorragende Einführung in die Verwendung des ESP32 zur Steuerung von Smart-Home-Geräten. Man lernt dabei, wie man WLAN-Verbindungen herstellt, HTTP-Anfragen sendet und wie man einen einfachen Webserver mit HTML erstellt. Für Einsteiger bietet dieses Projekt einen umfassenden Überblick über die Grundlagen der IoT-Programmierung und zeigt, wie man eine benutzerfreundliche Oberfläche für die Steuerung von Geräten entwickelt.
Viel Spaß beim Nachbauen und Experimentieren!