Nerves @ 434 MHz

Use Nerves to control RF items around you

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.


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 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 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 for more install options)


$ brew install librtlsdr


$ 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/

🎉 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"}

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/ ~/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), "")
    Replex.replay(file, 433907740, sample_rate: 250_000)

And take it for a run…


Success! 🎉🕺


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.


comments powered by Disqus