Dockerising the RF to MQTT gateway

Dockerising the RF to MQTT gateway

This is the second attempt at dockerizing a RF signal sniffer and transmitter. I wrote a while ago about creating an RF to MQTT Gateway, to allow Home Assistant to communicate with cheap, battery powered devices that run on radio-frequency signals such as 433MHz. The previous post is about using an Arduino to accomplish this. It works fine but it runs on a dedicated hardware device that consumes power1.

I love Docker and dockerizing applications and processes and the next step was to eliminate this hardware device and containerise it instead on a piece of hardware that I have plugged in 24/7 anyway - the Rasperry Pi.

One of the benefits of Docker is that it allows you to virtualize a dedicated device, such as the program running on Arduino, and run it on a shared host. Why run this simple gateway on physical hardware when I can run it as yet another software container on my home automation hub? I am not using any of the GPIO on my Pi and combining these features onto the same hardware is pretty cool! Check it out:

Raspberry Pi with RF Receiver Hardware

Hardware Parts

PartModelDatasheet
RF ReceiverSRX882 v1.3Link
RF TranmitterSTX882 v1.2Link

RF Receiver and Transmitter Close Up - Top RF Receiver and Transmitter Close Up - Bottom

You can access and clone the repository here, all images have been pushed to Dockerhub. A simple clone and docker-compose up command should orchestrate the entire set up for you!

After cloning, add your MQTT brokers connection details in receive.py and wire your RF receiver to your Raspberry Pi. The data pin is on pin 27 by default. Wire as shown in the image gallery below.

You can test this setup by running docker-compose up and sending some RF commands. The Python script will pick up those signals and print them to console.

Raspberry Pi showing the RF receiver set up. (no RF emitter at this point)

Signal Range

The signal range is about 20 meters. It works well for devices within this range with little latency. I found this device picks up a lot of noise on other RF protocols (2-4) and I made the script ignore those protocols. In order to reduce the amount of noise, you can connect a capacitor across the power pins.

Signal Latency

If you are going to take the dockerised approach as described in this article, then I suggest you ensure your Rasbperry Pi is not overloaded with processes. If your Pi is under load or running lots of containers/processes in parallel, then this will introduce latency to the RF signal processing because less CPU time is available to the processing of real-time RF signals, resulting in delays and signal loss.

My Pi was running at 5% CPU utilisation, which is why I never considered CPU time to be a contributing factor. It is true though, if the Python container is not running (as another process is occupying the CPU) at the exact moment a new RF signal is registered by the RF receiver module, then the RF gateway container will miss that RF signal, resulting in signal loss.

This manifested itself in unreliable RF signal reception and having to hold a RF remote button for 3-5 seconds before a signal code is captured and processed. This was driving me nuts because I could not identify the bottleneck.

When I migrated my Docker stack to a different machine (and shut down containers on the Pi except for the rfgateway), then these problems disappeared because the Pi was able to dedicate more CPU cycles to running the rfgateway container.

Update October 2018: A combinatino of slow network, CPU time allocation and inefficient Python code caused the problems described above.

I have since optimised the Python code to be as performance efficient as possible. This involved removing unnecessary log statements and using a different MQTT publish function (this improved performance considerably).

The script is now fast enough to pick up multiple signals sent by the same source (a RF error correction measure). Some additional signal-cleaning logic was implemented to discard those duplicates and only send one MQTT message into the MQTT and home automation system.

RF signals are now picked up instantaneously, resulting in an end-to-end response time of <0.5s. That is, from RF button press to completion of HA automation. This makes the system very responsive.

Implementation Details

I was gonna try two different RF implementations - one Python based, the other C++ based, because I read online that any Python GPIO interfacing has timing issues because of garbage collection and Pythons inaccurate sleep function. The Python image is based on the arm32v7/python docker image and the rpi_rf python package. It works well so far.

I included a C++ test docker image below, however it seems that my RF receiver module is not compatible with the rc-switch library used in the C++ program. If you have any luck in running the container and sniffing RF codes, please leave a comment. The program should work, as much is promised here.

Since I got the Python implementation to work, I did not bother completing the C++ implementation. If you make any progress please let me know so I can check it out. I am interested in a C++ version because Python’s garbage collection is supposedly very slow, which impacts the timing of RF signals.

Now we’ve got the RF to MQTT gateway working we can start integrating some RF devices into Home Assistant! Exciting times indeed.

  1. It’s power supply may actually consume more power than the micro controller itself. ↩︎