Exploring HDMI CEC

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.

LG remote

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 tag:

DEBUG:   [24978]  << Recorder 1 (1) -> TV (0): vendor id LG (e091)
TRAFFIC: [24979]  << 1f:87:00:e0:91
TRAFFIC: [25248]  >> 01:89:01
DEBUG:   [25251]  >> 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: [157930]  >> 01:44:46
DEBUG:   [157937]  >> TV (0) -> Recorder 1 (1): user control pressed (44)
DEBUG:   [157940]  key pressed: pause (46)
TRAFFIC: [158030]  >> 01:8b:46
DEBUG:   [158032]  >> TV (0) -> Recorder 1 (1): vendor remote button up (8B)
DEBUG:   [158033]  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: [14897]  >> 01:8c
DEBUG:   [14897]  >> TV (0) -> Recorder 1 (1): give device vendor id (8C)
DEBUG:   [14898]  << Recorder 1 (1) -> TV (0): vendor id LG (e091)
TRAFFIC: [14899]  << 1f:87:00:e0:91
TRAFFIC: [15175]  >> 01:89:01
DEBUG:   [15175]  >> TV (0) -> Recorder 1 (1): vendor command (89)
TRAFFIC: [15176]  << 10:89:02:05
NOTICE:  [15298]  SL initialised
TRAFFIC: [18646]  >> 01:1a:03
DEBUG:   [18646]  >> TV (0) -> Recorder 1 (1): give deck status (1A)
DEBUG:   [18647]  >> Recorder 1 (1): deck status changed from 'stop' to 'LG other'
DEBUG:   [18648]  << Recorder 1 (1) -> TV (0): deck status 'LG other'
TRAFFIC: [18649]  << 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:

LG manual on SimpLink Functions

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.

Further Reading

If you’re interested, the CEC specifications is quite straighforward to read. You can also take a look at the libCEC source on GitHub to see how they handle various manufacturer quirks.


7 comments on “Exploring HDMI CEC

  1. peutipoix says:

    Thanks, was nice to read, can’t wait to try with my samsung tv now…

    • darell tan says:

      Thanks for the kind words. This was actually only part 0 of the project, the initial research. Unfortunately, since my TV doesn’t support remote pass-thru, the rest of the project will have to wait until I get a TV that has support for it. If your TV does support it, please let me know the model 🙂

      • Travis J. Bennett says:

        Newer Samsung Smart TVs do… still working on getting the keyboard to pass thru via libCEC.

  2. Travis J. Bennett says:

    Newer Samsung Smart TVs do… still working on getting the keyboard to pass thru via libCEC.

  3. Eisbaeeer says:

    Does anyone try to implement the libcec into a socket server as a API? In this way the RPI will be the flexible remote control, including turn on TV from PC.

  4. Verry creative post

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.