A couple of years ago I built a pretty basic smart home application allowing me to control my remote controlled sockets via an Android app or a Web Extension. It’s based on the rcswitch library run on an Apache. The 433 MHz signals are sent by a FS1000A transmitter hooked up via GPIO to a Raspberry Pi. The whole setup lied on the ground in a corner of my apartment behind a curtain next to my network wall jack. Now where we have just moved to a nice new and twice as big home, I needed a solution which could be placed in the middle of all rooms to allow the rather weak 433 MHz signals to reach every receiver. Additionally, I wanted to get rid of having to maintain a full Ubuntu server, which only serves as a light switch for the most part.
Around that time, I stumbled upon the ESP8266: a low-cost WiFi microchip with full TCP/IP stack and microcontroller capability – ideally soldered on a NodeMCU or Wemos D1 for the maximum level of convenience. Arduino and Wifi: a whole new world of IoT-possibilities. Once you’ve added the board manager to your Arduino IDE, you can use those tiny boards just like an ordinary Arduino. As the Arduino WebServer library can turn a NodeMCU development board into a light-weight HTTP server and the rcswitch library is also available on Arduino, I decided to put both – NodeMCU and FS1000A – into a junction box to create a DIY 433 MHz RF WiFi bridge.
To reach the bridge you should either assign a static IP or use mDNS. Keep in mind that mDNS is not supported by all operating systems out of the box. If in doubt, use a static IP. To make the bridge accessible from outside your home network, you need to open and forward a port on your router (port 80 by default and can be changed in line 10). It’s recommend to secure any connection made through the public internet. By the time I was writing the script, there hasn’t been a HTTPS server implementation around. However, I found HelloServerBearSSL while writing this article. It looks very promising and is definitely worth a try.
This project works with simple DIP-switch remote outlets only. It became quite hard to get the “old” DIP outlets as most producers switched to the “new” outlets, which use a button on the receivers to “learn” a signal. There is a NewRemoteSwitch library to deal with them. But as I just recently found one last triple pack in a dollar store, I am stocked until I will eventually move to Wifi controlled outlets.
Enough talk. A typical request contains the five digit system code, five digit unit code and a binary power code separated by comma. You can also concatenate multiple commands using semicolon. A sample request to switch on outlet A and switch off outlet B would look like this:
x.x.x.x/switch?command=10010,00001,1;10010,0010,0
Here is the code. Further down is a download link.
#include <ESP8266mDNS.h> #include <ESP8266WebServer.h> #include <ESP8266WiFi.h> #include <RCSwitch.h> #include <WiFiClient.h> const char* ssid = "SSID"; const char* password = "PASSWORD"; ESP8266WebServer server(80); RCSwitch rcswitch = RCSwitch(); void setup(void) { Serial.begin(115200); digitalWrite(2, HIGH); // Turn onboard led off rcswitch.enableTransmit(15); WiFi.mode(WIFI_STA); // (WIFI_AP) will spawn an access point. useful when no router is present. WiFi.begin(ssid, password); Serial.println(""); if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Wifi connect failed! Rebooting..."); delay(5000); ESP.restart(); } MDNS.begin("bridge"); // will make the bridge available under bridge.local Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); server.on("/switch", handleSwitchRequest); //Associate the handler function to the path server.begin(); Serial.println("HTTP server started"); } int countCharacterOccurrence(char *haystack, char needle) { int count = 0; for (int i = 0; i < strlen(haystack); i++) if (haystack[i] == needle) { count++; } return count; } void handleSwitchRequest() { bool isBadRequest = false; String response = ""; if (server.arg("command") != "") { char commands[1024]; // make some room to get the string server args server.arg("command").toCharArray(commands, sizeof(commands)); // convert string to char array char *pointerCommands = commands; // create a pointer and store commands at its memory's address char *commandToken; char *subCommandToken; while ((commandToken = strsep(&pointerCommands, ";")) != NULL) { // delimiter is the semicolon. we are using the address of the pointer to the input string. strsep expects the address (pointer) of a pointer to the string that should be separated. if found, pointerCommands is updated to point past the delimiter. returns a pointer to the result token. int counter = 0; char* commandArray[3]; if (countCharacterOccurrence(commandToken, ',') != 2) { isBadRequest = true; response += "ERROR:\t" + String(commandToken) + " - Bad format. Expected: ?command=10000,10000,1;\n"; continue; } while ((subCommandToken = strsep(&commandToken, ",")) != NULL) { // delimiter is the comma commandArray[counter++] = subCommandToken; } if (strcmp(commandArray[2], "1") == 0) { rcswitch.switchOn(commandArray[0], commandArray[1]); response += "ON:\t" + String(commandArray[0]) + "," + String(commandArray[1]) + "," + String(commandArray[2]) + "\n"; } else if (strcmp(commandArray[2], "0") == 0) { rcswitch.switchOff(commandArray[0], commandArray[1]); response += "OFF:\t" + String(commandArray[0]) + "," + String(commandArray[1]) + "," + String(commandArray[2]) + "\n"; } else { isBadRequest = true; response += "ERROR:\t" + String(commandArray[0]) + "," + String(commandArray[1]) + "," + String(commandArray[2]) + " - Power must be either 1 or 0.\n"; } delay(50); // will otherwise hick up and fatal } } server.send(isBadRequest ? 400 : 200, "text / plain", response); } void loop(void) { server.handleClient(); }
DOWNLOAD: 433MHzWifiBridge Project