Eine Funksteckdose wird meist mit einer Fernbedienung ausgeliefert. Wie man eine Funksteckdose mit einem Wemos D1 mini steuert und die Steckdose in das Intranet / Internet einbindet, möchte ich in diesem Tutorial erläutern.
Hinweis: Die Steckdose selbst gibt 230V an Spannung aus, dieses ist gefährlich und kann zu schweren Verletzungen führen.
Daher bleibt das Gehäuse von einer Erweiterung unberührt.
Den Wemos D1 mini habe ich gleichnamigen Tutorial WEMOS D1 Mini – Übersicht ausführlich behandelt.
Ziel
Das Ziel dieses Tutorials soll es sein eine Funksteckdose mit einem Arduino anzusteuern (an / aus). Dazu soll in der ersten Ausbaustufe ein Taster mit Pulldown Widerstand die Steckdose an und aus schalten. In der zweiten Ausbaustufe soll dieses über eine Webseite gelöst werden. Diese Webseite kann man je nach vorhandener Infrastruktur in das Internet einbinden.
Benötigte Komponenten
Für einen “fliegenden” Aufbau benutze ich gerne Breadboards diese gibt es in verschiedenen Größen und haben den Vorteil das der Aufbau ohne Löten zu erledigen ist.
- min. 1 Funksteckdose (mit Fernbedienung),
- 433 MHz Sender & Empfänger,
- 400 PIN Breadboard ,
- Breadboardkabel,
- Wemos D1 mini
433 MHz Sender & Empfänger
Der 433MHz Sender & Empfänger Modul besteht aus zwei Modulen.
Wie diese beiden Module funktionieren habe ich im Tutorial Arduino Tutorial 37: 433 MHz Sender & Empfänger beschrieben.
Benötigte Bibliothek
Für die Entwicklung des Sketches verwende ich die Bibliothek RCSwitch welche vom Git Repository des Entwicklers geladen werden kann.
Wie man diese Bibliothek in die Arduino IDE einbindet habe ich unter Arduino IDE, Einbinden einer Bibliothek erläutert.
Ermitteln der benötigten Daten von der Funksteckdose
Bevor man nun die Funksteckdose in den Zustand “an” bzw. “aus” versetzen kann, muss man erst einmal die Daten auslesen. Dazu wird ein 433 MHz Empfänger benötigt.
Quellcode
Den nachfolgenden Sketch habe ich von den Beispielen der Bibliothek übernommen und aus den zwei Dateien in eine zusammengefasst.
#include <RCSwitch.h> RCSwitch rcSwitch = RCSwitch(); void setup() { Serial.begin(9600); rcSwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 } void loop() { if (rcSwitch.available()) { Serial.println("<--- Beginn"); output(rcSwitch.getReceivedValue(), rcSwitch.getReceivedBitlength(), rcSwitch.getReceivedDelay(), rcSwitch.getReceivedRawdata(),rcSwitch.getReceivedProtocol()); rcSwitch.resetAvailable(); Serial.println("<--- End"); Serial.println(); } } static const char* bin2tristate(const char* bin); static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength); void output(unsigned long decimal, unsigned int length, unsigned int delay, unsigned int* raw, unsigned int protocol) { const char* b = dec2binWzerofill(decimal, length); Serial.print("Decimal: "); Serial.print(decimal); Serial.print(" ("); Serial.print(length); Serial.println(" Bit)"); Serial.print("Binary: "); Serial.println(b); Serial.print("Tri-State: "); Serial.println(bin2tristate( b)); Serial.print("PulseLength: "); Serial.print(delay); Serial.println(" microseconds"); Serial.print("Protocol: "); Serial.println(protocol); Serial.println("Raw data: "); Serial.print("\t"); for (unsigned int i=0; i<= length*2; i++) { Serial.print(raw[i]); Serial.print(","); if(i!= 0 && (i%5)==0){ Serial.println(); Serial.print("\t"); } } Serial.println(); } static const char* bin2tristate(const char* bin) { static char returnValue[50]; int pos = 0; int pos2 = 0; while (bin[pos]!='\0' && bin[pos+1]!='\0') { if (bin[pos]=='0' && bin[pos+1]=='0') { returnValue[pos2] = '0'; } else if (bin[pos]=='1' && bin[pos+1]=='1') { returnValue[pos2] = '1'; } else if (bin[pos]=='0' && bin[pos+1]=='1') { returnValue[pos2] = 'F'; } else { return "not applicable"; } pos = pos+2; pos2++; } returnValue[pos2] = '\0'; return returnValue; } static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength) { static char bin[64]; unsigned int i=0; while (Dec > 0) { bin[32+i++] = ((Dec & 1) > 0) ? '1' : '0'; Dec = Dec >> 1; } for (unsigned int j = 0; j< bitLength; j++) { if (j >= bitLength - i) { bin[j] = bin[ 31 + i - (j - (bitLength - i)) ]; } else { bin[j] = '0'; } } bin[bitLength] = '\0'; return bin; }
Ausgabe auf dem seriellen Monitor
Wenn das oben genannte Sketch auf dem Arduino hochgeladen wurde, so kann man nun beim Betätigen der Taste auf der Fernbedienung folgenden Inhalt auf dem seriellen Monitor auslesen.
Daten beim einschalten der Funksteckdose:
<--- Beginn Decimal: 83029 (24 Bit) Binary: 000000010100010001010101 Tri-State: 000FF0F0FFFF PulseLength: 359 microseconds Protocol: 1 Raw data: 11144,284,1140,296,1140,288, 1144,284,1144,288,1136, 304,1124,300,1132,1008, 424,1004,428,1000,424, 1004,432,992,432,996, 436,1000,440,276,1148, 288,1140,280,1156,996, 428,280,1148,1008,428, 280,1140,232,1772,660, 1360,72,2776, <--- End
Daten beim Ausschalten der Funksteckdose:
<--- Beginn Decimal: 83028 (24 Bit) Binary: 000000010100010001010100 Tri-State: 000FF0F0FFF0 PulseLength: 358 microseconds Protocol: 1 Raw data: 11144,300,1128,308,1124,304, 1124,304,1128,304,1132, 308,1120,1012,420,1016, 408,1016,408,1020,420, 1012,432,988,428,1012, 416,996,444,276,1144, 292,1144,284,1144,1000, 420,296,1140,1004,432, 284,1140,1004,432,284, 1136,1008,436, <--- End
Es gibt nun 2 Möglichkeiten die Funksteckdose mit den empfangenen Daten zu steuern.
Tri-State
Mit dem Hexadezimalen Wert “Tri-State”, der Pulse Länge sowie dem Wert für Protokoll kann man die Funksteckdose wie folgt steuern:
#include <RCSwitch.h> RCSwitch rcSwitch = RCSwitch(); const int senderPin = 15; // An Pin 15 const int protocol = 1; const int pulseLength = 358; const int wait = 1000; void setup() { rcSwitch.enableTransmit(senderPin); rcSwitch.setPulseLength(pulseLength); rcSwitch.setProtocol(protocol); } void loop() { rcSwitch.sendTriState("000FF0F0FFFF"); delay(wait); rcSwitch.sendTriState("000FF0F0FFF0"); delay(wait); }
Binär
Bei einigen Funksteckdosen wird der Wert “Tri-State” nicht ausgegeben, daher muss dann auf den binären Wert zurückgegriffen werden. Dazu wird die Funktion “rcSwitch.send(<<BINARY_WERT>>);” statt “rcSwitch.sendTriState(<<TRI_STATE_WERT>>);” verwendet.
#include <RCSwitch.h> RCSwitch rcSwitch = RCSwitch(); const int senderPin = 15; // An Pin 15 const int protocol = 1; const int pulseLength = 358; const int wait = 1000; void setup() { rcSwitch.enableTransmit(senderPin); rcSwitch.setPulseLength(pulseLength); rcSwitch.setProtocol(protocol); } void loop() { rcSwitch.send("000000010100010001010101"); //rcSwitch.sendTriState("000FF0F0FFFF"); delay(wait); rcSwitch.send("000000010100010001010100"); //rcSwitch.sendTriState("000FF0F0FFF0"); delay(wait); }
Ausbaustufe I – “einfach”
In der Ausbaustufe I möchte ich nun einen einfachen Taster (mit Pulldown Widerstand) an den Wemos D1 mini anschließen um dann auf Tastendruck die Funksteckdose ein bzw. aus zuschalten.
Schaltung
Der Aufbau der Schaltung änder sich nur gering da hier der Taster mit Pulldown Widerstand hinzugefügt wird.
Video
Quellcode
#include <RCSwitch.h> RCSwitch rcSwitch = RCSwitch(); const int btnUp = 13; //Taster auf PIN 13 int statusBtn = 0; const int senderPin = 15; // An Pin 15 const int protocol = 1; const int pulseLength = 358; const int wait = 450; void setup() { Serial.begin(9600); rcSwitch.enableTransmit(senderPin); rcSwitch.setPulseLength(pulseLength); rcSwitch.setProtocol(protocol); pinMode(btnUp,INPUT); //Setzen des Eingangssignals des Tasters } void loop() { int btnStatus = digitalRead(btnUp); //Lesen des Status des Tasters. if(btnStatus == HIGH){ statusBtn = statusBtn ==HIGH?LOW:HIGH; //Wenn der Taster auf HIGH ist dann die Funksteckdose EIN schalten. if(statusBtn == HIGH){ rcSwitch.send("000000010100010001010101"); } else { rcSwitch.send("000000010100010001010100"); } } }
Download
Ausbaustufe II – “Webseite”
Der aufmerksame Leser meines Blogs wird sich sicherlich an das Tutorial zum Wemos D1 erinnern, dort habe ich eine LED über eine Webseite gesteuert. Wenn man nun diesen Sketch als Grundlage nimmt, muss man “nur noch” die Funktion für die Funksteckdose und den 433MHz Sender implementieren (und dieses haben wir ja schon in der Ausbaustufe 1 abgehandelt).
Im weiteren verwende ich den Wemos D1 mini, dieser kleine Microcontroller hat den Vorteil das dieser auf das 400 PIN Breadboard passt und noch ordentlich Platz für weitere Module vorhanden ist.
Aufbau
Für dieses Beispiel werden folgende Komponenten benötigt:
- 1x Wemos D1 mini
- 1x 433MHz Funksender
- 1x LED, 5 mm, blau,
- 1x 220 Ohm Widerstand
- Breadboardkabel
- 400 PIN Breadboard
Die Komponenten werden wie auf der folgenden Grafik verbunden.
Modul / PIN | Anschluss |
---|---|
433MHz Funksender | |
VCC | 5V |
GND | GND |
Data | digitaler PIN D8 |
LED | |
Kathode (abgeflachte Seite) | GND |
Annode | digitaler PIN D5 |
Quellcode
#include <RCSwitch.h> #include <ESP8266WiFi.h> const char* ssid = "****"; //SSID aus dem Router const char* password = "*****"; //Passwort für den Zugang zum WLAN const String htmlOK = "HTTP/1.1 200 OK"; const String htmlContentType = "Content-Type: text/html"; const String htmlBegin = "<!DOCTYPE HTML><html>"; const String htmlLinks = "<head><link rel='stylesheet' href='http://progs.draeger-it.blog/wemosd1/d1.css'/><link rel='shortcut icon' href='http://progs.draeger-it.blog/wemosd1/favicon.ico' /></head><body>"; const String htmlEnd = "</body></html>"; const String htmlBreakLine = "</br>"; int ledPin = D5; //digitaler PIN 5 des Wemos D1 an welchem die LED angeschlossen ist. int ledStatus = LOW; //aktueller Status der LED (default / start -> AUS) const int senderPin = 15; // An Pin 15 const int protocol = 1; const int pulseLength = 358; const int wait = 450; RCSwitch rcSwitch = RCSwitch(); WiFiServer server(80); //Port auf welchem der Server laufen soll. void setup() { Serial.begin(115200); //Baudrate für die Serielle Geschwindigkeit. delay(10); //10ms. Warten damit die Seriele Kommunikation aufgebaut wurde. rcSwitch.enableTransmit(senderPin); rcSwitch.setPulseLength(pulseLength); rcSwitch.setProtocol(protocol); pinMode(ledPin, OUTPUT); //Den LEDPin als ausgang setzen. digitalWrite(ledPin, ledStatus); //Die LED initial auf den Status "AUS" setzen. Serial.print("Aufbau der Verbindung zu: "); //Ausgabe der SSID auf der Seriellen Schnittstelle. Serial.println(ssid); WiFi.begin(ssid, password); //Initialisieren der Wifi Verbindung. while (WiFi.status() != WL_CONNECTED) { //Warten bis die Verbindung aufgebaut wurde. delay(500); //Einen Punkt auf der Seriellen Schnittstelle ausgeben so das der Benutzer erkennt dass, das Sketch noch läuft. Serial.print("."); } //Bei erfolgreicher Verbindung wird der folgende Text ausgeben. Serial.print("Mit "); Serial.print(ssid); Serial.print("erfolgreich verbunden!"); server.begin(); // Starten des Servers. Serial.println("Server gestartet"); //Ausgabe auf der Seriellen Schnittstelle das der Server gestartet wurde. // Ausgabe der IP Adresse Serial.print("Adresse : http://"); Serial.print(WiFi.localIP()); Serial.println("/"); } /** * Die Funktion gibt den HTML Kopf auf dem Client aus. * Dieses wird für jeden Respond verwendet. **/ void printHtmlHeader(WiFiClient client){ client.println(htmlOK); client.println(htmlContentType); client.println(""); client.println(htmlBegin); client.println(htmlLinks); } void loop() { //Prüfen ob sich ein Client verbunden hat, wenn nicht die Loop "verlassen" WiFiClient client = server.available(); if (!client) { return; } // Wenn sich ein Client verbunden hat solange warten bis Daten gesendet werden. Serial.println("Neuer Client verbunden."); while(!client.available()){ delay(1); } //Lesen der Anfrage vom Client String request = client.readStringUntil('\r'); Serial.println(request); client.flush(); //Wenn in der Anfrage die Zeichenkette "/toggle" vorkommt dann... if (request.indexOf("/toggle") != -1) { if(ledStatus == HIGH){ ledStatus = LOW; rcSwitch.send("000000010100010001010100"); } else { ledStatus = HIGH; rcSwitch.send("000000010100010001010101"); } digitalWrite(ledPin, ledStatus); Serial.print("Funksteckdose Status: "); Serial.println(ledStatus); } //Gibt den Html Header auf der Antwort aus. printHtmlHeader(client); //Ab hier wird die Webseite zusammengesetzt. client.println("<div class='mainWrapper'>"); client.print("Die Funksteckdose ist : "); String value = "-undefined-"; //Die Adresse für die Bilder const String imgStartUrl = "http://progs.draeger-it.blog/wemosd1/"; String imgUrl = imgStartUrl + "light_on.png"; if(ledStatus == HIGH) { value = "AN"; } else { value = "AUS"; imgUrl = imgStartUrl + "light_off.png"; } client.print(value); client.println(htmlBreakLine); client.println(htmlBreakLine); client.println("<img src='"+imgUrl+"' widht='64' height='64'/>"); client.println(htmlBreakLine); client.print("<div class='headline'>Klicke <a href=\"/toggle\">hier</a> um die Funksteckdose "); client.print(value=="AN"?"aus":"an"); client.print(" zuschalten.</div>"); client.println(""); client.println("</div>"); client.println(htmlEnd); delay(1); //1ms. Pause }
Hallo Stefan,
danke für die Anleitung!
Mein Problem: Ich erhalte beim mehrmaligen Einschalten meiner Funksteckdose verschiedene binäre Codes. Und wenn ich einen davon sende, schaltet die Dose nicht. Was tun?
sg
Hallo,
bitte prüfe doch welche Einstellung auf der Steckdose gewählt ist. Dort kannst du in der Regel durch einen Schiebeschalter den “Port” wählen.
Dann musst du auf der Fernbedienung noch die “richtige” Taste drücken.
Welches Gerät verwendest du denn? (Marke, Modellnummer)
Gruß,
Stefan
Hallo,
Ich habe deine Anleitung exakt verfolgt, jedoch passiert bei mir nichts.
Ich hab alles ganz normal angeschlossen, habe auch schon alle Pins durchprobiert,
jedoch erhalte ich keine Ausgabe auf meinem Seriellen Monitor, wenn ich eine Taste
meiner Funkfernbedienung drücke.
Was mir aufgefallen ist, dass wenn ganz schlicht ich eine LED einschalten möchte, muss ich bei “pinMode(, OUTPUT)” den Pin auf D0, D1, …. setzen.
Mit dieser Erkenntnis habe ich auch in Zeile 5 deines Codes die 0 auf “D0” geändert,
(Was wahrscheinlich keinen Sinn macht aber nun gut) und wie zu erwarten passierte nichts.
Hast du eine Idee, woran dies liegen könnte, dass bei mir keine Ausgabe kommt ?
MFG
Hallo Seba,
welches Microcontroller verwendest du?
Versuche mal das Tutorial https://draeger-it.blog/arduino-tutorial-433mhz-sender-empfaenger/ hier wird erstmal ein ganz einfaches Sketch erzeugt. Dieses sollte eigentlich laufen.
Gruß,
Stefan Draeger
Hallo nochmal,
ich benutze den “Wemos D1 mini”, so wie du eigentlich.
Ein Update habe ich.
Ich habe es geschafft, den Sender mit dem Empfänger kommunizieren zu lassen.
Soweit so gut. Den Sender habe ich an einen “Funduino Mega2560” angeschlossen, den Empfänger an den eben genannten ESP8266. Ich bekomme auf dem Wemos ohne Probleme die vom Mega2560 gesendeten Daten. Wenn ich jetzt aber wie blöd auf meinen Fernbedienungen rumdrücke, passiert nichts. Es werden keine Daten Empfangen. Sowohl von der Fernbedienung der Steckdosen, als auch der des Rollladens. Beides basiert eigentlich auf 433mhz. Bzw. auf 433,92mhz. Aber stellt das …,92mhz wirklich ein Problem dar? Ich habe mich eigentlich schlau gemacht und es hieß, es sollte passen. Oder irre ich mich gewaltig?
Gruß,
Seba M
Wirklich toll und super beschrieben wie es funktioniert! Habe für mein Gartenhaus Funksteckdosen und habe dein Tutorial gleich umgesetzt. Nach ein paar kleineren Problemen mit der Schaltung die ich selbst beheben konnte, hat dann alles funktioniert.
Danke für das Tutorial 🙂
Hallo Stefan,
Danke für die schöne Anleitung. An einer Stelle hast Du, glaube ich, einen Fehler. Folgende Zeilen müssen vertauscht werden:
rcSwitch.setPulseLength(pulseLength);
rcSwitch.setProtocol(protocol);
Grund: setProtocol überschreibt die Pulslänge wieder, siehe https://github.com/sui77/rc-switch/issues/117
Wenn die Pulslänge nicht allzusehr daneben liegt ist es vermutlich oft egal. Bei mir habe ich lange nachdem Fehler gesucht und es dann auf sdiese Art gelöst gekriegt.
Felix
Hi Felix,
danke für den Hinweis. Auf den Fehler bin ich jedoch nicht gestoßen da bei mir alles Funktioniert hat.
Gruß,
Stefan
Hallo Stefan,
ein wirklich gelungene Anleitung – das Schalten von meinen alten Schaltsteckdosen funktionierte auf Anhieb und ich war total angefixt!
Leider hing sich mein Wemos nach einigen Minuten bis Stunden sporadisch auf, das ließ sich abschließend nur durch Reset oder Stecker ziehen beseitigen – und so habe ich mich auf Fehlersuche begeben und Folgendes rausgefunden:
//Lesen der Anfrage vom Client
String request = client.readStringUntil(‘\r’);
Dein obiger Programmcode ließ sich durch eine einfache nmap-Anfrage aus dem Tritt bringen,
vermutlich bleibt client.readStringUntil beliebig lange auf Empfang wenn es sich nicht um eine html-Anfrage handelt. Nach Einbau eines Timeouts kehrte der Wemos wieder in den Empfangsmodus zurück. Evtl. kannst Du einen solchen timeout auch in Deinem Beispiel integrieren?
Gruß
Ralf
Hallo Stefan,
Du bist die Erlösung stundenlager Googlearbeit! Perfektes auslesen in allen Formaten!!
Ein dickes DANKE!!
Hallo Stefan,
hab dein tolles Projekt heute gefunden und nachgebaut jedoch funktioniert die Schaltung nicht so richtig.
Steckdose lässt sich nur ein oder manchmal zweimal schalten (meistens ist nach dem ersten Mal Schluss) und es scheint so als ob die Verbindung verloren wird?
Nach einem Bootvorgang verbindet sich der Wemos und bekommt die IP Adresse zugewiesen. Wird diese im Browser aufgerufen schaltet sich die Steckdose ein lässt sich einmal ausschalten und dann geht nichts mehr.
So sieht es im Serial Monitor aus:
22:02:04.475 -> …….Mit FRITZ!Box7590RLerfolgreich verbunden!Server gestartet
22:02:08.283 -> Adresse : http://192.168.188.77/
22:02:09.133 -> Neuer Client verbunden.
22:02:09.133 -> GET /toggle HTTP/1.1
22:02:09.575 -> Funksteckdose Status: 1
22:02:20.961 -> Neuer Client verbunden.
22:02:20.961 -> GET /toggle HTTP/1.1
22:02:21.403 -> Funksteckdose Status: 0
22:02:21.403 -> Neuer Client verbunden.
eine Idee wie das behoben werden könnte, Am Board liegt es denk ich nicht denn ich hab schon ein anderes ausprobiert mit dem selben Ergebnis.
Danke Christian
Hi Christian,
ich habe mir das mal nachgebaut aber bisher keinen Fehler finden können.
Hast du ggf. einpaar weitere Hinweise für mich?
Welchen Mikrocontroller hast du verwendet?
Welches Funkmodul verwendest du? (hier ggf. ein Link zu ebay oder ähnlichem)
Gruß, Stefan