Many home and office items use radio frequencies to communicate control. So naturally, lets take that over to control them from a single Nerves device with one wire on a GPIO pin.
TL;DR
This was presented as a lightning talk at ElixirConf 2019. If you want a brief overview, check out the video 👇.
The Problem
A while back, I needed to replace our circa 1980’s ceiling fan. It was loud, clunky, and its time had come. I thought it would be fun to replace it with one that had a remote because its 2019. Our world is more and more connected. Plus, #yolo.
After some research, I settled on the Hunter Windemere and revelled in my new found ability to lazily control a ceiling fan from my bedside. The world was mine having been freed from the rigorous act of getting up to flip switches like a chum.
O how wrong I was…
Turns out that adding a remote just adds another point of failure. One that can very easily be misplaced in the abysses of my home.
Plus, the fan remembers the last state meaning that if you turn the light off with the remote, toggling the wall switch off/on will have no affect. The fan will always be in that off state, even when power is introduced again!
So lets fix this and bring back my needless convenience!
A Frivolous Quest
You might be thinking “How are you going to do this, Jon?!”. Well, the answer is simple…
As it turns out, the remote uses radio frequency to transmit the control the fan. Each button on the remote has a different signal and payload to represent the desired command. We could try to demodulate this, reverse engineer the payload, then recreate sending the data on the same frequency. Or…we save quite a bit of time and implement what is commonly as a replay attack. Simply put, you use a small receiver to create a copy of the signal. Then just send that signal from your own transmitter effectively “replaying” what the remote has built in.
This doesn’t work in every case, such as garage doors and some security locks which typically use a rolling code implementation specifically to prevent replay attacks. But for most RF controller items in the home, like the fan, security isn’t really a concern so the signal is simple and copyable.
For this case, we’ll just focus on the fan light. Though for a multifunction remote (with many buttons), you would just repeat this procedure for each button.
Record the signal
We’ll actually need a little bit of hardware for this part. Specifically, some sort of receiver that can receive the signal and create a file for you. Something like SDR (Software Defined Radio) which just makes it easier to deal with radio signals in software rather than traditional hardware. For this, I’ll be using the RTL SDR.
You’ll also want some way to interact with the dongle. For simplicity, I use an application with GUI to help narrow down the frequency, and the command line to record the signal. You can find lots of different software options on rtl-sdr.com. I’m using MacOS and alternate between CubicSDR and Ultimate Radio Hacker which are 2 open source and universal applications.
Before recording, we need to know what frequency the device is transmitting at. By law, any transmitter device commericially produced and sold has to register an ID and transmit frequency so we can use sources like https://fccid.io to look that up. For my remote, I just had to find that FCC ID on the back:
Looking that up, I see my remote uses 434 MHz
. But, we need a little more precision
so this is where the software comes in place. Fire up CubicSDR
, plug in your SDR,
and then set the center frequency
to something close to the FCC defined frequency.
Then just click a button on the remote and look for where the signal peaks. Hover
your mouse over the center (or as close as you can tell) and save that frequency.
You’ll need to use it when recording and transmitting.
Now that we have a little more precise frequency, we need to use it to record the signal. For this, I used the rtl_sdr CLI for recording I/Q files and installed it via the command line:
(You can also look at rtl-sdr.com for more install options)
MacOS
$ brew install librtlsdr
Linux
$ sudo apt-get install rtl-sdr
Then plug in your frequency and path to where you want the recording to save and run it.
While running, hit the desired button on your remote a few times, then manually kill
the process when done with ^C
(Control+C):
$ rtl_sdr -s 250000 -f 433907740 ~/recordings/fan_light.iq
🎉 We now have a recording to replay!
Replaying the signal
This is where things get cool.
Normally one might need specific transmitting hardware that could potentially get expensive. But I have a Raspberry Pi 3+, Nerves, and a determination to save money. So I found this open source library called rpitx which allows you to transmit signals from 5 KHz - 1500 MHz from a single GPIO pin! And since most home devices fall within 300-400 MHz (in the US), this works perfectly. Currently GPIO 4 is the required pin, but with a little editing of the library, you could also get GPIO 6 and GPIO 20 to work as well.
Yes..that is all you need…
There is actually a lot to rpitx, but all we
really need is the sendiq
binary for transmitting an I/Q recording file. So to make that easier in Nerves
(or Elixir in general), I created replex
which includes precompiled sendiq
binary and some small wrappers to make things
easier in Elixir land.
In short, replex
is just a really small
wrapper of convenience functions. Use it, copy it, reimplement, whatever works for
your use case.
For my case, I want this in Nerves and created a small radio
project, then added replex
as a dependency:
def deps do
{
#...other deps
{:replex, "~> 0.1"}
}
end
Building an Elixir release copies all the contents from the priv/
directory into
the build. And since Nerves firmware is utilizing Elixir build process, that’s where
we want to put our recording so it gets copied to the device:
$ mkdir -p ~/repos/radio/priv
$ cp ~/recordings/fan_light.iq ~/repos/radio/priv/
Then I add a little bit of code to my Radio
module to transmit my recording:
defmodule Radio do
def fan_light() do
file = Path.join(:code.priv_dir(:radio), "fan_light.iq")
Replex.replay(file, 433907740, sample_rate: 250_000)
end
end
And take it for a run…
Success! 🎉🕺
Conclusion
From here, we can do whatever we want to interface with the Nerves device to control all the things. I added buttons all around the room that connect to my Nerves device so that I can control the fan no matter where the remote ends up. You could do the same, build a web page with buttons, add a clapper, etc etc. I’ll let you take it from here to decide how much further you can take this.
🍻