We recently had a new boiler fitted with the Salus RT500RF controller. I’ve been playing around with a lot of home automation recently involving LightwaveRF, RFXCom unit and OpenHAB. Having temperature monitoring in my lounge and bedroom I really wanted the option to be able to control the boiler allowing the OpenHAB control system to decide when to enable the boiler based on factors such as room temperature, time of day and outside temperature.
Opening the RT500 RF transmitter is pretty easy (just a couple of screws inside) and I found a nicely labelled RFM02B module inside.
Initial attempts
I then used a JeeLink USB stick and nRFmon to find out what frequency we were operating on. I was fortunate that the transmission was long enough to be picked up on the frequency scan. It seems that nRFMon is pretty slow over a wide band. I initially thought it looked like OOK. I had hoped that I would be able to use the RFM12B in the JeeLink for reverse engineering the protocol, but this isn’t straight forward. So I got out my Open Logic Bench and decided to attack from the other side and get the baseband signal going into the RFM02.
Using the RFM02 datasheet I probed the FSK pin and discovered it’s running at approximately 2.4kHz. Knowing this I could set the logic analyser to nice long capture window by dropping the sampling rate. Using a sample rate of 100kHz I got the following trace:
This then allowed me to enter the bit timings into a spreadsheet so I could compare different messages.
At this stage I still thought I has OOK on my hands and proceeded to start trying to send the boiler control messages using the OOK method from the Jeelink. This was far from successful.
Changing Vector
I decided I needed a bit more information about the RF protocol and decided to invest in a USB SDR (software defined radio) for about £13 from eBay. This would allow me to see the RF signal properly, decode it and then compare to what I was outputting on the Jeelink so I could debug it.
This was much more successful as I was able to receive the signal and record the RF signals as an IQ wave file for post-processing using SDR#.
Using gnu-radio and the information from a blog post about using gnuradio to decode rfm12 signals I was able to create a decoder for the boiler transmitter. The baseband signal I received and decoded from the transmitter matched nicely with what I had probed from using my logic analyser.
I was also able to ascertain that the deviation from the carrier frequency was ~+130kHz and that the carrier frequency was ~868.2 Mhz.
Decoding messages
Now I had the raw line coded message I was needed to work out the format. Reading the RFM module datasheet I eventually realised that the message was using a standard format of a pre-amble followed by the sync word, the data and then what appears to be a stop byte. With some more data points (recording the signal sent with different address jumper settings) it should become apparent what the format of the data bits is.
However, I put that to one side at this stage as I had what I needed to turn the boiler on and off – which is the primary aim!
Jeelib library
I then created my own version of the jeelib library to allow the control of the salus RT500RF boiler controller via the Jeelink USB stick. The library and example code can be found on github as part of my JeelinkHa (Jeelink Home Automation) project.
The main modifications are the stripping out of the RX stuff and CRC so that I can send my own custom messages. I have also created a nice wrapper for the sending of boiler commands which makes the arduino sketch somewhat simpler.
The plan is to re-implement the RX side of things so that I can receive none RFM packets (such as from the eq-3 MAX! range of radiator valves) and to also extend it so I can transmit other 868Mhz messages (such as to the eq-3 MAX! radiator valves).
OpenHAB integration
OpenHAB is brilliant – it’s amazingly flexible and very powerful for home automation! To integrate the JeelinkHA control into OpenHAB I created a custom binding based on the RFXCom binding that was already present. This JeelinkHA binding can be found on github.
Hi Paul, you mention extending the code to control eq-3 MAX! TRV’s. Have you got any further with this?
Cheers,
Steve
Forgot to enable follow-up comments, doing that with this comment!
Hi Steve,
I’ve changed my domain, but I never did do the TRVs with the Jeelink. I ended up buying a CUL device and implementing a binding for OpenHAB.
It’s a bit of a work in progress, but it’s working as I need it to for now. You can see the full status in the Max!CUL wiki page.
Thanks,
Paul
Great work. Thanks for publishing this. I got this working on my Arduino with very little effort. I had to use the RFID address that you use (11100). Did you find the 4 byte data codes for any other addresses? I would like to try to work out the protocol that Salus use. I suspect that the first byte is the Gateway_ID, followed by a command byte, single byte CRC and a terminator byte. However, I have not had any luck with this, so far. Not sure how the jumper settings equate to the Gateway_ID either.
Regards,
Onosher
Just had a Salus remote thermostat installed and keen to start playing with home automation. Some great starter points here
Pingback: Upgrades... Salus RT500RF 868MHz wireless boiler control - QDH
Hi
I am new to arduino and am struggling to get your library to work, I get the following error.
Arduino: 1.6.4 (Mac OS X), Board: “Arduino Uno”
java.lang.NullPointerException
at processing.app.BaseNoGui.removeDescendants(BaseNoGui.java:944)
at processing.app.BaseNoGui.removeDir(BaseNoGui.java:966)
at processing.app.Base.removeDir(Base.java:2559)
at processing.app.Sketch.saveAs(Sketch.java:706)
at processing.app.Editor.handleSaveAs(Editor.java:2398)
at processing.app.Editor.handleSave(Editor.java:2339)
at processing.app.Editor.handleRun(Editor.java:1996)
at processing.app.EditorToolbar.mousePressed(EditorToolbar.java:324)
at java.awt.Component.processMouseEvent(Component.java:6522)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6290)
at java.awt.Container.processEvent(Container.java:2234)
at java.awt.Component.dispatchEventImpl(Component.java:4881)
at java.awt.Container.dispatchEventImpl(Container.java:2292)
at java.awt.Component.dispatchEvent(Component.java:4703)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4530)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
at java.awt.Container.dispatchEventImpl(Container.java:2278)
at java.awt.Window.dispatchEventImpl(Window.java:2750)
at java.awt.Component.dispatchEvent(Component.java:4703)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:751)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:702)
at java.awt.EventQueue$3.run(EventQueue.java:696)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:724)
at java.awt.EventQueue$4.run(EventQueue.java:722)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:721)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
/Users/Dan/Documents/Arduino/libraries/JeelinkHaLib/JeelinkHaRF12.cpp:319:19: fatal error: RF12.h: No such file or directory
#include
^
compilation terminated.
Error compiling.
Could you give me any pointers on what I might be missing.
Thanks
Not sure. It looks like you’re missing the RF12 library. Try adding it using these instructions – http://jeelabs.net/projects/jeelib/wiki
Hi Dan,
I purchased the Jeelink Classic V3 and whilst trying to upload I hit the same issue as you described. I have imported the jeelink-master library however I am now faced with an error message:
In file included from ~/Arduino/libraries/JeelinkHaLib/JeelinkHa.h:10:0,
from ~/Arduino/libraries/JeelinkHaLib/JeelinkHaSalusRT500.h:7,
from ~/Arduino/JeelinkHaStick/JeelinkHaStick.ino:12:
~/Arduino/libraries/JeelinkHaLib/JeelinkHaRF12.h:19:0: warning: “rf12_data” redefined
#define rf12_data (rf12_buf)
Arduino 1.8.5 (Fedora 25) Board: Atmel atmega328p
How did you resolve your issue?
Thanks
I just noticed that there is a One-Touch-Override (OTO) unit which can set back the temperature by a set number of degrees, when you go out for instance. The OTO unit communicates with the thermostat rather than the boiler controller which will be a challenge for the batteries. There must be a jumper that tells the thermostat to leave its radio in receive to see the OTO commands as and when they are sent.
There appears to be a lot going on radio protocol wise and I hope someone might have got to the bottom of the OTO radio protocol.
Total novice to this: Do we have any novice instruciotns to get this working.
So far I’ve programmed the JeeLink and compiled the OpenHAB binding to 1.7.1
I know I’m missing something as I can’t get around in my head how the two communicate the JeeLink and the Boiler.
I get the following:
10:25:00.284 [INFO ] [c.internal.ModelRepositoryImpl:80 ] – Loading model ‘default.sitemap’
10:25:00.518 [INFO ] [c.internal.ModelRepositoryImpl:80 ] – Loading model ‘rrd4j.persist’
10:25:00.549 [INFO ] [c.internal.ModelRepositoryImpl:80 ] – Loading model ‘exec.persist’
10:25:00.559 [INFO ] [c.internal.ModelRepositoryImpl:80 ] – Loading model ‘db4o.persist’
10:25:00.568 [INFO ] [c.internal.ModelRepositoryImpl:80 ] – Loading model ‘logging.persist’
10:25:00.580 [INFO ] [c.internal.ModelRepositoryImpl:80 ] – Loading model ‘mysql.persist’
10:25:00.620 [INFO ] [c.internal.ModelRepositoryImpl:80 ] – Loading model ‘default.items’
10:25:00.670 [DEBUG] [i.internal.GenericItemProvider:154 ] – Processing binding configs for items from model ‘default.items’
10:25:00.675 [DEBUG] [i.internal.GenericItemProvider:133 ] – Read items from model ‘default.items’
10:25:01.153 [INFO ] [.o.u.w.i.servlet.WebAppServlet:79 ] – Started Classic UI at /openhab.app
10:25:02.285 [DEBUG] [.r.internal.RuleModelActivator:42 ] – Registered ‘rules’ configuration parser
10:25:02.302 [DEBUG] [m.r.internal.engine.RuleEngine:77 ] – Started rule engine
10:25:04.627 [DEBUG] [.j.internal.JeelinkHaActivator:34 ] – JeelinkHa binding has been started.
10:25:04.644 [DEBUG] [j.internal.JeelinkHaConnection:48 ] – Activate
10:25:04.645 [DEBUG] [j.internal.JeelinkHaConnection:74 ] – Configuration updated, config true
10:25:04.646 [INFO ] [j.internal.JeelinkHaConnection:107 ] – Connecting to JeelinkHa [serialPort=’/dev/ttyUSB0′ ].
10:25:04.659 [DEBUG] [.b.j.internal.JeelinkHaBinding:58 ] – Activate
10:25:04.751 [DEBUG] [j.i.c.JeelinkHaSerialConnector:152 ] – Data listener started
and thats it.
Has anyone documented a guide.
Would appricate it.
Hi
Do we have a basic guide for setting up the bindings?
I have compiled to openhab 1.7.1 , when I run I get to Data Listener started. I feel I’m missing something as I don’t understand how the device would communicate with my Salus receiver on the boiler.
I’ve been playing with this too – but doing hardware mod on the thermostat programming unit to IOT enable it. I suspect this unit is a lot more basic than one might think.
On the RFM board there are 4 pins – VCC, vOn, vOff, GND. Briefly connecting VCC to vOn or vOff transmits signal which turns the boiler on of off. (>=250ms connection is stable, 30ms doesn’t work; I guess this briefly powers up the board which transmits as soon as powered). It looks like the thermostat decides when the boiler should be on or off – the only link between this unit and the receiver is the transmission by the RT500RF of an on or off command as needed (The icon on the unit shows the boiler on even if the receiving unit is powered off – I doubt it would do this if there were authentication protocols etc.). The RT500RF doesn’t seem to receive, only transmit. These are the only 4 connections between the RFM02B board and the MCU.
The jumper for the address on the unit connects to the RFM02B board, not the main board like the other jumpers. I’m guessing this jumper adjusts the radio signal somehow rather than the bytes which are transmitted. It seems the receiving unit has to be on the same radio settings to receive a signal. i.e. no authentication protocols etc. (This might be checked by comparing the signals when the jumpers are changed).
As I understand it, the OTA unit (RT300) is paired with and sends to the receiver (boiler controller), not the programmable thermostat itself. The RT300 provides a simple override – i.e. sending an on or off command to the boiler control unit if the temperature is above or below what is set on the OTA unit. I guess this works the same as the temporary override function on the RT500RF thermostat unit. What is the reason for thinking the RT300 transmits to the RT500RF thermostat? (there are certainly no such jumpers inside the RT500RF and, according to Salus tech, no change of settings on existing setup is needed to add an RT300 unit. The only configuration is that jumpers on the new RT300 must be set the same way as on the existing setup).
I’m guessing from price, size and functionality that the RT300 OTA unit uses the same RF board, but no MCU as in the RT500RF, just a basic thermostat and switches to set the override.
Note that there are some more recent units which look like they have authentication and pairing (as one would expect). The compatibility between Salus units can be confusing (at least I’ve found that – as tech support explained, these newer units ending with ‘5’ such as RT305 are not compatible with the RT500RF, unlike the RT300. My old RT500RF is a lot more basic than the Salus ‘5’ series, but probably easier to mod.
BTW, as POC, I’ve soldered 4 wires to these pins in the RT500RF and hooked up to a logic level shifter and an Arduino. The Arduino can then be used to turn the boiler on and off wirelessly. I plan to put in an Arduino Pro Mini (modded to be ULP) and NRF24L01+ inside the RT500RF – there is even room inside for these to have separate batteries. (I decided an ESP8266 is too power hungry). I may also try to control the unit’s Reset, Set, Select and Up and Down Switches with the Arduino (copper tape may be needed). Then I’d be able to reprogram the unit remotely.
I have decoded most of the Salus protocol and as you say it is quite primitive. The RT500ROF is superior in matters other than the OTO controller. I actually upgraded from the RF to the ROF to get the additional features. The ROF does transmit to the boiler controller not the thermostat. The setback and thence boiler on/off is determined by the controller using the thermostat transmissions together with the OTO transmissions.
Pingback: RFM12B Linux Kernel Module development - QDH
Great work, I wonder if you could help me with something on the RX side? My goal is to operate a second relay with a Arduino and RFM02 to power a circulating fan when the heat is on.
Sorry – I’m focussed on other things and don’t have this setup now.
@Andrew Stancliffe
Take a look over at these sites:
http://jeelabs.net/projects/hardware/wiki
Thanks for the reply.
Is there anyone out there who can enlighten me with details of nodes preamble protocol etc. I am looking at cloning the RX side
I have cloned most of the RX of the RT500ROF which is a superset of the RT500RF. It will be easier if you raise a post on http://jeelabs.net/projects/cafe/boards/6
I am JohnO in that forum.
My earlier post:
http://jeelabs.net/boards/6/topics/6604?r=6871#message-6871