Creating a DIY 433 MHz ESP8266-based Home Automation bridge to switch DIP remote control outlets

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.

Firefox WebExtension used to turn on desk lamp
Firefox WebExtension used to turn on desk lamp

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.

433Mhz RF WiFi Bridge Junction Box opened and closed
433 MHz RF WiFi Bridge Junction Box opened and closed

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