Introduction: Mercury RF Remote Socket Control From Raspberry Pi
How to control Mercury RF remote sockets from the Raspberry Pi using Python.
Step 1: Preamble
I wanted to build a custom controller to monitor and control the environment in my (sub)tropical greenhouses, and the first steps were to monitor and control the humidity (heating is already controlled by thermostatic systems attached to the heaters). I wanted to use mains switching controls (in the UK 240 volts AC) and did not wish to have mains electricity anywhere close to the computer (Raspberry Pi) especially in a humid environment. This then requires the use of Mains remote switching, and I selected the AVSL Mercury RF Remote Socket Adaptor sold by Amazon (Mercury Part Number 350.115UK, Amazon ref. http://www.amazon.co.uk/Mercury-350-115-Remote-Control-Adaptor-Black/dp/B0051NIJA4 ) pictured above. These are relatively low cost, and had good reviews.
The next step was to get these sockets to work with the Raspberry Pi, and for this I selected the RF modules described as "433Mhz RF transmitter and receiver kit" by Amazon (search for"rf transmitter") shown here, and these are ridiculously cheap at £1.08 for the set (as of Jan 2016).
Step 2: Initial Investigations
Having assembled the parts that I needed, it was now time to investigate the software further. I had already planned to use software on the Pi that required the use of the GPIO Library, and to write the main controller in Python. Reading several web pages that describe how to control RF transmissions (for example https://github.com/lexruee/pi-switch-python ) and reading how to use RF Sniffer ( http://www.princetronics.com/how-to-read-433-mhz-codes-w-raspberry-pi-433-mhz-receiver/ ) plus other articles ( http://blog.rot13.org/2013/10/433-mhz-power-sockets-with-rc-switch-arduino-or-raspberry-pi.html ) I eventually got to the stage of recording a received signal from the Mercury hand-held controller through RF Sniffer on the Pi, and then sending the same signal out through "codesend" and it did not work.
At this stage I started to look for Mercury articles on the web, and found an interesting article ( http://npham.dk/?p=34 ), and this led me investigate the signal sent from the hand-held controller to see if I could decode it in detail.
I found that the blocks of signal codes sent by (and received by) the Mercury system are not straightforward, and are not easy to interpret for use by "codesend". For example, my RF Sniffer returned values of 5330227 for socket 1 "on" and 5330236 for socket 1 "off". Sending these via "codesend" did not switch the socket. Furthermore the sockets and hand-held controller had the numbers 1114 printed on them, so I converted the values to binary to see if there was a pattern that could be useful. Here are the table of switch states with their values as binary patterns, followed by the number on the socket and its binary pattern:-
- 1 on 5330227 10 1 0001 0101 0101 0011 0011
- 1 off 5330236 10 1 0001 0101 0101 0011 1100
- 2 on 5330371 10 1 0001 0101 1010 1100 0011
- 2 off 5330380 10 1 0001 0101 0101 1100 1100
- 3 on 5330691 10 1 0001 0101 0111 0000 0011
- 3 off 5330700 10 1 0001 0101 0111 0000 1100
- 4 on 5332227 10 1 0001 0101 1101 0000 0011
- 4 off 5332236 10 1 0001 0101 1101 0000 1100
- 5 on 5338371 10 1 0001 0111 0101 0000 0011
- 5 off 5338380 10 1 0001 0111 0101 0000 1100
- 1114 1 0001 0110 10
The obvious elements here are the least significant quartet of bits (Right Hand Side) which represent "on" and "off".
The pattern of bits shown divided into groups is purely for readability and to see any obvious patterns, and it can be seen that the next innermost quartets represent the switch numbers, and then there is a problem because there seems to be a combination of bits that is not straightforward to interpret as the switch numbers increase.
Anyway, none of this was much use as the "codesend" did not produce any switching, so I decided to search elsewhere for information, and a method to get it all to work.
An approach to Mercury brought no response.
Step 3: Breakthrough!
Further (essential) reading got me to the most useful piece of work that I have found on the web for this project, and I recommend it for anyone wishing to decode RF at 433MHz. You can find it here ( http://www.hoagieshouse.com/RaspberryPi/RCSockets/RCPlug.html ) REF 1.
With this information to hand I connected the 433MHz receiver board to the Pi power rail (3.3v) and the ground to the Pi ground, and constructed a cable rescued from an old microphone headset with a 1Megohm resistor to be used as a scope probe. The probe has the resistor soldered to the core of the microphone cable, and the braided sheath of the cable soldered to a solid copper wire to plug in to the ground rail on a breadboard. The other end of the cable has the 3.5mm jack still attached, and this plugs in the sound board on a PC. With this assembly, it is possible to use Audacity ( http://audacityteam.org/ ) as an oscilloscope. Of course the sound board on the PC has to be able to accept the signal from the cable (not all sound boards are suitable!), and the inline resistor helps to protect the sound board from over-voltage inputs. The sound board on my system is a Realtek, and works well with this setup.
Armed with my makeshift scope, I am now able to see the signal sent from the Mercury controller, and to try to mimic the pattern on the Pi, to be sent from the transmitter.
Step 4: Decoding the Mercury Controller Signals
The picture above shows the Audacity output with annotation. This matches the bit pattern from the RFSniffer in broad outline, but with some significant additional short pulses. The obvious short pulses are at the start (LHS) forming a Start Bit, and at the end (RHS) forming a Stop Bit, but there are additional short pulses in the block. I interpret these as some form of framing as they appear in all the expanded pulse streams.
The task now is to mimic all these pulses in an output array for onward transmission as a signal from the Pi to the socket.
Step 5: Modifying the Code
Using the switch code provided from the link (REF 1 above) I changed the bit patterns to match those recorded from the Mercury hand-held controller, so to switch the Mercury switch number one, I used
// 1 sniffer on 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 0 1 1 0 0 1 1
strcpy(szOn, "0000000000001000111010001110100010001000111010001110100011101000111010001110100 010001110111010001000111011101000000000000000000000");
where you can see that the original RFSniffer bit pattern corresponds to the reproduced detected pattern (in broad outline only!).
The timing of the output of bits from the switch code had to be modified from the original so that the bit spacing (and total message length) corresponded to the pattern transmitted from the Mercury controller.
Testing the new code from the terminal window switched the remote switch - IT WORKED!
The modified mercuryswitch.cpp is available from here .
Step 6: Finally - Calling the Mercury Switch Control Code From Raspberry Pi Python
It is required to call the mercuryswitch code (compiled from mercuryswitch.cpp - use the command in the Pi terminal window)
g++ -o mercuryswitch mercuryswitch.cpp
This is called in the Python script by using a call to "subprocess" thus
subprocess.call( ["sudo","./mercuryswitch","1","on"] )
Note that the function "subprocess" requires a list as its parameter as one of its optional parameter structures, hence the use of the square brackets around the set of parameters. Also, "mercuryswitch" has to be run from superuser and requires "sudo" to achieve this.
A test program written in Python for the Pi is given here .
Don't expect these RF modules to work over long distances. Mine are working at up to four feet, above which they become unreliable, and fail to switch sometimes. I am running them at 3.3 volts straight from the Pi GPIO pins, and have modified the tiny boards by adding an aerial of the appropriate length for the wavelength (17.2cms for 433MHz quarter length), but I haven't yet investigated using a ground plane to support a better signal quality.
Good Luck!
13 Comments
2 years ago
Great stuff. I've delved at little deeper into generating code pairs that work with mercury sockets which I share here if it is of use to anyone. The mercury remotes come pre-configured, and although it comes with a sticker, not sure it really is that relavent.
When controlling from a Pi you can use any valid code that the Mercury is capable of decoding. I have found by trial and error, that the maximum bit length looks to be 24 bits. The on / off codes have specific rules, as already discussed. In python, the following lambda will generate a valid on/off code pair from the seed number given.
gen=lambda n: (int("{0:b}".format(~3 & n | 12),2), int("{0:b}".format(~12 & n | 3),2) ) if 15 < n < 10000000 else (0, 0)
To use:
gen(5330224)
(5330236, 5330227)
1st number is OFF, 2nd number is ON.
If you need these expressed in binary, add another lambda:
tobin=lambda c: (bin(c[0])[2:], bin(c[1])[2:]) if c[0] != 0 and c[1] != 0 else (0, 0)
then you could combine as:
tobin(gen(5330224))
('10100010101010100111100', '10100010101010100110011')
If your seed number is out of range, the function will return (0, 0)
To generate a random pair of codes (there are 624999 combinations!)
import random
MAX=10000000
gen=lambda n: (int("{0:b}".format(~3 & n | 12),2), int("{0:b}".format(~12 & n | 3),2) ) if 15 < n < MAX else (0, 0)
print(gen(int(random.random() * (MAX-1))))
Assuming you generate your own codes with the above, the next challenge is to get the mercury remote to register the code. Set up your transmitter to send the 'on' code in a loop whilst holding down the yellow button on the Mercury socket. After a few seconds, you should hear it click on, then just send the off code to test.
3 years ago
I managed to get this working with codesend with my Mercury switches by doing the following:
./codesend 0 189
where is the code from RFSniffer.
But the third parameter into codesend is critical - that being the 189, to make each pulse 189 microseconds long. It seems very sensitive to getting the timing spot on.
Question 4 years ago
Thank you ever so much for taking the time to produce this article. One question I have for you; If I already have the same Mercury plugs as you and 433MHz receiver and transmitter can I use the code produced by the RFSniffer & your software to reverse engineer what binary I need, or do I absolutely have to make my own home-brew oscilloscope to extract the pulses etc? I'm wondering if the pulses/gaps might stay relatively the same but the codes change and I'm hoping I can determine what I need from the codes produced by RFSniffer. Thanks for your time and help.
Reply 4 years ago
As a further comment I would say that as RFsniffer doesn't seem to find the "framing spacers" then it may be essential to use a scope to decode the bit patterns, especially the bit spacing. It may be that my timings were out by a fraction due to RPi speed and frequencies, and that caused the receiver (Mercury Switch) to fail to receive a correct signal, and this in turn may have caused the switches not to work reliably at distance. Here are some pictures of the full Audacity traces that show the "framing" patterns.
Good luck!
PS By the way, I did not use the Mercury switches as proposed in the article, and finally adopted the use of solid state relays and RPi signals to switch a transistor controlled voltage level intermediate from 3.3v > 12v > 240v mains switching.
The first picture (left) is Mercury Switch 2 ON, and the second (right) is Switch 2 OFF.
Reply 4 years ago
Hi Ian, thanks for your replies, I have actually found a solution I'm really happy with and has been in constant use for the past week. I'm using the Mercury Plugs you mention in your article, the real break-through point for me was finding a version of RFSniffer that someone had modified which not only outputted the code but also the Pulse length! I then used this knowledge with parts from someone elses Github project to be able to specify the Pulse length when sending the codes and it works perfectly every time.
I'm also using a Fork I've made of the "Fauxmo" project: https://github.com/ashleycawley/fauxmo/tree/home which allows my RPi to fake being a Belkin WeMo device that my Amazon Echo's communicate with, I can speak to my Echo and have it perform tasks on the Pi. I modified the Fauxmo code so that it could execute commands or shell scripts for me (as I work best in Bash); the end result is I can be on any floor of my house and ask Alexa to turn different devices or lights on or off. It's like magic. I have had 6 different devices (Lighting indoor & outdoors, monitors, fans and more) all hooked up and in use for a week and it has been flawless, never missed a beat. I'm impressed at the range of the 433MHz single coming off the little transmitter connected directly to my Pi, I live in a three floor brick-building and I can control devices in every room on every floor without issue.
I even have a Home Automation control panel on my phone that allows control of it all (see attached image).
I'm standing on the shoulders of giants really, thanks to great articles like your own and the work of many others on Github, I have combined pieces from around three different Github projects to make my solution work, I'm so impressed with it I might have to package it up as its own project and perhaps write-up a guide so that others can follow it with ease and do not have to put as much time and effort in to cracking the codes and crawling the net to pickup the different pieces required.
I have even stumbled upon Github projects where they have tried to do exactly this (control these Mercury sockets) and have failed and given up. I would love for them to know it is possible.
Thankfully I was able to avoid building my own home-brew oscilloscope, decoding and conversion process!
Thanks again.
@ashleycawley on Twitter.
Reply 4 years ago
Hi Ashley
Great to hear that you found a solution, and have pushed forward the boundaries of knowledge for this project.
Yes, a new Instructable would be good - have a go! What would be most useful would be a detailed description of the codes, expanding on my original, and how the RFSniffer has been modified (with references to the authors of that work) to provide variable bit lengths as this is the key to your successful results.
I am so pleased that you have found a solution!
Regards
Ian
Reply 4 years ago
Hi Ian, I've never done a Instructables guide before but I may look in to it. I will at some point hopefully detail the specifics on where everything came from and provide necessary credits, to pull it altogether, life is just getting in the way at the moment!
Reply 4 years ago
Hi Ashley
Thanks for your comments and request. Yes, it is easier to use the RFsniffer than to try my Audacity 'scope! You may need to look up Manchester encoding to help you decipher the bit patterns. I spent many hours trying with RFsniffer (old version) to get what I needed, and then failed! That is why I developed the Audacity 'scope - it allows me to freeze sequences and enlarge and expand very easily.
Good luck, and let me (and this Instructable) know how you get on.
Regards
Ian
Reply 4 years ago
Another comment regarding the frame spacing and the "short bits" shown in the scope output. It may be possible to just replace the "real" bits in to the frames that I had produced in the code for the RPi. It could be possible that the RFsniffer bit patterns can be selected bit for bit corresponding to my traces, and put in to the array of zeros and ones to fit inside the frames of shorter bits. This would alleviate the need to use Audacity, if it works!
Reply 4 years ago
Here are the bit patterns used in the subroutine to switch 2 ON and OFF.
// 2 sniffer on 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 1 1 0 0 0 0 1 1
strcpy(szOn, "0000000000001000111010001110100010001000111010001110100011101000111010001110111011101000100010001000111011101000000000000000000000");
// 2 off sniffer 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 1 1 0 0 1 1 0 0
strcpy(szOff, "000000000000100011101000111010001000100011101000111010001110100011101000111011101110100010001110111010001000100000000000000000000");
From which it can be seen where the "frame spacers" are, and these don't show in the RFsniffer paterns.
7 years ago
did you try it with livolo brand swithces maby?
Reply 7 years ago
No, I haven't tried the Livolo switches, but the same process should work (eventually!). You may try changing the pulse widths slightly to ensure a good match between the Hand-held controller, and what you are transmitting from your system. Also, it may worth trying a random delay between repeat transmissions to try to avoid any noise patterns from affecting each transmission at the same position in the bit stream.
7 years ago
Great project. I am going to have to try this with my Raspberry PI.