Talking to LightwaveRF 433MHz

Posted on
  • Hi!

    LightwaveRF is a popular 433MHz home automation product line in the UK, sold in B&Q, Maplin etc.

    I have an Arduino controlling things using the same kind of 433 transmitter used in the Espruino tutorial, so I thought I'd try to port it to the Espruino at the weekend and had no luck :(

    The code for the Arduino which works is here: https://github.com/lawrie/LightwaveRF/bl­ob/master/LightwaveRF.cpp#L188

    The protocol with some more accurate values for timings is described here:
    https://wiki.somakeit.org.uk/wiki/Lightw­aveRF_RF_Protocol

    I have tried timings and values from the Arduino code and also made it to spec according to the protocol. My code was orginially written to be compact and efficient but when it didn't work I rewrote it to make debugging easier. This is based on the Arduino code:

    var TX = A0;
    
    function sendBits(bitStream) {
      bitStream.forEach(function(bit) {
        digitalPulse(TX,0,0.025); //delay 25us
        digitalPulse(TX,bit,0.28); //pulse 280us
        digitalPulse(TX,0,0.28); //pulse 280us
        if (bit === 0) digitalPulse(TX,0,0.3); //pulse 300us
      });
    }
    
    function sendMsg(command) {
      var repeat = 12;
      var bitStream = [];
      bitStream.push(1); //msg start bit
      for (var b=0;b<10;b++) { //for 10 bytes
        bitStream.push(1); //byte start bit
        for (var i = 7; i >= 0; i--) { //split byte into bits
          bitStream.push( command[b] & (1 << i) ? 1 : 0 );
        }
      }
      bitStream.push(1); //msg end bit
      var interval = setInterval(function() {
        //console.log(bitStream);
        sendBits(bitStream);
        if (repeat-- == 1) clearInterval(interval);
      }, 10.25); // delay 10250us between repeats
    }
    
    var on = [0xf6,0xf6,0xf6,0xee,0x6f,0xeb,0xbe,0xed­,0xb7,0x7b];
    var off =[0xf6,0xf6,0xf6,0xf6,0x6f,0xeb,0xbe,0xe­d,0xb7,0x7b];
    var socketOn = false;
    setWatch(function() {
      socketOn = !socketOn;
      digitalWrite(LED2,socketOn);
      sendMsg(socketOn ? on : off);
    }, BTN1, { repeat:true, edge:"rising", debounce:10 });
    

    I am pretty certain the bitStream is correct, I checked it manually. So I guess the issue is sending the bits. The LightwaveRF protocol is a bit weird, and I wrote the code several different ways. I think it is because I'm not understanding how digitalPulse is working compared to the delays in the Arduino code. For example, I set the TX to an LED and when it finished "sending" the LED remained on when surely it should be low.

    Any help would be really appreciated, we could do a lot with Espruino and LightwaveRF!

  • The espruino code I posted here might help with sending the bit stream. http://forum.espruino.com/conversations/­255528/newest/

  • Thanks for the link, I had read that, and it made sense, but the LightwaveRF has a strange arrangement of using low output for signalling as well as high, and I think that is where I've not quite got it.

    Think I will stick a logic analyser on it in the mean time, see exactly what it is doing.

  • Hi - this looks really cool, I didn't realise that there were a bunch of cheapish devices out there that shared the same protocol. Have you seen http://www.espruino.com/Remote+Control+S­ockets ? That's not lightwaveRF, but it does use 433Mhz.

    One thing that might help is that digitalPulse tries to output an actual pulse, so digitalPulse(A0,1,2) raises A0 for 2ms and then lowers it as well. And the pulses will go on in the background, so just because you finished the last pulse doesn't mean that it won't still be doing things (so the delay on setInterval might end up being too small).

    Looking at that page on the protocol it seems like it's easier to actually think of the 'payload' a bit differently... For instance they're saying you get 11110110 and output a 1250us pause when you get 10. It'd be much easier to treat that as 111010 and to then output a 1250us pause when you just get 0 instead.

    Have you been able to look at the output on an oscilloscope? I guess with 250uS pulses you're going to have to make sure that the code executes quickly, as if it doesn't you could well end up with pauses that'll cause you problems... Sadly Espruino isn't super quick at the moment.

    I haven't tested this, but what about:

    // 1 added to front, and 10 converted to 0 already
    var encode = [
    "1111010",
    "1110110",
    "1110101",
    "1110011",
    "1101110",
    "1101101",
    "1101011",
    "1011110",
    "1011101",
    "1011011",
    "1010111",
    "0111110",
    "0111101",
    "0111011",
    "0110111",
    "0101111"];
    
    function sendNibbles(nibbles) {
      var repeat = 12;
      var cmdStream = "var p=digitalPulse;";
      cmdStream+="p(TX,1,0.25);p(TX,0,0.25);";­ //msg start bit
      nibbles.forEach(function(nibble) {
        encode[nibbble].split("").forEach(functi­on(bit) {
            cmdStream+="p(TX,1,0.25);p(TX,0,"+(bit?"­0.25":"1.25")+");";
        });
      });
      cmdStream+="p(TX,1,0.25);p(TX,0,0.25);";­ //msg end bit
      cmdStream+="p(TX,1,0);"; // wait for it to finish - hopefully set to 0 after as well
    
      var interval = setInterval(function() {
        eval(cmdStream);
        if (repeat-- == 1) clearInterval(interval);
      }, 10.25); // delay 10250us between repeats
    }
    

    The code above will create a string of JavaScript code that'll then be executed very quickly. It's probably overkill, but hopefully it'll mean that you don't have any speed problems when sending the pulses

  • Thank you, that is an amazing response and a really interesting way to look at it. I tried the code but so far it hasn't worked, it seems to remain high afterwards too. It has given me a better understanding of other approaches though.

    I haven't hooked it up yet to see the output but will do so after work tonight.

  • Just looking into this a bit more - looks like there's quite a bit of info out there. I'm quite tempted to take a trip to Maplin right now :)

    Please let me know how you get on - I'd love to get a working module that handles both RX and TX... There's a huge amount that you could do with this.

    edit:

    oops about the finishing pulse. Try replacing:

    cmdStream+="p(TX,1,0.25);p(TX,0,0.25);";­ //msg end bit
    cmdStream+="p(TX,1,0);"; 
    

    with

    cmdStream+="p(TX,1,0.25);"; //msg end bit
    cmdStream+="p(TX,1,0);"; // wait
    
  • Yeah it is pretty good, I've got many lightswitches and sockets replaced with the Lightwave stuff now, and the info is out there to control it all, dim them, set moods etc.

    I'll keep playing with it. Comparing the output side by side with a working module would be the next easiest step.

  • It looks like really good quality equipment - I'd be tempted to try controlling one of these radiator valves as part of my current heating controller project:
    http://www.maplin.co.uk/p/lightwaverf-ra­diator-valve-n81qj

    There is so much out there I sometimes wonder why I bother trying to develop my own controllers but I suppose that isn't the point!!

  • @DaveNI yes, I was looking at those. I've been after ETRVs for a while - but like all this stuff, when you look at doing them all over your house the price starts to look quite scary!

    I have no idea about LightwaveRF, but the PC/phone/etc software often seems rubbish on this kind of thing. Being able to control the ETRVs exactly how you want them would be fantastic.

    edit: Just looked at this more closely. Looks like LightwaveRF may be more of a brand name. Seems like the TRV uses 868Mhz, and the electrical stuff uses 433Mhz. It doesn't mean you can't do both, it's just a bit of a pain.

  • Yes, although quite cheap, it adds up really fast. I just have it in a few areas I wanted to control.

    The software is pretty bad as usual, and of course you need to buy an extra expensive box with ethernet connection which does the RF and can be controlled over the network or via their website (it connects out).

  • I got the logic sniffer on the case and sure enough you can see my signal is not right compared to the working one. I have something to work with now :)


    2 Attachments

    • txgood.png
    • txbad.png
  • Great, thanks - that's a huge help.

    Which one of those waveforms is the one that comes from Espruino, and was that with the code that I'd posted?

  • Sorry I thought it would label them. The top one is the output from the Arduino which works, bottom one is my Espruino code.

    I've attached wave form of the code you posted below.


    1 Attachment

    • txgordon.png
  • Ahh, thanks. So I guess that's what I thought - your original code was probably fine, but was running a bit too slowly to put the pulses end to end.

    And I was trying to be too clever in my code. Due to the awesomeness of JavaScript, it looks like the ternary operator doesn't treat the string "0" as false. Also I think the pausing between isn't working right because setInterval is always calling back every 10ms, rather than 10ms + the length of the signal.

    Try:

    // 1 added to front, and 10 converted to 0 already
    var encode = [
    "1111010",
    "1110110",
    "1110101",
    "1110011",
    "1101110",
    "1101101",
    "1101011",
    "1011110",
    "1011101",
    "1011011",
    "1010111",
    "0111110",
    "0111101",
    "0111011",
    "0110111",
    "0101111"];
    function sendNibbles(nibbles) {
      var repeat = 12;
      var cmdStream = "var p=digitalPulse;";
      var sigLength = 0.5;
      cmdStream+="p(TX,1,0.25);p(TX,0,0.25);";­ //msg start bit
      nibbles.forEach(function(nibble) {
        encode[nibbble].split("").forEach(functi­on(bit) {
            var length = (bit=="1")?0.25:1.25;
            cmdStream+="p(TX,1,0.25);p(TX,0,"+length­+");";
            sigLength += 0.25+length;
        });
      });
      cmdStream+="p(TX,1,0.25);"; //msg end bit
      sigLength += 0.25;
      var interval = setInterval(function() {
        eval(cmdStream);
        if (repeat-- == 1) clearInterval(interval);
      }, sigLength+10.25); // delay 10250us between repeats
    }
    
  • I'm so impressed you are programming this blind. That looks a lot more respectable in the output, just not quite the right pattern.

    When I was going over the code, I did notice that perhaps the "byte start bit" was missing. It looks like this is going to work though, just need to find the bug.


    1 Attachment

    • txgordon.png
  • It works! Thanks @Gordon, your code worked great. I realised you'd already added the byte start bit as per your comment once I traced through the code. The sigLength was of course needed, I'd forgotten to adjust that too.

    So for anyone else wanting to try this, hook up the 433 to Espruino as per the tutorial on the Espruino website. Use this piece of test code:

    var TX = A0;
    var encode = [
    "1111010",
    "1110110",
    "1110101",
    "1110011",
    "1101110",
    "1101101",
    "1101011",
    "1011110",
    "1011101",
    "1011011",
    "1010111",
    "0111110",
    "0111101",
    "0111011",
    "0110111",
    "0101111"];
    
    function sendNibbles(nibbles) {
      var repeat = 12;
      var cmdStream = "var p=digitalPulse;";
      var sigLength = 0.5;
      cmdStream+="p(TX,1,0.25);p(TX,0,0.25);";­ //msg start bit
      nibbles.forEach(function(nibble) {
        encode[nibble].split("").forEach(functio­n(bit) {
            var length = (bit=="1")?0.25:1.25;
            cmdStream+="p(TX,1,0.25);p(TX,0,"+length­+");";
            sigLength += 0.25+length;
        });
      });
      cmdStream+="p(TX,1,0.25);"; //msg end bit
      sigLength += 0.25;
      var interval = setInterval(function() {
        eval(cmdStream);
        if (repeat-- == 1) clearInterval(interval);
      }, sigLength+10.25); // delay 10250us between repeats
    }
    
    var on = [0x0,0x0,0xf,0x1,0xf,0xe,0xf,0xe,0xf,0xe­];
    var off =[0x0,0x0,0xf,0x0,0xf,0xe,0xf,0xe,0xf,0x­e];
    
    var socketOn = false;
    setWatch(function() {
      socketOn = !socketOn;
      digitalWrite(LED2,socketOn);
      sendNibbles(socketOn ? on : off);
    }, BTN1, { repeat:true, edge:"rising", debounce:10 });
    

    Put the LightwaveRF device into learning mode (usually hold down 1 or 2 buttons until it starts flashing).

    Send the "On" message by pressing the button on the Espruino. Device should blink rapidly to indicate code accepted. Press the Espruino button again to turn the device off.

    You now have control of the LightwaveRF device with the Espruino.

    Now it is time to start working on a more complete implementation...

  • Wow, great news! Thanks for trying it out so quickly...

    As mentioned in some other places these are branded LightwaveRF, JSJS Designs and Siemens.

    Looks like white Siemens dimmer switches (and other stuff) are sold by screwfix:

    £15 for the 2 gang dimmer switch seems amazing (about the same price as a normal dimmer), but looks like they're out of stock everywhere so I wonder if they've stopped stocking it :(

    edit: It does surprise me that in this age of encryption, this is just asking for abuse. It'd be dead easy to write a receiver that pulled out the device IDs, then you could record them all over the course of a few days and could then keep turning every socket and light in every lightwaverf-equipped house within radio range off.

  • Oh, I just looked around and the prices have really gone up at most places. I was paying £15 at most for a dimmer. I have the 2 gang dimmer in one room. The ones I have are Siemens branded, like these:

    http://www.diy.com/departments/siemens-w­hite-switched-single-push-button-remote-­control-socket/212641_BQ.prd

    And a multipack of adaptors + remote for £18 which is reasonable:

    http://www.diy.com/departments/siemens-w­hite-unswitched-remote-control-socket-pa­ck-of-3/178820_BQ.prd

    Yes there are a couple of problems with it, security and that it is only one-way so you can't query the state of a device or get a reply that your command was successful.

    Lack of security is ok for me, the most they can do is prank me by turning my lights on/off and it is unlikely someone would bother where I live.

    The one-way issue is annoying, I have some house lights on a timer when I'm away to make the house look like it is occupied, but the system doesn't know if it is working. Though reliability has been close to 100% in my experience. You pay a lot more for 2-way home automation sadly.

  • Hmm, I wonder if they were cheaper because of some introductory thing.

    Do the sockets remember their pairing after they've been unplugged? I had a cheap (non-lightwave) set that I bought and I find that pretty frustrating - if there's a power cut I'd have to go around re-pairing everything.

    I guess the 2 way isn't such dealbreaker for me. It's probably done because getting a long battery life on the receiver is tricky, but you'd have hoped that for the sake of a few pence extra they would have added transmit to the sockets... They could even tell you how much power they were using :)

  • I got them quite some time ago when they were a fairly new entry on the market, so it could well have been priced to gain market share. I'm not in a rush to expand it so I'll have to keep an eye on prices and try to pick up a deal.

    They do remember their pairing, and you can pair several remotes to a single device. They also have a lock function so you can send a message to lock the socket in an on/off state so it can't manually be switched. There is an "all on/off" message useful for turning off all the lights at night, or on in the event of a fire. Many nice features really.

  • btw, I can't help thinking that your interpretation of the protocol is how it should have been, it makes so much more sense. The encode array you created by adding a bit and turning 10 into 0 now has an obvious pattern running through it. And simply having a longer pause on a 0 is much easier. Of all the people that reversed LightwaveRF and re-implemented the protocol and you are the only one that saw it... :)

  • Thanks! It's odd - I think if you're staring at a screen full of pulses it's hard to work out much of a pattern (I wouldn't have liked to have done it!). It's much easier to step back when someone else has done 99% of the hard work :)

    you can send a message to lock the socket in an on/off state so it can't manually be switched

    That could be good with children - although it'd be a nightmare if someone did try and prank you :)

  • Refactored this a bit and it works nicely. Now going to move my efforts to GitHub:

    https://github.com/thomascannon/espruino­-lightwaverf/

    A humble start, but something to build from. Given it is using JS it would be nice to make it object oriented so we could do:

    rooms.list;
    =["Study","Bedroom"]
    rooms("Study").allOff;
    rooms("Bedroom").on("Lamp");
    

    You get the idea anyway.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Talking to LightwaveRF 433MHz

Posted by Avatar for thomc @thomc

Actions