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


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.

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.

event ideal min max
short pulse 889μs 444μs 1333μs
short space 889μs 444μs 1333μs
long pulse 1778μs 1334μs 2222μs
long space 1778μs 1334μs 2222μ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.

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.

Implementation

The decoding state machine consists of 4 states, and 4 transition types.

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.

int RC5Advance(RC5State *State, RC5EVENT Event)
{
    static unsigned char trans[] = {0x01,  // 00 00 00 01  from state 0: short space -> 1                                                                                         
                                    0x81,  // 10 01 00 01  from state 1: short pulse -> 0, long pulse -> 2                                                                        
                                    0x8B,  // 10 01 10 11  from state 2: short space -> 3, long space -> 1                                                                        
                                    0xFB}; // 11 11 10 11  from state 3: short pulse -> 2                                                                                         
    if (Event==RC5EVENT_ERROR) {
        RC5Reset(State);
    } else {
        int newState = trans[State->state]>>Event & 0x3; // keep low 2 bits                                                                                                       
        if (newState==State->state) {
            RC5Reset(State);  // no transition indicates error
        } else {
            State->state = newState;
            if (newState == RC5STATE_MID0) {
                RC5Emit(State, 0); // always emit 0 when entering mid0 state
            } else if (newState == RC5STATE_MID1) {
                RC5Emit(State, 1); // always emit 1 when entering mid1 state
            }
        }
    }
    return State->bits == 14;
}

Contributors

Thanks to Boris Yost for pointing out an error in my notes above. The times in table are in microseconds, not milliseconds as I had them originally labelled.


-Guy Carpenter
Clearwater Software
31 Oct 2000
revised 2 Aug 2007