Controlling radio switches with a Raspberry Pi

November 23, 2019

Introduction

During the Holiday season of 2018, I thought it would be really cool to control Christmas lights remotely from a phone or website. In fact, we already had our Christmas tree lights hooked up to a radio controlled switch with a remote. It seemed trivial to instead have a Raspberry Pi send radio signals to the switches rather than a remote. All I needed to know was what frequency and signal to send.

Research

First, I started just like I start all other projects: with lots of research. First, I wanted to know what frequency the switches listened to. A quick look at the product page for the switches shows us this information:

Specs

Etekcity ZAP Remote Outlet Switches
Voltage: 120V/60Hz
Frequency: 433.92MHz

I continued with research into the 433.92MHz Frequency. Quickly, I found that our switch transmits on a very common radio band known as LPD443. LPD443 is used for low-power devices such as Smart Home devices and keyless entry systems. Our switch specifically listens to channel 35 of LPD443 at 433.925MHz.

Now that I knew what frequency to transmit on, I needed to know what to transmit. Using my RTL-SDR radio dongle, I tuned to that frequency, pressed the button, and listened with GQRX, a radio receiving program.

What I saw (and heard) wasn’t as exciting as you might think: I saw a tiny little blip on the screen and a short burst of static noise. Wow, I thought, That tells me literally nothing! So obviously I needed some sort of decoding tool to analyse this signal.

Decoding the Signal

RTL-433 does exactly that. I downloaded the github repository, compiled the program, and ran it. Here’s what I saw:

Terminal Output

RTL-433 showed me exactly what the remote control was transmitting to turn the lights on and off. Further experimentation and analysis of the signals yielded these patterns:

Switch 1 on:
00000100 00010101 00110011 0
...repeats for 7 more packets
00000100 00010101 00000000 0

Switch 1 off:
00000100 00010101 00111100 0
...repeats for 7 more packets
00000100 00010101 00000000 0

Switch 2 on:
00000100 00010101 11000011 0
...repeats for 7 more packets
00000100 00010101 10000000 0

It seems the first 2 bytes are the remote identifier, which changes with each set of remote and switches (I tested another set that my mom bought). The last 2 bytes are the control sequence, which is unique for switch 1,2,3 and on or off.

Now all that’s left is to program a raspberry pi to transmit PWM signals with those bytes and everything should work (keyword should).

Transmitting Signals with a Raspberry Pi

After doing some considerable searching about transmitting signals directly with the raspberry pi’s GPIO pins (something I’ve done before with pifm), I struggled to find any reasonable articles that provided a solution. Most articles I found discussed using GPIO pins to transmit FM signals. I initially inferred that if FM signals can be transmitted by GPIO pins then so should PWM signals.

With no solution in using the pins themselves to transmit signals, I searched for other options.

I discovered, signals on the 433MHz frequency can be easily transmitted using a transmitter module like the one found here. I ordered the pack of 10 transmitter and receiver modules (only 10 bucks!) and a Raspberry pi kit (I already had an RPI2B) with breadboard so I could connect the transmitters to the raspberry pi.

While waiting for the shipment to come in, I started work on a python project that listened for HTTP requests with specific paths that let me control the radio switches. The project is hosted on Github here. It’s gone through a couple of revisions since I first wrote the software, but the idea is still the same. As a bit of a coding challenge, I decided to make the project modular and scalable with other switches and devices that I may want to control in the future. The software is running live at pi.joewebserver.com.

Finishing the Project

To make a long story short, after my raspberry pi kit came in, I managed to fry my board by plugging some wires in wrong. This was my first time ever using a breadboard and jumper wires, so I was pretty distraught. My blunder put the project on hold for a couple of weeks.

Fast forward, a new Raspberry Pi 3 arrived in the mail. I wired up the transmitter and breadboard (correctly, this time) and fired up my raspberry pi. In order to transmit signals, I needed another utility program, 433Utils, to send PWM signals on a GPIO pin connected to the transmitter. Looking at some example code and modifying an existing file, I wrote this script to send codes, which my python interface would call.

switchctl.cpp

#include "../../rc-switch/RCSwitch.h"
#include <stdlib.h>
#include <stdio.h>


int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("more arugments!\n");
        return -1;
    }

    int switchnum = atoi(argv[1]);
    int command = atoi(argv[2]);
    int code = 0;
    int endcode = 0;

    if(switchnum < 1 || switchnum > 3 || command < 0 || command > 1) {
        printf("Invalid switch/command range");
        return -1;
    }

    if(wiringPiSetup() == -1) return 1;
    RCSwitch rx = RCSwitch();
    rx.setPulseLength(175);
    rx.enableTransmit(0);

    if(switchnum == 1) {
        endcode = 267520;
        if(command == 1) {
            code = 267571;
        } else {
            code = 267580;

        }
    } else if (switchnum == 2) {
        endcode = 267520;
        if(command == 1) {
            code = 267715;
        } else {
            code = 267724;
        }
    } else if (switchnum == 3) {
        endcode = 267520;
        if(command == 1) {
            code = 268035;
        } else {
            code = 268044;
        }
    }


    rx.setRepeatTransmit(8);
    rx.send(code, 24);
    rx.setRepeatTransmit(1);
    rx.send(endcode, 24);
    return 0;

}

The compiled script is called through the python interface with a subprocess. This was my first time coding in C++, and I really enjoyed it!

Conclusion

In the end, the project was unfortunately finished after Christmas day, but I was still able to test my code using the lights on the christmas tree. I can’t describe the joy I felt tapping a button on my phone and seeing the lights turn on. The best part was, I made that happen; I was the one who coded the back end. Christmas is also my favorite holiday.

I continued refining the code and web interface after my initial tests. The site on pi.joewebserver.com is hosted on my main server, and requests to the python interface are proxied through Nginx.

I really enjoyed working on the project since I learned so much about radio technology and coding. It was also a great exercise to practice my coding skills in python and html/javascript. This project is probably one of the most important projects I’ve done, since I still use it on a daily basis for my bedroom lights!

Writing this from the future, there are some major improvements I could make to the code and the methods used. I now know of WSGI applications, so instead of hosting the websever directly in python, I could use something like gunicorn to handle requests far more efficiently. I could have also used a web framework like Flask to handle request parsing. In fact, looking at the code now, I see some major vulnerabilities in the way I handle request strings! I should probably fix that since the site is open to the web…