Cloning the remote control of an RC-switch using TI CC1101

The CC1101 wireless RF transceiver from TI is a low-cost (~$4) wireless module that you can connect to your MCU (i.e. Arduino) and transmit/receive data at sub-1GHz frequencies (315/433/868/915 MHz) with support for various modulation formats (ASK, FSK, etc.). In this post I will use such a module to clone a common remote controlled outlet/switch, which operates at 433.95MHz.

Since the CC1101 is quite popular, you can obviously find tons of information on programming and using it. Although, none of the articles I came across describe the full procedure to initially identify the signal and then clone it using this module. That’s the main reason I decided to write about it.

Signal identification

First things first, identify the signal we want to clone. There are basically three ways to find out the signal specifications of our device.

  • Check the model and/or the FCC ID (mostly available in the US) and get the information from the internet
  • Tear down the remote control and try to figure out how it works
  • Use SDR to find and identify our signal

Here we are going to try all three ways, mainly because the remote controlled switch I own is a no-name Chinese one with no manual or labels.

After popping up the case, on one side we can see an HS2260A encoder with some soldered inputs next to it (address and data lines) and on the other side a round crystal oscillator with “R433A” engraved on it.

We most definitely know now that the frequency is around 433MHz, so we can just jump right onto our SDR and move on from there.

Use SDR to identify the signal

I am going to use a cheap RTL-SDR dongle for this, along with SDR#, but you are free to use any hardware (HackRF, BladeRF, etc.) with any software (e.g. HDSDR, GQRX). Just make sure you can record the transmitted samples of your remote.

By pressing the remote control buttons a few times while observing the spectrum around 433MHz, I quickly find out that the exact frequency this remote transmits is 433.946MHz. Also, judging from the waterfall this probably seems like an ASK/OOK modulation. Setting this as my center frequency, I hit the record button in SDR# and then click the remote control buttons a few more times.

The remote control signal in SDR#

Let’s load the recorded file in Audacity and see what we can get from it.

The recorded signals in Audacity

We can clearly see the transmitted pulses of the remote control. The top waveform is the signal for the ON button, while the bottom is the OFF one. They only differ in the last 5 pulses. Assuming that the short pulse equals with a ‘0’ symbol and the long pulse with an ‘1’, we can come up with the following sequences:

  • ON:
    0000010101010101001100110
  • OFF:
    0000010101010101001111000

In order to recreate those codes, we would need to convert these pulses to a real bit-stream. We can see that the high section of the ‘1’ symbol is almost three times the length of the high section of the ‘0’ symbol. Additionally, the low section of the ‘1’ symbol (after the long high) is almost equal with the high section of the ‘0’ symbol. If we suppose that we can represent each symbol with four digits/bits, we would essentially have 1000 for the ‘0’ and 1110 for the ‘1’. So, the above sequences become like that:

  • ON: 1000100010001000100011101000111010001110100011101000111010001110100010001110111010001000111011101000 (or 0x88888E8E8E8E8E8E88EE88EE8)
  • OFF: 1000100010001000100011101000111010001110100011101000111010001110100010001110111011101110100010001000 (or 0x88888E8E8E8E8E8E88EEEE888)

Up until now, we have the correct frequency, the correct bit-stream and we still need the baud rate and the actual duration of each digit, in order to fully recreate the transmission.

Staying in Audacity, we can measure the length of a single digit (e.g. the high section of the ‘0’ symbol) by selecting the corresponding section within the waveform, which is approximately 448 samples.

Length of a single digit

The baud rate (the rate at which a single digit is transmitted) can be calculated using the following formula:

Baud rate = 1 / (<samples_per_digit> / <recording_frequency>) = 1 / (448 / 2400000) = 5357

Likewise, the duration of a single digit is 448/2400000 = ~187usec. Thus, the duration of the entire code (or burst) is 187usec * 100 (number of digits) = 18700usec = 18.7msec, while the gap between these bursts is 11800 samples = 4916us ~= 5msec.

At this point we have completely analyzed the transmitted signals and we can move on to cloning them.

Cloning the signal using an Arduino (and a CC1101 module)

We are going to use an Arduino Nano v3.0 (~$3) with our CC1101 module to clone the signals, plus a couple of resistors, two buttons and an LED. The entire schematic is shown below.

Schematic of the CC1101 connected to an Arduino Nano

Connections

Arduino Nano v3.0 CC1101 module
GNDGND
3V3VCC
D2GD0
D10CSN
D11SI
D12SO
D13SCK

Additionally, I connected an LED on pin D7 of the Nano and two push buttons, one for the ON signal and one for the OFF signal, on pins A5 and A4 accordingly.

Here’s how my prototype looks like:

Prototype of the cloned remote control

CC1101 Configuration

The CC1101 contains some registers that we need to configure programmatically, based on our findings above, in order to correctly transmit our signals. We are going to use a tool, that TI has developed, which can help us get the proper values of those registers, right through its GUI. If you require further information about these registers and the CC1101 itself, you should check the very detailed datasheet of CC1101, as well as the various application notes from TI.

After launching the SmartRF Studio by TI, under the CC1101 icon we select the “Open RF Device in Offline Mode” option.

In the new window, ensuring that we are in “Expert Mode” to enable all options, we set the following RF Parameters:

  • Base frequency: 433.946MHz
  • Xtal frequency: 26MHz
  • Modulation format: ASK/OOK
  • Channel number: 0
  • Data rate: 5.357 kBaud
  • TX power: 10 dBm

and under the “Packet TX” tab, we set the Packet count to “Infinite”, with no seq. number and when we click the “Advanced” checkbox below, we also choose “No preamble/sync” and “Fixed packet length mode” with length of 13 bytes (number of bytes after we converted the code bit-stream to HEX).

Note that some of the above values will be automatically rounded, by the SmartRF software itself, to the closest acceptable ones. For example, we set our data rate to 5.357 kBaud and it gets automatically rounded to 5.35583 kBaud.

SmartRF Studio CC1101 RF Parameters and TX options

On the right side of the Device Control Panel, there is the “Register View” for the CC1101. There we click on the “Register Export” to get the list of the registers, in a nice formatted output, ready to be used in our program later on. In the “Register Export” window, I selected the “PA Table” checkbox and set the “Registers” input text box to the following:

cc1101.writeReg(CC1101_@RN@, 0x@VH@); @<<@// @Rd@

That way we got the following output:

 # PA table 
 #define PA_TABLE {0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00}
 //
 // Rf settings for CC1101
 //
 cc1101.writeReg(CC1101_IOCFG0, 0x06);   // GDO0 Output Pin Configuration
 cc1101.writeReg(CC1101_FIFOTHR, 0x47);  // RX FIFO and TX FIFO Thresholds
 cc1101.writeReg(CC1101_PKTLEN, 0x0D);   // Packet Length
 cc1101.writeReg(CC1101_PKTCTRL0, 0x00); // Packet Automation Control
 cc1101.writeReg(CC1101_FSCTRL1, 0x06);  // Frequency Synthesizer Control
 cc1101.writeReg(CC1101_FREQ2, 0x10);    // Frequency Control Word, High Byte
 cc1101.writeReg(CC1101_FREQ1, 0xB0);    // Frequency Control Word, Middle Byte
 cc1101.writeReg(CC1101_FREQ0, 0xB3);    // Frequency Control Word, Low Byte
 cc1101.writeReg(CC1101_MDMCFG4, 0xF7);  // Modem Configuration
 cc1101.writeReg(CC1101_MDMCFG3, 0xB0);  // Modem Configuration
 cc1101.writeReg(CC1101_MDMCFG2, 0x30);  // Modem Configuration
 cc1101.writeReg(CC1101_MDMCFG1, 0x00);  // Modem Configuration
 cc1101.writeReg(CC1101_MDMCFG0, 0x2E);  // Modem Configuration
 cc1101.writeReg(CC1101_DEVIATN, 0x15);  // Modem Deviation Setting
 cc1101.writeReg(CC1101_MCSM0, 0x14);    // Main Radio Control State Machine Configuration
 cc1101.writeReg(CC1101_FOCCFG, 0x16);   // Frequency Offset Compensation Configuration
 cc1101.writeReg(CC1101_WORCTRL, 0xFB);  // Wake On Radio Control
 cc1101.writeReg(CC1101_FREND0, 0x11);   // Front End TX Configuration
 cc1101.writeReg(CC1101_FSCAL3, 0xE9);   // Frequency Synthesizer Calibration
 cc1101.writeReg(CC1101_FSCAL2, 0x2A);   // Frequency Synthesizer Calibration
 cc1101.writeReg(CC1101_FSCAL1, 0x00);   // Frequency Synthesizer Calibration
 cc1101.writeReg(CC1101_FSCAL0, 0x1F);   // Frequency Synthesizer Calibration
 cc1101.writeReg(CC1101_TEST2, 0x81);    // Various Test Settings
 cc1101.writeReg(CC1101_TEST1, 0x35);    // Various Test Settings
 cc1101.writeReg(CC1101_TEST0, 0x09);    // Various Test Settings
 

Programming the Arduino Nano

In order for the Nano to talk (over SPI) with the CC1101, we are going to use the great library from Daniel Berenguer, which is part of the panstamp project.

Having the SPI library and the correct registers configuration, the rest of the code is trivial. In my case, I just had to select the option with the old bootloader under the Tools->Processor menu, before uploading the sketch to my Arduino Nano.

#include "cc1101.h"

#define LEDOUTPUT 7

CC1101 cc1101;

const int buttonPin1 = A5;
const int buttonPin2 = A4;

int button1State = 0;
int button2State = 0;

bool sending = false;

void blinker() {
  digitalWrite(LEDOUTPUT, HIGH);
  delay(100);
  digitalWrite(LEDOUTPUT, LOW);
  delay(100);
}

void setup()
{
  Serial.begin(9600);

  pinMode(LEDOUTPUT, OUTPUT);
  digitalWrite(LEDOUTPUT, LOW);

  pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);

  // blink once
  blinker();

  Serial.println("Initializing CC1101.");
  cc1101.init();

  Serial.println("Setting up the PA_TABLE.");
  byte PA_TABLE[] = {0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00}; // 10dBm
  cc1101.writeBurstReg(CC1101_PATABLE, PA_TABLE, 8);

  Serial.println("Setting up the registers.");
  cc1101_SetupRegisters();

  delay(1000);
  Serial.println("CC1101 initialized.");
}

void sendoncode() {
  byte ondata[13] = { 0x88, 0x88, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x88, 0xEE, 0x88, 0xEE, 0x80 };
  sending = true;
  cc1101.sendData(ondata, 13);
  sending = false;
}

void sendoffcode() {
  byte offdata[13] = { 0x88, 0x88, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x88, 0xEE, 0xEE, 0x88, 0x80 };
  sending = true;
  cc1101.sendData(offdata, 13);
  sending = false;
}

void loop()
{
  if (!sending) {
    button1State = digitalRead(buttonPin1);
    button2State = digitalRead(buttonPin2);

    if (button1State == HIGH) {
      Serial.println("Sending the on code");
      digitalWrite(LEDOUTPUT, HIGH);
      sendoncode();
      digitalWrite(LEDOUTPUT, LOW);
    }

    if (button2State == HIGH) {
      Serial.println("Sending the off code");
      digitalWrite(LEDOUTPUT, HIGH);
      sendoffcode();
      digitalWrite(LEDOUTPUT, LOW);
    }
  }
}

void cc1101_SetupRegisters() {
  //
  // Rf settings for CC1101
  //
  cc1101.writeReg(CC1101_IOCFG0, 0x06);   // GDO0 Output Pin Configuration
  cc1101.writeReg(CC1101_FIFOTHR, 0x47);  // RX FIFO and TX FIFO Thresholds
  cc1101.writeReg(CC1101_PKTLEN, 0x0D);   // Packet Length
  cc1101.writeReg(CC1101_PKTCTRL0, 0x00); // Packet Automation Control
  cc1101.writeReg(CC1101_FSCTRL1, 0x06);  // Frequency Synthesizer Control
  cc1101.writeReg(CC1101_FREQ2, 0x10);    // Frequency Control Word, High Byte
  cc1101.writeReg(CC1101_FREQ1, 0xB0);    // Frequency Control Word, Middle Byte
  cc1101.writeReg(CC1101_FREQ0, 0xB3);    // Frequency Control Word, Low Byte
  cc1101.writeReg(CC1101_MDMCFG4, 0xF7);  // Modem Configuration
  cc1101.writeReg(CC1101_MDMCFG3, 0xB0);  // Modem Configuration
  cc1101.writeReg(CC1101_MDMCFG2, 0x30);  // Modem Configuration
  cc1101.writeReg(CC1101_MDMCFG1, 0x00);  // Modem Configuration
  cc1101.writeReg(CC1101_MDMCFG0, 0x2E);  // Modem Configuration
  cc1101.writeReg(CC1101_DEVIATN, 0x15);  // Modem Deviation Setting
  cc1101.writeReg(CC1101_MCSM0, 0x14);    // Main Radio Control State Machine Configuration
  cc1101.writeReg(CC1101_FOCCFG, 0x16);   // Frequency Offset Compensation Configuration
  cc1101.writeReg(CC1101_WORCTRL, 0xFB);  // Wake On Radio Control
  cc1101.writeReg(CC1101_FREND0, 0x11);   // Front End TX Configuration
  cc1101.writeReg(CC1101_FSCAL3, 0xE9);   // Frequency Synthesizer Calibration
  cc1101.writeReg(CC1101_FSCAL2, 0x2A);   // Frequency Synthesizer Calibration
  cc1101.writeReg(CC1101_FSCAL1, 0x00);   // Frequency Synthesizer Calibration
  cc1101.writeReg(CC1101_FSCAL0, 0x1F);   // Frequency Synthesizer Calibration
  cc1101.writeReg(CC1101_TEST2, 0x81);    // Various Test Settings
  cc1101.writeReg(CC1101_TEST1, 0x35);    // Various Test Settings
  cc1101.writeReg(CC1101_TEST0, 0x09);    // Various Test Settings
}

The complete project, with the CC1101 library included, can be found in my GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *