HTTP.GET-Befehle per „Fernsteuerung“ mit einem ESP32

Ich habe in meinem Haushalt einige Schaltaktoren von Shelly. Diese schalten recht zuverlässig per WLAN Lichter, Rolladen und Anderes. Bedient werden die Aktoren dabei über eine App, die es sowohl für IOS als auch für Android gibt. Mich stört aber schon seit einiger Zeit, dass es eben nur über das Handy oder sogar noch komplizierter über den PC geht. Der Weg über den PC funktioniert deshalb, weil jeder Aktor seinen eigenen Webserver mitbringt. 

Eine unabhängige Fernsteuerung muss her.

Ein wichtiger Punkt bei dem ganzen Vorhaben ist die Bedienbarkeit der Aktoren über eine einfache Eingabe in einen Browser:

Sofern ein Shelly-Aktor hinter der IP-Adresse vorhanden ist, würde dieser Befehl beim Absenden das Relais im Aktor schalten und damit gegebenenfalls das Licht einschalten. Ausschalten geht natürlich auch indem das „on“ durch „off“ ersetzt wird.

Die Fernsteuerung sollte natürlich möglichst klein sein, sonst könnte ich ja wieder ein Mobiltelefon oder ein Tablet nehmen. Zudem soll der Stromverbrauch niedrig sein, damit man länger damit arbeiten kann. Da es hier um einen Selbstbau gehen soll, sollten Informationen zum verwendeten Bauteil im Internet leicht zu finden sein.

Die Wahl fiel auf den WEMOS S2 mini. Das ist ein sehr kleines Entwicklerboard mit einem ESP32-Chip. Das Original ist zur Zeit der Beitragserstellung schlecht zu bekommen. Clone gibt es aber beispielsweise über Aliexpress genügend. An dieser Stelle aber gleich die Warnung. Die Platinchen sind sehr billig, – die Fertigungsqualität aber leider auch. Bei einem Einzelpreis von rund 2 -3 Euro sollte man sich also lieber gleich 5 Stück schicken lassen, damit wenigstens ein oder zwei davon richtig funktionieren. Die größte Problematik liegt übrigens bei den Clones in der WiFi-Anbindung, – und genau die brauchen wir. Bei mir funktionieren 3 von 5.  „Glück gehabt!“. Wer ein Bild des Originals sehen will, findet es hier.

Die kleine Platine, besser gesagt der ESP32 auf der Platine, muss programmiert werden. Ansonsten läuft im Auslieferungszustand auf dem Platinchen microPhyton. Zu diesem Zweck bedient man sich der ARDUINO IDE. Aber auch damit ist es noch nicht getan. Um dem ESP32 einen Sketch zu aufzuspielen muss das spezielle ESP32-Umfeld erst noch bekannt gemacht werden. Der Sketch ist übrigens das Betriebssystem des Chips, den wir wie ein normales Programm aufbauen.

Ich kann an dieser Stelle nicht den ganzen Weg zur Schaffung des richtigen Entwicklungsumfelds aufzeigen und verweise daher auf die Quellen, die mir maßgeblich geholfen haben:

Einrichtung ARDUINO IDE

Aktivierung Serieller Monitor

Die beiden Beiträge sollten gewissenhaft abgearbeitet werden, sonst wird es wirklich schwierig!

Sobald die Grundvoraussetzung geschaffen sind, kann es auch schon an den eigentlichen Programmcode gehen, den ich nun abschnittsweise erklären werde. Ich habe mir im Vorfeld aus vielen Quellen die notwendigen Informationen beschafft. Wie immer, gibt es dabei auch einige Informationen, die in die falsche Richtung laufen. Das vorliegende Programm ist evolutionär entstanden:

  1. Bei Programmstart einfach nur den Befehl absetzen und den Erfolg genießen, wenn die Lampe „an“ ist.
  2. Licht in die Sache bringen: Zur Kontrolle eine LED ansteuern
  3. Einen Taster einbauen und Abfragen. Schalten nur nach Tasterbetätigung.
  4. Zweiten Taster einbauen um Ein- und Ausschalten zu ermöglichen.
  5. Interruptsteuerung. Der komplizierteste Teil und vorerst die letzte Evolutionsstufe

„Einfach“ nur den Befehl http://192.168.1.124/relay/0?turn=on abzusetzen, hört sich zunächst mal banal an. Dafür muss aber eine WLAN-Verbindung aufgebaut werden und die richtige Methode und damit der richtige Befehl gefunden werden. Für diese beiden Aufgaben werden gleich in den ersten Programmzeilen zwei Bibliotheken eingebunden:

#include <WiFi.h>
#include <HTTPClient.h>

Zwei Interrupt-Routinen (dazu später mehr) werden angemeldet:

void IRAM_ATTR Licht1On();
void IRAM_ATTR Licht1Off();

Eine Spezialität ist hier das Flag IRAM_ATTR. Laut ESP32-Dokumentation ist dies zwingend für die Interruptbehandlung. Die Routine wird damit gezielt in einen dafür vorgesehenen Speicherplatz geschoben. Es scheint in vielen Fällen aber auch ohne das Flag zu funktionieren.

Ein paar Variablen müssen eingeführt werden:

int ledPin = 15;    //die interne LED
int taster1OnPin = 11;
int taster1OffPin = 10;
volatile int button;

Damit das Programm leichter lesbar und vor allem änderbar bleibt, werden die benutzten PINs am Board hier mit Namen versehen. So hängt am PIN 11 des WEMOS S2 mini der Taster für das Einschalten des Lichts. Der zweite Anschluss des Tasters liegt auf GND.

Der Taster für das Ausschalten des Lichtes liegt auf PIN 10 und überbrückt ebenfalls zu GND.

In den ersten Aufbaustufen hatte ich noch  eine externe LED verbunden. Das ist aber gar nicht nötig, denn es gibt an PIN 15 eine direkt auf der Platine verbundene blaue LED.

Für die interne Programmsteuerung wird eine Variable button gebraucht, um die Tastendrücke unterscheiden zu können. Das ist eigentlich eine direkte Notwendigkeit aufgrund der Interruptsteuerung. Dass die Variable auch noch volatile sein muss, liegt ebenfalls daran. Zur Vertiefung des Themas Interruptsteuerung kann ich diesen Beitrag empfehlen.

So ein ESP32 hat viele Anschlüsse. Nahezu alle Anschlüsse können diverse Funktionen übernehmen: Sie können als Ausgang dienen, um beispielsweise eine LED zu schalten. Sie können aber auch als Eingang dienen, um einen Tastendruck abzufragen. Es gibt noch wesentlich mehr spannende Funktionen auf so einem ESP32, allerdings brauchen wir die hier nicht und deshalb führe ich das nicht weiter aus. Soviel sei noch gesagt: Der WEMOS S2 mini hat eine Besonderheit zu bieten. Fast alle Anschlüsse können im Rahmen von externen Interruptsteuerungen benutzt werden. Man muss dem Chip allerdings mitteilen, welche Aufgabe die einzelnen benutzten Anschlüsse zu erfüllen haben:

void setup() {
  
  pinMode(ledPin, OUTPUT);
  pinMode(taster1OnPin, INPUT_PULLUP);
  pinMode(taster1OffPin, INPUT_PULLUP);

Diese Definitionsaufgabe leitet das setup des Programms ein. Es dürfte klar sein, warum die jeweiligen Anschlüsse mit OUTPUT bzw. INPUT belegt sind. Der Zusatz PULLUP deckt eine Besonderheit der Elektronik ab. Wenn der Taster am PIN 10 gedrückt wird, verbindet er diesen PIN mit GND. Wenn der Taster allerdings nicht gedrückt wird, hängt der Anschluss quasi in der „Luft“. Das wäre ein undefinierter Zustand und führt nicht selten zu sehr komplexen Fehlerbildern. Um nun vorzubeugen, wird einfach ein Widerstand von der Betriebsspannung zum PIN 10 gelegt, – ein sogenannter PULLUP-Widerstand, weil er die Spannung hochzieht. GND entspricht in der Logik einem LOW und die Betriebsspannung entspricht HIGH. Damit man nun nicht den Widerstand physisch in die Schaltung einbauen muss, haben die Chipentwickler bereits die PULLUP-Widerstände für jeden PIN integriert. Übrigens gibt es bei dieser Platine keinen PULLDOWN! Den müsste man dann tatsächlich extern realisieren.

Oben wurde bereits die Interrupt-Steuerung angesprochen. Ich will das hier nicht vertiefen, aber wenigstens kurz anreißen: Normalerweise läuft ein Programm einfach stupide von Anfang bis Ende durch. Bei den Arduino- bzw. ESP32-Sketches laufen sie sogar in einer Dauerschleife. Die Abfrage eines Tastendrucks könnte daher immer nur an genau der gleichen Stelle im Programmablauf erfolgen. Will man aber erreichen, dass zu jedem Zeitpunkt der Tastendruck registriert wird, benutzt man einen Interrupt. Der horcht quasi die ganze Zeit und wartet, dass ein definiertes Ereignis eintritt. In diesem Fall hält er das Hauptprogramm theoretisch sofort an und kümmert sich um den Vorfall. Danach geht es zurück in die Abarbeitung des Hauptprogramms an genau der Stelle, wo vorher abgebrochen wurde.

Interrupts haben aber ein paar starke Einschränkungen. Quasi alles, was Zeit kostet, ist in Interrupt-Routinen verboten. Ich verweise nochmals auf die angesprochenen Quellen.

  //Interrupts initialisieren

  attachInterrupt(digitalPinToInterrupt(taster1OnPin), Licht1On, FALLING);
  attachInterrupt(digitalPinToInterrupt(taster1OffPin), Licht1Off, FALLING);
  
  // !!!! LOW funktioniert offensichtlich bei diesem Interrupt nicht !!!!

Ein Interrupt wird eingeleitet durch den Befehl attachInterrupt. Dann wird die Art des Interrupts als Argument mitgegeben. digitalPinToInterrupt gibt an, dass es sich um einen externen Interrupt, der durch eine Zustandsveränderung an einem Anschluss erzeugt wird, handelt. Der Name des betroffenen PINs (taster1OnPin bzw. taster1OffPin) wird übergeben. Das nächste Argument bezeichnet die Programmroutine, die aufgerufen werden soll, wenn der Interrupt eintritt. Das ist hier Licht1On oder die Off-Variante davon. Zuletzt wird noch mitgegeben, welches Ereignis überwacht werden soll. Hier gibt es LOW, FALLING, RISING und CHANGE. LOW hat bei mir nicht funktioniert, – warum weiß ich noch nicht. In Beispielen fand ich aber auch grundsätzlich für diese Aufgabenstellung das FALLING vor. Es sagt aus, dass eine Zustandsänderung von HIGH nach LOW stattgefunden hat.

  Serial.begin(115200);
  delay(4000);
  WiFi.begin("meine SSID", "mein Password");
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
  
  Serial.println("Connected to the WiFi network");
  
}

Hier geht es jetzt um die Verbindungen. Zum einen wird eine serielle Verbindung über USB aufgebaut. Das geht über den Befehl Serial.begin(115200). Die Zahl stellt die übliche Verbindungsgeschwindigkeit dar.

Nach einer 4-sekündigen Pause wird die WiFi-Verbindung über den Befehl WiFi.begin etabliert. An dieser Stelle müssen natürlich die eigen SSID und das passende Password zwischen den Anführungszeichen eingetragen werden. Nochmal: Die Anführungszeichen bleiben stehen!!

Der Erfolg des Verbindungsversuchs wird über die Bedingung der while-Schleife geprüft. Solange die Verbindung nicht aufgebaut ist, sendet die Platine über Seriell bzw. USB eine entsprechende Meldung. Im Erfolgsfall erfolgt ebenfalls eine Meldung.

Leider ist der WEMOS S2 mini und ganz speziell die Clones davon mitunter recht störrisch bei der Verbindung. Mal ist die Verbindung sofort da und ein anderes Mal dauert es ewig. Manchmal kommt sogar überhaupt keine Verbindung zustande. Die Problematik soll angeblich darin liegen, dass der Chip während des Aufbaus der WiFi-Verbindung besonders viel Strom zieht und dafür aber gar nicht ausgelegt ist. Es gibt im Netz verschiedene Herangehensweisen vom direkten Eingriff auf die Chipregister bis hin zu einem Kondensator mit 10uF zwischen EN und 3,3V. Zuletzt bin ich noch auf eine Seite gestoßen, die sehr umfangreich das Thema WiFi-Connection behandelt. Die Erkenntnisse daraus sind aber hier noch nicht eingeflossen.

Die Setup-Routine ist damit abgeschlossen.

Wir haben zwei Interrupts eingerichtet. Diese beiden Interrupts rufen bei Auslösung eine der beiden folgenden Routinen auf:

void Licht1On(){
  button = 1;  
}

void Licht1Off(){
  button = 2;   
}

Die Routinen sehen eigentlich wie übliche Unterprogramme aus. Im Definitionsteil wurden sie schon mit einem speziellen Flag etabliert. Sobald eine der beiden Routinen aufgerufen wird, bekommt die Variable button die Nummer des entsprechenden Tasters zugewiesen.

Arduino-Sketche und natürlich auch die ESP32-Sketche haben immer eine ständig sich wiederholende Schleife:

void loop() {

Es wäre sinnlos, ständig http-Befehle absenden zu wollen, wenn gar keine Verbindung besteht. Deshalb wird bei jedem Durchlauf der Schleife zunächst geprüft, ob die Verbindung aktiv bzw. noch aktiv ist.

if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status

    Serial.println("verbunden");
       
    HTTPClient http;
  
    digitalWrite(ledPin, HIGH);
    delay(200);
    digitalWrite(ledPin, LOW);
    

Wenn die Verbindung akiv ist, wird die für das Senden notwendige Struktur http definiert. Das kurze Aufleuchten der LED ist jetzt nur noch eingebaut, um eine visuelle Kontrolle zu ermöglichen. Die LED-Aktivität wird in der endgültigen Version wohl rausfliegen. Denn später, wenn die Platine über eine Batterie gespeist wird, soll jeder unnötige Verbraucher eliminiert werden.

   if (button == 1){
      digitalWrite(ledPin, HIGH);
      delay(400);
      digitalWrite(ledPin, LOW);
      delay(400);
      digitalWrite(ledPin, HIGH);
      delay(400);
      digitalWrite(ledPin, LOW);

      Serial.println("Button 1");

In den Interruptroutinen wurde die Variable mit der Nummer des zugehörigen Tasters befüllt. Jetzt wird diese Variable zur Steuerung ausgewertet. 

Der Eine oder Andere C-Programmierer wird jetzt anmerken, dass man hier eleganter eine case-Abfrage einbauen könnte. Das hätte ich auch getan, wenn ich nicht gelesen hätte, dass im Rahmen Interrupt-Handlings der ESP32-Serie Probleme bei mehr als 4 case-Fällen beobachtet wurden. Angeblich wandelt der Compiler sogar beim ESP32 case-Strukturen in if-Strukturen um. … und genau da scheint es zu haken.

Neben ein wenig Lichterspiel (2mal langes Leuchten) und der seriellen Meldung kommt der eigentliche Befehl:

      http.begin("http://192.168.1.124/relay/0?turn=on"); //Specify the URL
      int httpCode = http.GET(); 

Wieder unter der Annahme, dass sich der Schaltaktor unter der IP-Adresse 192.168.1.124 befindet, wird der schon weiter oben diskutierte GET-Befehl abgesendet. Der Erfolg der Aktion wird in der Variablen httpCode erwartet.

      if (httpCode > 0) { //Check for the returning code
  
        String payload = http.getString();
        Serial.println(httpCode);
        Serial.println(payload);
      }
  
      else {
        Serial.println("Error on HTTP request");
      }
    }

Der Return-Code wird auch gleich ausgewertet, – zumindest jetzt noch, und über die serielle Schnittstelle ausgegeben. Shellys sind da übrigens recht gesprächig und geben viele Informationen zurück.

Die soeben besprochene if-Struktur, die mit if (button == 1){ eingeleitet wurde, wiederholt sich jetzt für jeden weiteren Taster. Im vorliegenden Fall also button == 2 und der der Taste zugeordnete Befehl wird gesendet. Statt on ist das dann off.

    if (button == 2){
      digitalWrite(ledPin, HIGH);
      delay(400);
      digitalWrite(ledPin, LOW);
      delay(400);
      digitalWrite(ledPin, HIGH);
      delay(400);
      digitalWrite(ledPin, LOW);

      Serial.println("Button 2");

      http.begin("http://192.168.1.124/relay/0?turn=off"); //Specify the URL
      int httpCode = http.GET();                                        //Make the request

      if (httpCode > 0) { //Check for the returning code
  
        String payload = http.getString();
        Serial.println(httpCode);
        Serial.println(payload);
      }
  
      else {
        Serial.println("Error on HTTP request");
      }
    }

Zum Schluss wird noch aufgeräumt:

    http.end(); //Free the resources
    button = 0;
    delay(5000); 
  } 
   
}

Die Datenstruktur http wird freigegeben, die button-Variable wird auf 0 gesetzt, Rein zu Kontrollzwecken steht hier noch ein delay drin. Das fliegt später natürlich auch raus. Die letzte geschweifte schließende Klammer beendet die loop.

Wie weiter oben schon angedeutet ist die Schaltung am WEMOS S2 mini als trivial zu bezeichnen. Von den gewählten Anschlüssen, bei mir PIN10 und PIN11 geht es jeweils auf den einen Anschluss eines Tasters. Der zweite Anschluss des jeweiligen Tasters geht auf GND auf der Platine. Das war es auch schon. Es müssen in diesem Fall aktiv schließende Taster verwendet werden. Das heißt, dass der Taster den Kontakt bei Betätigung schließt und damit den Weg zwischen PIN10 bzw. PIN11 zu GND freigibt. ich habe das auf einem kleinen Breadboard aufgebaut.

Das blaue und das rosa Kabel sind für den oben beschriebenen WiFi-Hack. Ob der wirklich etwas bringt, kann ich nicht wirklich beurteilen. Die schwarze Leitung kommt von GND und wird dann mit der grünen und der lila Leitung an jeweils einen Taster angeschlossen. Die graue Leitung läuft vom Taster 2 (oben, weißer Knopf) zu Anschluss PIN10 und die gelbe Leitung läuft von Taster 1 (unten, roter Knopf) zu PIN11.

Die Verwendung von Breadboards in der Entwicklungsphase kann ich nur empfehlen. Das ist wesentlich einfacher als ständig zu löten.

Ich habe mir die einzelnen Bestandteile alle separat gekauft und lag damit sicherlich preislich höher, als beispielsweise das folgende Angebot. Tatsache ist aber auch, dass irgendwelcher Kleinkram dann doch wieder fehlt, den man dann einzeln nachkaufen muss. Glücklicherweise sind die Bestandteile aber alle recht günstig:

Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Für den Käufer ändert sich der Preis dadurch nicht.

Bei den Leitungen benötigt man 3 Typen: 

  1. beidseitig Steckkontakt männlich (Beispielsweise Verbindungen nur auf dem Breadboard)
  2. beidseitig Buchse weiblich (Beispielsweise Verbindungen zwischen Pfostenleisten, wie sie auf den Platinen zu finden sind)
  3. Steckkontakt und Buchse an einem Kabel (Verbindung zwischen Pfostenleisten und Breadboard)

    Diese Set scheint mir alle Anforderungen abzudecken.

    Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Für den Käufer ändert sich der Preis dadurch nicht.

Ich kann hier nicht ausführlich in die Nutzung der Arduino IDE einsteigen. Das füllt allen schon mehrere Beiträge und ist zur Genüge an diversen Stellen im Internet erklärt. Der Umgang mit elektronischen Bauteilen kann unter Umständen gefährlich sein. Vorsicht! Fragen beantworte ich jederzeit gern, – vielleicht nicht immer sofort im Tagestakt.

Ausblick: In der vorliegenden Version ist die „Fernsteuerung“ natürlich überhaupt nicht alltagstauglich. Das war bisher nur die Machbarkeitsstudie. 

Im nächsten Schritt, wird

  1. der Programmcode so umgebaut, dass man schon im Header alle Konfigurationsdaten wie beispielsweise Befehlszuordnungen zu Tastern findet.
  2. Zudem wird die Anzahl der Taster auf mindestens 8 erhöht. 
  3. Die Platine wird an eine aufladbare Batterie angeschlossen um mobil zu werden.
  4. Ein Gehäuse zum Selberdrucken für die Aufnahme aller Komponenten und sinnvoller Tastermechanik wird entwickelt.
  5. Eine Lochrasterplatine mit den Tastern wird entwickelt.

Stay tuned! Bleib dran!

0 0 votes
Article Rating
Abonnieren
Benachrichtige mich bei
guest
0 Comments
Inline Feedbacks
View all comments