I’ve been using LightwaveRF‘s range of wirelessly controlled lighting and power outlets for some time and I’ve built up a collection of switches and remote controls for them. These switches broadcast control messages, formed of a command (e.g. on/off) and a unique identifier for the transmitter, using a simple on-off keying modulation on 433.92MHz with each light fitting or other device being “paired” to one or more transmitters. In reality the pairing is actually just telling the device which command(s) to respond too – the transmitter units can’t receive anything which prevents a bidirectional pairing. This all runs in parallel with the LightwaveRF network bridge which is really just an IP-controlled 433.92Mhz transmitter.
With my home automation system now growing to include Philips Hue and various homemade devices I’d like the LightwaveRF switches and remote controls to be able to control more than just LightwaveRF devices which means taking them our of their constrainted world of 433.92MHz. To do this I am building a set of 433.92MHz receivers to forward the command messages to my MQTT broker. This means I can extend my existing set of automation rules to react to the LWRF switch commands by, for example, sending a command to the Hue Hub to control a Hue light. I can also build up more complex operations with other rules such as only performing certain actions outside of daylight hours, or controlling multiple devices at the same. This also allows for more interesting control of the LightwaveRF devices themselves because I can now insert rules between the transmitter and receiver simply by not pairing the device with the transmitter and instead routing everything through the MQTT world and the LightwaveRF network bridge.
The prototype is based on three main components:
- A 433.92MHz receiver – I’m using a cheap one from Amazon. This has a 17.3cm wire antenna soldered on to it.
- An Arduino Uno R3 to handle the demodulation of the on-off keying and the protocol decoding.
- An existing Raspberry Pi to forward the decoded messages from the Arduino to the MQTT broker.
Why the separate Arduino? That’s because I don’t then have to worry about responsiveness of the decoding software if it was to be running on a Raspberry Pi alongside other stuff – some of which may also be latency sensitive.
There is plenty of material online (see the resources below) about the LightwaveRF protocol. I found that none of the existing code (at least what I could find) worked in the way I wanted so I implemented a decoder from scratch using the various online protocol resources as guides.The decoder, running on the Arduino, is formed of three main pieces. Firstly a demodulator that looks for state changes on the data coming from the 433.92MHz receiver. This then feeds the demodulated bits into the main protocol decoder which looks for the message start bit, each byte’s start bit, and a valid pattern for the byte (which is a form of 4b8b encoding). The final piece kicks in once the transmission has ceased for 25ms: the buffered received data is de-duplicated and for each unique message a MQTT message is sent over the serial port (via USB).
The format of the MQTT message is topic=”LWRF433/<unitID>/<channel>”, message=”<command 0=on,1=off>|<level>”, where:
- <unitID> is the unique 3 byte identifier for the switch unit
- <channel> is the channel number, the meaning of which varies across the different type of switches
- <command> and <level> tell the light what to do and vary depending on whether the controlling switch is a simple switch, dimmer, or mood switch.
The initial prototype is connected to an existing Raspberry Pi running LibreELEC/Kodi as a media centre. The Arduino is connected to a USB port on this Pi. The function of the Pi is simply to forward MQTT messages received over the USB serial port from the Arduino to the house MQTT broker. To do this in the LibreELEC/Kodi environment is a little trickier than in a general Linux/Raspbian environment due to the locked-down, read-only nature of the LibreELEC distribution. However the Kodi add-on mechanism provides a reliable way to do this.
I created a Kodi add-on formed of two main parts. Firstly a Python script that acts as the main forwarding loop between the USB serial port and the MQTT broker. This is based on code from a previous, albeit standalone, proxy doing largely the same thing – it includes a device scanning function to watch for Arduino devices appearing and to fire up proxy threads for each one – this was originally done to avoid static configuration and to allow for hot-plugging of new Arduino devices (this was in a far more complex, multi-Arduino system, compared to this simple case).
The second part of the Kodi add-on is really just boilerplate and plumbing to kick off this script.
So putting it all together this is how it works:
- A button is pushed on a LightwaveRF switch, this sends a message (actually a burst of several) on 433.92MHz.
- The 433.92MHz receiver module receives these messages and passes the raw data to the Arduino.
- The software on the Arduino decodes the messages, de-duplicates and sends one or more MQTT messages over the USB serial port.
- The proxy script running on the Kodi Pi forwards the MQTT message(s) to the MQTT broker.
- Part of my MQTT rules engine is subscribed to LWRF433 messages and receives the message(s). After debouncing, the relevant rule is invoked, e.g. sending a separate MQTT message to control a Hue light.
The initial use-case is to use the LightwaveRF remote control to turn off the room lights, pause whatever the media centre is playing, and turn off the TV, all with a single button push. To do this I created a rule that on receipt of the appropriate LWRF command via MQTT sends out two more MQTT messages, one to turn off the TV and one to pause Kodi. Both of these are handled by a separate add-on running on the same Kodi Pi, but that piece of functionality is completely separate to the LWRF add-on described above and is the subject of a future blog. The room lights turn off directly in response to the LightwaveRF 433MHz command without any MQTT infrastructure being used.