Smart power sockets are the simplest way to automate your home, but at around $40 each for a Wi-Fi or ZWave based socket, you're unlikely to buy more than a few.

You might already have some cheap RF based sockets though – the kind that come with their own custom remote control, and have some channel and ID selectors on the back. Unfortunately, there are no smart home hubs on the market that work with those. Wouldn't it be great if you could link those into your DIY smart home system somehow? Well, you can – quite easily in fact – with around $10 in parts.

With a little more work, you can also integrate some other bespoke RF-based remote hardware, like this budget cinema screen.

What You Need:

  • ESP8266 NodeMCU v12E dev board (exact model doesn't matter, v1 or v3 should be fine too). The reason we're using a NodeMCU board is because we want the easy Wi-Fi connection later. The link is for a pack of 2, which works out at $7 each.
  • Package of 433Mhz transmitter and receiver (around $3).
  • RCSwitch and MQTT libraries, and our code – all available to download from Github.
  • An MQTT server, local or remote.
  • Some RF controlled sockets running on 433 mHz band (it should say on the remote control). I bought mine from Maplin as a pack of 3 for about £20 ($25.89).

If this is your first time programming the NodeMCU board, you'll need to download the Arduino plugins for it: follow along with the first part of our Arduino Killer introductory guide for the NodeMCU/ESP8266 chip. You'll also need CH430 drivers. You can find signed macOS CH430 drivers here, or Windows here.

I've used v1.6.5 of the Arduino because anything higher introduces more problems than it solves. Downgrade if you haven't already.

Before you proceed, I'm going to assume a basic level of knowledge about Arduino programming, and that you have your NodeMCU setup in the board manager, and are able to correctly upload some demo code. You should also have added the libraries included in our download to your Arduino/libraries folder.

If you have an existing PubSubClient or MQTT library, back it up and remove it - the one I've included in the download is the only one where I could reliably receive messages on NodeMCU, and I tried a lot!

RF Sniffing (Optional)

This step isn't needed if you only want to control DIP-switch or dial selector plug sockets – those are supported out of the box, and minimal code modification will be needed (this is still interesting to do first, though, so you'll understand what's going on behind the scenes).

If you have other RF remotes that you'd like to try adding, you'll need to first "sniff" the RF codes being transmitted. To do so, load up the ReceiveDemo_Advanced sketch from the Menu -> Examples -> RCSwitch folder, and change the following line from 0

        mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2
    

to 2.

        mySwitch.enableReceive(2); // Receiver on GPIO 2 / D4.
    

Wire up the receiver module as follows. Looking at the front of the receiver board (it's the longer of the two, the transmitter is square) – the side with the components on:

  • Far right is GND. Connect to GND on the NodeMCU board.
  • Far left is VCC. Connect to VIN on the NodeMCU board.
  • Middle two pins are the signal. Connect either one to D4 on the NodeMCU (they are connected together, so it doesn't matter which).
433 wiring-1

Now upload the modified ReceiveDemo_Advanced, and when it's done, open up the serial monitor and start pressing buttons on your remote controls. Copy out the decimal (including bit length), pulse length, and protocol when you press a button.

screen RF sniffing

After doing this, I found my projector screen was using

  • SCREEN UP: Received 8694273 / 24bit; Pulse length: 355 or 356; Protocol: 1
  • SCREEN DOWN: Received 8694276 / 24bit;Pulse length: 355 or 356;  Protocol: 1

Continue for as many buttons as you need.

Testing the Transmitter

Next, we're going to try sending codes using the transmitter. Wire the transmitter module (the square one) as follows. Be careful: the labelling on these pins is atrocious.

The VCC pin is actually in the middle, not on the left side. I destroyed one module in the process of figuring this out. That thing that says "ATAD" is actually "DATA", spelled backwards. Again, the data goes to D4, VCC to VIN, and GND to GND (remove the receiver module, you no longer need it).

433 wiring-2

Load up the Examples -> RCSwitch -> TypeB_WithRotaryOrSlidingSwitches, and again, change the data pin:

        mySwitch.enableTransmit(10);
    

to

        mySwitch.enableTransmit(2);
    

Note, a variety of examples are in included in the library, and which one works for you will depend on the exact type of switch that you have. Type A (dip switches) and B (dials or sliders) are the most common – refer to the pictures on the RCSwitch page. For type B, turning on and off a socket is as simple as:

        mySwitch.switchOn(1, 4);

mySwitch.switchOff(1, 4);

where 1 is the channel ID (the top dial), and 4 is the socket ID (the bottom dial). These were written in roman numerals on my sockets. A maximum of 16 individual sockets can therefore be addressed, though multiple sockets can use the same address if you have multiple devices to turn on at once.

However, my projector screen was a little different - it used a different pulse length. So, to operate those, the following worked. Note you can also define a different protocol if your remote needs it, BUT make sure you define the protocol BEFORE the pulse length. The pulse length is overwritten when changing protocol.

        
// Note that my screen actually requires TWO button presses (not a long press, but two physical presses), so I'm delaying a bit then sending the same signal again

void screenUp(){
  mySwitch.setPulseLength(358);
  mySwitch.send(8694273,24); // (decimal code, number of bits)
  delay(2000);
  mySwitch.send(8694273,24);
}
void screenDown(){
  mySwitch.setPulseLength(358);
  mySwitch.send(8694276,24);
  delay(2000);
  mySwitch.send(8694276,24);
}

Test all your codes are working first before you move on to the next step.

Controlling via MQTT

Open up the sketch you downloaded from Github named mqtt_rcswitch.ino, and start by modifying the network SSID and password for your home. Then, change the channel name if you wish, and set the MQTT server. If you don't already have an MQTT server running on your OpenHAB install, read part 2 of our OpenHAB beginner's guide. Note that my code is designed for type B (rotary switch) sockets, though you could easily modify it for DIP switches too.

The most important part of the code is the messageReceived() function, which responds to incoming MQTT commands. In this function, we're first checking for the major keyword – I chose "switch" and "screen". In the case of "switch", we then parse out the channel and plug ID; then check the payload body for the command.

        
void messageReceived(String topic, String payload, char * bytes, unsigned int length) {
  
  if (topic.indexOf("switch") >=0){
    //switch control, parse out the channel and plug id
    
    int channel = getValue(topic,'/',3).toInt();
    int plug = getValue(topic,'/',4).toInt();
    
    if(payload == "on"){
      mySwitch.switchOn(channel, plug);
    }
    else{
      mySwitch.switchOff(channel, plug);
    }
  }
  else if (topic.indexOf("screen") >=0){
    //screen control
    if(payload == "up"){
      screenUp();
    }
    else if(payload == "down"){
      screenDown();
    }
  }
  /* add another else if here to listen for more commands (or just modify the one above if you dont want screen) */
}

By default then, the following MQTT commands work:

livingroom/control/switch/X/Y (where X is channel, and Y is plug ID; with message body on or off)

livingroom/control/screen (with message body up or down)

Use the command line or a GUI MQTT client to test your devices before adding to OpenHAB.

Adding to OpenHAB

As a last step, we just need to create some items for these switches in OpenHAB. I've defined the following items for now, but you should be able to figure out how to add more:

/* RF433mHz devices */

Switch CinemaScreen "Screen" (Cinema) { mqtt=">[broker:livingroom/control/screen:command:ON:down],>[broker:livingroom/control/screen:command:OFF:up]"}

Switch Switch41 "Switch41" (Cinema) {mqtt=">[broker:livingroom/control/switch/4/1:command:ON:on],>[broker:livingroom/control/switch/4/1:command:OFF:off]"}

Switch Switch42 "Switch42" (Cinema) {mqtt=">[broker:livingroom/control/switch/4/2:command:ON:on],>[broker:livingroom/control/switch/4/2:command:OFF:off]"}

You should now be able to control your RF devices from OpenHAB! One thing I was pleasantly surprised by was the range – a single node was able to cover most of my house. You can of course add another node, listening to the same channel, which simply repeats the same commands, if you need further coverage.

The only limitation to bear in mind is that the sockets themselves can't report their status, so if you use the original remote, control the status of the socket may not be accurately reflected within OpenHAB. Stick to using just the OpenHAB interface and you should be fine.

Questions or problems? Ask away in the comments, and I'll do my best to help. If you'd like to improve on my code, feel free to submit a pull request.