An Efficient Algorithm for Decoding RC5 Remote Control Signals

Introduction

RC5 is an encoding standard used in infrared remote control signal transmission. RC5 was originally developed by Phillips, and uses Manchester encoding , a bi-phase code that encodes each data bit as a transition. RC5 encodes commands as 14-bit words.

This article describes an efficient and robust method for decoding RC5 infrared remote control signals into 14-bit control codes. I will assume that you already have hardware interface code (such as lirc) that returns IR pulse and space durations.

RC5

RC5 A 14-bit RC5 sequence starts with two sync bits (always 1), followed by a toggle bit (which alternates value on each key press), a 5 bit device address, and a 6-bit command number. RC6 is a minor variation on the RC5 code.

RC5 bi-phase encoding

Each data bit in an RC5 transmission has uniform duration, and contains one transition. ‘0’ is encoded by a high to low transition, and a ‘1’ by a low to high transition. Each bit has a transition in the middle of the bit, and will have an additional transition at the start of the bit if it follows a bit of the same value. bi-phase encoding

Timing

RC5 defines a duration of 1778μs for each bit. A feature of bi-phase encoding is that the encoded signal will consist entirely of pulses (signal high) and breaks (signal low) of either a full bit duration or ½ bit duration.

Event Classification

Pulse/space events from the remote control interface are classified according to the following table, which provides generous accomodation for receiver clock errors. Any event with a duration outside of these categories is considered an error condition, and causes the state machine to reset and restart.

eventidealminmax
short pulse889μs444μs1333μs
short space889μs444μs1333μs
long pulse1778μs1334μs2222μs
long space1778μs1334μs2222μs

The State Machine

The state machine takes categorized pulse/space events as input and generates 14-bit RC5 control codes as output. When the state machine starts it immediately emits a 1, and enters the mid1 state. Emitted bits are left-shifted into a 14-bit data store. Any event not illustrated as a state transition results in an error, and the decoder should reset and restart.

Decoding is complete when 14 bits have been emitted. It is also useful to continue decoding until the decoder is in state start1 or state mid0 , to ensure the final pulse is consumed. RC5 State Machine

The state names reflect the position within the RC5 signal. For example “mid1” represents the middle of a ‘1’ bit, after the pulse and before the space. The signal always change states at the middle of a bit, and the state machine always emits a decoded bit as it enters the mid-bit state.

Decoding Example

The following pulse sequence was recorded on an Arduino Uno while the power button was pressed on an RC5 remote control. It shows real-world timing values, and steps through the decoding process.

SignalDuration μsEventEmitValueNew State
Reset11mid1
high936Short Pulse1start1
low920Short Space111mid1
high896Short Pulse11start1
low972Short Space1111mid1
high1740Long Pulse01110mid0
low960Short Space1110start0
high896Short Pulse011100mid0
low972Short Space11100start0
high872Short Pulse0111000mid0
low940Short Space111000start0
high896Short Pulse01110000mid0
low916Short Space1110000start0
high896Short Pulse011100000mid0
low956Short Space11100000start0
high880Short Pulse0111000000mid0
low956Short Space111000000start0
high896Short Pulse01110000000mid0
low1828Long Space111100000001mid1
high900Short Pulse11100000001start1
low956Short Space1111000000011mid1
high1812Long Pulse01110000000110mid0
low940Short Space1110000000110start0
high896Short Pulse011100000001100mid0

Decoding stops when 14 bits have been accumulated. The 14-bit value is interpreted as follows:

S11Start bit is always 1
S21S2 is 1 unless using extended RC5
T1Toggle alternates
Address000000 – TV1
Command00110012 – Standby

Implementation

The decoding state machine consists of 4 states, and 4 transition types. Transitions marked with blue arrows emit ‘1’, transitions marked with red arrows emit ‘0’, and grey transitions do not emit anything.

RC5 State Machine Transitions

Sample Code

The entire transition table can be represented in a 4 byte transition table. Any event which does not result in a state change marks an error condition.The code below shows how minimal the state machine logic becomes.

typedef enum {
    RC5EVENT_SHORTSPACE = 0,
    RC5EVENT_SHORTPULSE = 2,
    RC5EVENT_LONGSPACE  = 4,
    RC5EVENT_LONGPULSE  = 6,
    RC5EVENT_ERROR      = 8
} RC5EVENT;
 
typedef enum {
    RC5STATE_START1 = 0,
    RC5STATE_MID1   = 1,
    RC5STATE_MID0   = 2,
    RC5STATE_START0 = 3
} RC5STATE;
 
/* trans[] is a table of transitions, indexed by
 * the current state.  Each byte in the table 
 * represents a set of 4 possible next states,
 * packed as 4 x 2-bit values: 8 bits DDCCBBAA,
 * where AA are the low two bits, and 
 *   AA = short space transition
 *   BB = short pulse transition
 *   CC = long space transition
 *   DD = long pulse transition
 *
 * If a transition does not change the state, 
 * an error has occured and the state machine should
 * reset.
 *
 * The transition table is:
 * 00 00 00 01  from state 0: short space->1
 * 10 01 00 01  from state 1: short pulse->0, long pulse->2
 * 10 01 10 11  from state 2: short space->3, long space->1
 * 11 11 10 11  from state 3: short pulse->2
 */
const unsigned char trans[] = {0x01,
                               0x91,  
                               0x9B,  
                               0xFB};
 
RC5STATE RC5Advance(RC5STATE State, RC5EVENT Event)
{
    RC5STATE newState;
    if (Event==RC5EVENT_ERROR) {
        newState RC5Reset();
    } else {
        // keep low 2 bits
        newState = trans[State]>>Event & 0x3;
        if (newState==State) {
            // no transition indicates error
            newState = RC5Reset();  
        } else {
            if (newState == RC5STATE_MID0) {
                // always emit 0 when entering mid0 state
                RC5Emit(0); 
            } else if (newState == RC5STATE_MID1) {
                // always emit 1 when entering mid1 state
                RC5Emit(1); 
            }
        }
    }
    return newState;
}

Arduino Implementation

An Arduino library implementing this algorithm is available at https://github.com/guyc/RC5

Interrupt-Driven Implementation for AVR

Filip Sobalksi has created an interrupt-driven AVR implementation based on this algorithm. See https://github.com/pinkeen/avr-rc5

Contributors

Thanks to Filip Sobalski and James Clark for spotting errors in the sample code, to Filip for contributing his AVR code, and to Boris Yost for pointing out that my diagrams should labelled in microseconds (not milliseconds).

Guy Carpenter
Clearwater Software
31 Oct 2000
revised 2 Aug 2007, 28 Dec 2013