Consumer Electronics Control (CEC) allows control of AV devices that are connected via HDMI. This is the feature of HDMI that enables your TV to automatically turn on and switch to the correct input when you switch on your set-top box, for example. It also allows you to control your set-top box using the TV remote (in some cases).
Electrically, the CEC bus is a single-wire bus that is shared between all HDMI devices, thus any CEC message can be received by all connected devices. Each device then claims one or more logical addresses on which it will receive direct CEC commands.
One interesting feature in the HDMI CEC specifications is Remote Control Pass Through, which allows button presses on the remote control to be passed through to HDMI-connected devices. I thought this feature could be used to unify the various remotes in my living room.
However, not all CEC devices are created equal. As usual, some manufacturers will deviate from the specifications, and/or introduce some quirks in their implementation (as you will see later). They also love to brand CEC with their own funky name, such as SimpLink or Anynet+.
Raspberry Pi as a CEC Bridge
As a quick and dirty way to check out the capabilities of my TV, I used a Raspberry Pi which has a HDMI connection that can be software-controlled. This also meant that I didn’t have to build my own CEC transceiver circuit.
libCEC provides an abstracted API for CEC and it supports the Raspberry Pi. This is how you can control media programs like XBMC using your TV remote and XBMC can also switch to the correct TV input when playing videos. I’m currently running Arch Linux on the Pi, so I used the following command to install libCEC:
pacman -S libcec-rpi
libCEC ships with a command-line client,
cec-client, that allows you to send CEC commands as well as sniff CEC traffic on the bus.
cec-client can act as a passive listener on the CEC bus with the
--monitor argument. When run without arguments,
cec-client announces itself on the CEC bus and waits for user input. In this mode, it will also automatically respond to queries such as “Give Device Vendor ID”.
In testing, it seems the messages received are either broadcasts or destined for my own logical address, so it could be possible that they might be filtered at some lower layer (hardware or the VideoCore library).
CEC Frame Format
CEC frames consist of a header block and optionally data blocks 1 and 2, which holds the opcode and its parameters, respectively. The header is a single byte which consists of the initiator and destination logical address, each of which are 4 bits.
cec-client displays CEC bus traffic with a
TRAFFIC tag, followed by the message direction and the raw bytes. At the same time, it also prints a human-readable description with a
DEBUG:  << Recorder 1 (1) -> TV (0): vendor id LG (e091) TRAFFIC:  << 1f:87:00:e0:91 TRAFFIC:  >> 01:89:01 DEBUG:  >> TV (0) -> Recorder 1 (1): vendor command (89)
The CEC decoder utility at cec-o-matic.com is also handy for decoding CEC frames.
On the LG TV
I have an LG 42LE5500 TV, which reports its CEC version as 1.3a. Unfortunately, it does not pass through all the remote buttons as I had hoped. The CEC spec defines a set of user control codes in Table 27, which has buttons like Channel Up, Channel Down, Play, Stop, and numbers 0-9.
For my TV, only the navigation button on the 4-way controller (up, down, left, right and enter/select) and VCR-like buttons (like Play, Pause, Fast-forward) are sent via CEC. Of course, the TV also supports basic CEC functionality such as announcing when it enters standby or turns on.
Here’s what happens when I press the Pause button:
TRAFFIC:  >> 01:44:46 DEBUG:  >> TV (0) -> Recorder 1 (1): user control pressed (44) DEBUG:  key pressed: pause (46) TRAFFIC:  >> 01:8b:46 DEBUG:  >> TV (0) -> Recorder 1 (1): vendor remote button up (8B) DEBUG:  key released: pause (46)
Notice the inconsistent message types used: the “User Control Pressed” (
0x44) message is supposed to be negated by “User Control Released” (
0x45) but the LG TV sends the “Vendor Remote Button Up” (
0x8B) message instead. Also, this message is not supposed to have any parameter as it negates the last-pressed button, but the TV decides to send the key code as a parameter anyway. libCEC takes care of these quirks so applications don’t have to.
There are 4 types of devices defined in the CEC spec: Recording Device, Playback Device, Tuner, Audio System. You can specify how
cec-client identifies itself using the
--type parameter. Only the Recording Device will have the buttons forwarded to it. Other devices such as a Playback Device or Tuner will not receive remote button presses.
Additionally, the LG TV will request for the device vendor ID and if it is also LG, it attempts to send a vendor-specific command:
TRAFFIC:  >> 01:8c DEBUG:  >> TV (0) -> Recorder 1 (1): give device vendor id (8C) DEBUG:  << Recorder 1 (1) -> TV (0): vendor id LG (e091) TRAFFIC:  << 1f:87:00:e0:91 TRAFFIC:  >> 01:89:01 DEBUG:  >> TV (0) -> Recorder 1 (1): vendor command (89) TRAFFIC:  << 10:89:02:05 NOTICE:  SL initialised ... TRAFFIC:  >> 01:1a:03 DEBUG:  >> TV (0) -> Recorder 1 (1): give deck status (1A) DEBUG:  >> Recorder 1 (1): deck status changed from 'stop' to 'LG other' DEBUG:  << Recorder 1 (1) -> TV (0): deck status 'LG other' TRAFFIC:  << 10:1b:20
There’s also a custom “deck status” that
cec-client responds to the TV (defined status codes are greater than
0x10). I’m not sure how these affect the capabilities that the TV will perceive the device to have, but I hope these aren’t used to determine device features. The CEC standard defines the Feature Abort message that should be used for this purpose.
I turned to the TV manual to see if there was any description of supported SimpLink features and there it was:
I should have consulted the user manual first, but this was a fun exercise nevertheless.
To sidetrack a bit, I really like the LG TV’s manual. It’s very hacker-friendly as it includes info in its Appendix on IR Codes including the hex codes for each button, a section on TV control via the RS-232 port that shows you wiring diagrams and the various commands that you can send over serial.