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. 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.
Signal | Duration μs | Event | Emit | Value | New State |
---|---|---|---|---|---|
Reset | 1 | 1 | mid1 | ||
high | 936 | Short Pulse | 1 | start1 | |
low | 920 | Short Space | 1 | 11 | mid1 |
high | 896 | Short Pulse | 11 | start1 | |
low | 972 | Short Space | 1 | 111 | mid1 |
high | 1740 | Long Pulse | 0 | 1110 | mid0 |
low | 960 | Short Space | 1110 | start0 | |
high | 896 | Short Pulse | 0 | 11100 | mid0 |
low | 972 | Short Space | 11100 | start0 | |
high | 872 | Short Pulse | 0 | 111000 | mid0 |
low | 940 | Short Space | 111000 | start0 | |
high | 896 | Short Pulse | 0 | 1110000 | mid0 |
low | 916 | Short Space | 1110000 | start0 | |
high | 896 | Short Pulse | 0 | 11100000 | mid0 |
low | 956 | Short Space | 11100000 | start0 | |
high | 880 | Short Pulse | 0 | 111000000 | mid0 |
low | 956 | Short Space | 111000000 | start0 | |
high | 896 | Short Pulse | 0 | 1110000000 | mid0 |
low | 1828 | Long Space | 1 | 11100000001 | mid1 |
high | 900 | Short Pulse | 11100000001 | start1 | |
low | 956 | Short Space | 1 | 111000000011 | mid1 |
high | 1812 | Long Pulse | 0 | 1110000000110 | mid0 |
low | 940 | Short Space | 1110000000110 | start0 | |
high | 896 | Short Pulse | 0 | 11100000001100 | mid0 |
Decoding stops when 14 bits have been accumulated. The 14-bit value is interpreted as follows:
S1 | 1 | Start bit is always 1 |
S2 | 1 | S2 is 1 unless using extended RC5 |
T | 1 | Toggle alternates |
Address | 00000 | 0 – TV1 |
Command | 001100 | 12 – 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.
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 CarpenterClearwater Software
31 Oct 2000
revised 2 Aug 2007, 28 Dec 2013