Reverse engineer RF modules

 

In this project I reversed engineered the Elro AB440 remote controller. This remote is used to enable or disable power sockets. Why is this fun? Because then I can use my Raspberry to control the power sockets in my house. With the internet browser on my phone I can make a connection to the webpage on my Raspberry and switch power sockets via an RF module. The module I used to connect to the Raspberry is this one and costs around 4 euro.

The Elro AB440

 

How to start

The basics are the same for reverse enginering all amplitude modulated devices. This includes many remote control equipment like car garage openers, dimmers, remote control fans, and these Elro remote switches off course. The first part in this project is finding the way the remote sends data (the reverse enginering part) and the last part is writing a program that will do the same thing as the remote.

Reverse enginering the RF remote

The remote

 

First thing I did is opening the remote and check for part numbers. I found the IC PT2262 and after googeling I found a datasheet here .

PT2262 pinout

 

 

According to the datasheet this IC is an encoder and pin 17 is an output. As a test I set the remote system code (the dip switch) to 10000 (set the dip switch 1 to on and 2 till 5 is set off). I used my cheap old scope and measured on this dout pin while pressing the “A on” button on the remote. I had to change the settings of my scope to 10ms/div and 2V/div to see a complete frame.

Scope output while pressing A on the elro remote.

 

This frame gets send a few times. According to the datasheet the output sends a tri-state data form. Basically that means I should recognize 3 different symbols in the above burst that represents 3 different bits. After many tests I figured out the correct bits/symbols should be:

Symbols/bits

 

 

 

Decoding the RF signal

Now I know the data signals that represent the bits. That means I can write down the above frame and I will get “abccccbccccbc”. This frame get send a couple of times and then the remote power socket gets activated. The next part is to figure out what this pattern means in relation with the system code and the button pressed. I tried many tests with the scope and pressing buttons. I wrote down the patterns and got the following:

Frames

I noticed the following:

  1. Every burst starts with an ‘a’ and then this bit is never used anymore. Getting me left with only bit b and c. So I will call ‘a’ the start bit from now on.
  2. The dip switch corrosponds with the next 5 signals. When a switch from the dipswitch is enabled then the signal changes accordingly. That means that these 5 bits corrosponds to the system code.
  3. The next 5 bits corrosponds to the buttons pressed, but the weird thing is that it was not always the case. After trying some same tests again I did get the expected signals so sometimes I received the wrong signal because the remote is from a bad quality and my scope too or something else…The code works as good as the remote so it should be oke.
  4. The last 2 bits are always the same. When I press the “on” button the bits are “bc“ and when I press the “off” buttons then the last 2 bits are always “cb”.

Coding the RF hack

First I wrote separate functions for the different bits. To get this done i need to know the exact time the signal goes on and off. To get this I zoomed in on my scope and received the following.

Frame zoom-in

 

It’s a bit difficult to see, but one division is 1 ms. I was able to export the data to a csv file and get a better estimate of the short and long signals. The short signal is about 300 micro seconds and the long signal about 920 micro seconds.

rf.c

/*
 * Copyright (c) 2009-2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of Oracle nor the names of its contributors
 *   may be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <stdio.h>
#include <stdlib.h> // for itoa
#include <wiringPi.h>
#include <string.h>

#define PINNUMBER 1
#define SHORTTIME 300
#define LONGTIME 920
#define ON 1
#define OFF 0
#define TIMESSEND 8 // code frame pg 8

int sendByteA();
int sendByteB();
int sendByteC();

int main(int argc, char**argv) {
    // argv[1] == system code
    // argv[2] == device code
    // argv[3] == on/off
    
    char param2Temp[10];
    char onTextArray[] = "on";
    int systemCodeInt;
    int deviceInt;
    int onOrOffParam;
    int tempByte2Send;
    int savedSystemCode;
    int savedDeviceCode;
    int savedOnOrOff;

    if (wiringPiSetup () == -1)
        return 1 ;

    pinMode (PINNUMBER, OUTPUT) ; // pin 6 aan de overkant zie wiringPi pinout!
      
    if( argc != 4 ) {
      printf("We need 2 arguments\n");
      return 0;
    }
    if( argc == 4 ) {
        if (strlen(argv[1]) > 3){
            printf("First parameter got to many chars\n");
            return 0;
        }
        else{
            strcpy(param2Temp, argv[1]);
            systemCodeInt = atoi(param2Temp);
            if (systemCodeInt == 0){
                printf("The systemcode parameter is not an integer\n");
                return 0;
            }
        }

        if (strlen(argv[2]) > 3){
            printf("Second parameter got to many chars\n");
            return 0;
        }
        else{
            char secParamChar = argv[2][0];
            deviceInt = secParamChar - 64;
           
            //printf("DeviceInt is: %d\n", deviceInt);
            if ((deviceInt < 0) || (deviceInt > 5)){
                printf("The device parameter is not A,B,C,D or E\n");
            }
        }
        
        if(strcmp(argv[3], onTextArray) == 0){
            onOrOffParam = 2;
        }
        else{
            onOrOffParam = 1;
        }

    }
    
    //Arguments are checked and converted to integers, now loop the data at least 5 times 
    printf("systemcode: %d, devicecode: %d, on/off: %d", systemCodeInt, deviceInt, onOrOffParam);
    
    for (int i = 0; i < TIMESSEND; i++){
        
        savedSystemCode = systemCodeInt;
        savedDeviceCode = deviceInt;
        savedOnOrOff = onOrOffParam;
        
        sendByteA();
        
        for(int j = 0; j<5; j++){ // loop 5 times to get the system code send
            tempByte2Send = systemCodeInt & 16;
            if (tempByte2Send == 16){
                sendByteB();
            }
            else{
                sendByteC();
            }
            systemCodeInt = systemCodeInt << 1;
        }
        
        for(int j = 1; j<6; j++){ // loop 5 times to get the device code send
            //tempByte2Send = 0;
            if (deviceInt  == j ){
                sendByteB();
            }
            else{
                sendByteC();
            }
            
        }
        
        for(int j = 0; j<2; j++){ // loop 2 times to send on/off
            tempByte2Send = onOrOffParam & 2;
            if (tempByte2Send == 2){
                sendByteB();
            }
            else{
                sendByteC();
            }
            onOrOffParam = onOrOffParam << 1;
        }
        
        
        delay(10);
        
        systemCodeInt = savedSystemCode;
        deviceInt = savedDeviceCode;
        onOrOffParam = savedOnOrOff;
        
    }
    
    return 0;
}

int sendByteA(){
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
}

int sendByteB(){
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
}

int sendByteC(){
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(LONGTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
}

The code starts with checking the parameters. The correct way to send a signal with this application is using the parameters as following.

./rf systemCode deviceCode on/off

For example to set system code 10000b (thats 16 in decimal, use the windows calculator) and have the ‘A’ “on” button pressed you have to use the following command on the Raspberry:

./rf 16 A on

On my Raspberry that will be:

pi@hdh_box:~/NetBeansProjects/rf/dist/Release/GNU-Linux $ ./rf 16 A on

After the code checked the parameters it will send all the bits according the parameters. Download the source code and executable here.

The hardware

The RF434 module connected to the Raspberry

 

My end result

 

For testing the 5V of the Raspberry can be used for the module, but if you need this to work on a reasonable distance then use almost 12V as power supply for the RF module only. Also connect an antenna to pin 4 of the module to extend the distance even more.

I used the same page I created on my Raspberry as in my last blog. I only changed the buttonaction.php file to send the RF signals instead of toggling gpio’s. Copy all the files to the webserver directory and open test.php on a web browser.

Download the files Buttonaction.php and Test.php here.

So far controlling RF modules with the Raspberry. In the next job the Infrared Remote will be reversed engineered. Thanks for reading!

Raspberry pi remote control

In this project I created a web page on the Raspberry pi that can be used to control power sockets or anything else that requires to be switched. The advantage of the Raspberry is the speed and the bigger memory compared to an Arduino with a ESP8266 WiFi. The disadvantage is that the Raspberry is not realtime so it’s difficult to create time critical programs, but thats not needed for this project anyway.

How does it work?

First copy all the files to your Raspberry webserver. In my case that’s the directroy /avr/www/html.

Then go to the page http://yourRaspberryIp/test.php and a page with 4 buttons appears on the screen.

Start page test.php

 

Click on a button and a led corrosponding to a particular GPIO get activated. Pressing the button again will turn off the led.

The hardware

The hardware is any Raspberry , a couple of led’s and some resistors to keep the current low.

Raspberry connection details

 

I soldered a few led’s on a pcb, but a breadboard can be used too.

The Raspberry with the leds

 

The software

There is one file called gpio and that one is the compiled ‘c’ file with the code to enable or disable a led/gpio. To enable or disable a port you have to give 2 parameters. The firs parameter is the text “on” to enable a pin or anything else to turn off a pin. The second number contains the pin number to enable or disable.

I used the wiringPi.h library for the gpio application and that means the pin’s in the library corrosponding to the physical pin’s are a as below.

WiringPi pinout

 
gpio

#include <iostream>
#include <stdio.h>
#include <stdlib.h> // for itoa
#include <wiringPi.h>
#include <string.h>

int main(int argc, char**argv) {
    // Prints welcome message...
    
    char param2Temp[10];
    char onTextArray[] = "on";
    char offTextArray[] = "off";
    int param2Int;
    
    if (wiringPiSetup () == -1)
    return 1 ;
 
    pinMode (7, OUTPUT) ; // pin 4
    pinMode (0, OUTPUT) ; // pin 6
    pinMode (3, OUTPUT) ; // pin 8
    pinMode (12, OUTPUT) ; // pin 10
    
    
    if( argc != 3 ) {
      printf("We need 2 arguments\n");
      
      return 0;
   }
    else if (argc == 3){ // is 2 params
      if (strlen(argv[1]) > 3){
          printf("First parameter got to many chars\n");
          return 0;
      }
      else{
          strcpy(param2Temp, argv[1]);
          param2Int = atoi(param2Temp);
      }
      
      if (param2Int < 1 || param2Int > 4 ){
          printf("Pin number out of range, only 1-4\n");
          return 0;
      }
      
      if(strcmp(argv[2], onTextArray) == 0){
          
          switch(param2Int){
              
              case 1:
                  digitalWrite (7, 1) ;
                  break;
              case 2:
                  digitalWrite (0, 1) ;
                  break;
              case 3:
                  digitalWrite (3, 1) ;
                  break;
              case 4:
                  digitalWrite (12, 1) ;
                  break;
              default:
                  break;
              
          }
          
      }
      else if (strcmp(argv[2], offTextArray) == 0){
          switch(param2Int){
              
              case 1:
                  digitalWrite (7, 0) ;
                  break;
              case 2:
                  digitalWrite (0, 0) ;
                  break;
              case 3:
                  digitalWrite (3, 0) ;
                  break;
              case 4:
                  digitalWrite (12, 0) ;
                  break;
              default:
                  break;
              
          }
      }
      //delay(2000);
      return 1;
  }
   

    return 0;
}

 

The file buttonaction.php is the server side scipt that will execute the gpio file on the server/Raspberry with a parameter to decide which pin to enable or disable.

buttonaction.php

<?php

$buttonName = $_GET["name"];

if($buttonName == "button1on"){
	exec('./gpio 1 on');
}
else if ($buttonName == "button1off"){
	exec('./gpio 1 off');
}
else if ($buttonName == "button2on"){
	exec('./gpio 2 on');
}
else if ($buttonName == "button2off"){
	exec('./gpio 2 off');
}
else if ($buttonName == "button3on"){
	exec('./gpio 3 on');
}
else if ($buttonName == "button3off"){
	exec('./gpio 3 off');
}
else if ($buttonName == "button4on"){
	exec('./gpio 4 on');
}
else if ($buttonName == "button4off"){
	exec('./gpio 4 off');
}


?>

 

The test.php file is the starting file you have to call within a browser. It contains a few links to the button images and executes the buttonaction.php script when clicking on the button.
test.php

<?php

?>

    
<article>
 

<h4>Rapberry GPIO:</h4>
<table>
  <tr>
    <td>Lamp livingroom</td>
    <td><img alt="" src="images/button1_on.png" id="imgClickAndChange" onclick="btn1Clicked()"  /></td>
  </tr>
<tr>
    <td>Camera garage</td>
    <td><img alt="" src="images/button2_on.png" id="img2ClickAndChange" onclick="btn2Clicked()"  /></td>
  </tr>
<tr>
    <td>Fan</td>
    <td><img alt="" src="images/button3_on.png" id="img3ClickAndChange" onclick="btn3Clicked()"  /></td>
  </tr>
<tr>
    <td>Television</td>
    <td><img alt="" src="images/button4_on.png" id="img4ClickAndChange" onclick="btn4Clicked()"  /></td>
  </tr>
</table>
    



<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
    <script type="text/javascript">

    var currentImage1 = 1;
    var currentImage2 = 1;
    var currentImage3 = 1;
     var currentImage4 = 1;

 function btn1Clicked() {   
    if (currentImage1 == 1){
	document.getElementById("imgClickAndChange").src = "images/button1_off.png";
	currentImage1 = 0;
	$.get("buttonaction.php", { name:"button1off" });

    }
    else if (currentImage1 == 0){
	document.getElementById("imgClickAndChange").src = "images/button1_on.png";
	currentImage1 = 1;
	$.get("buttonaction.php", { name:"button1on" });
    }

    return false;
 }

    function btn2Clicked() {   
    if (currentImage2 == 1){
	document.getElementById("img2ClickAndChange").src = "images/button2_off.png";
	currentImage2 = 0;
	$.get("buttonaction.php", { name:"button2off" });
    }
    else if (currentImage2 == 0){
	document.getElementById("img2ClickAndChange").src = "images/button2_on.png";
	currentImage2 = 1;
	$.get("buttonaction.php", { name:"button2on" });
    }
    return false;
    }

    function btn3Clicked() {   
    if (currentImage3 == 1){
	document.getElementById("img3ClickAndChange").src = "images/button3_off.png";
	currentImage3 = 0;
	$.get("buttonaction.php", { name:"button3off" });    }
    else if (currentImage3 == 0){
	document.getElementById("img3ClickAndChange").src = "images/button3_on.png";
	currentImage3 = 1;
	$.get("buttonaction.php", { name:"button3on" });
    }
    return false;
    }

     function btn4Clicked() {   
    if (currentImage4 == 1){
	document.getElementById("img4ClickAndChange").src = "images/button4_off.png";
	currentImage4 = 0;
	$.get("buttonaction.php", { name:"button4off" });
    }
    else if (currentImage4 == 0){
	document.getElementById("img4ClickAndChange").src = "images/button4_on.png";
	currentImage4 = 1;
	$.get("buttonaction.php", { name:"button4on" });
    }
    return false;
     }
    
</script>




</article>




 

Instead of led’s it’s also possible to use a relay-board and enable/disable power sockets. Another way is using the well know RF transmitter and receiving units. In the next project I will reverse engineer the RF remote and create an application for the Raspberry to act like an RF remote. Thanks for reading!