Welcome to End Point’s blog

Ongoing observations by End Point people

Using cec-client to Control HDMI Devices

Maintaining the horde of computers it takes to run Liquid Galaxies in all corners of the globe is a big job. As of November of 2012, we're monitoring 154 computers at permanent installations in addition to keeping our development and testing systems running like the well-oiled machines we want them to be. All that fluff aside, end users never see the fruits of our labor unless the TV's are working as expected! Without methods for getting and setting the status of displays, we are completely blind to what people are actually experiencing in front of a Liquid Galaxy.

Enter HDMI-CEC. CEC is a protocol that allows HDMI-connected devices to control each other in various ways. It has a set of standard features that make it easy for home users with a stack of DVD players or TiVos or other devices to change the active source, put everything on standby, control the volume, and some other handy tricks.

We typically use Samsung TV's which support CEC under the trade name "Anynet+". To interface between computers and TV's, we use Pulse Eight's USB - CEC adapters which, in conjunction with libCEC, give us a command line interface for arbitrary commands to the TV.

libCEC is available on apt for Ubuntu users:

$ sudo apt-get install cec-utils

Once installed, we have access to all kinds of fun commands. CEC-O-Matic is a great reference, and will even build command strings for you! Just bear in mind that CEC-O-Matic output has colons separating the octets, while cec-client's "tx" expects spaces.

The syntax of a libCEC "tx" command is like this:

$ echo 'tx <src-dst> <cmd> <args...>' | cec-client <device>

The first octet of the command will be the source and destination. The P8 CEC adapter uses device 1 by default, which is "Recording 1", and a TV is always 0, so when querying the TV our first octet will be "10".

The second octet is a command code. Let's say we want to know what language the TV's menu is set to. On CEC-O-Matic you can find this in the "Supporting Features" tab, "System Information" section, "Get Menu Language," which indicates that the message ID is "91".

Arguments are situational, and many commands will not require any arguments. We'll talk about arguments soon.

The device can be found with the ever-handy:

$ cec-client -l
Found devices: 1

device:              1
com port:            /dev/ttyACM0
firmware version:    2
firmware build date: Thu Aug  2 09:40:28 2012 +0000
type:                Pulse-Eight USB-CEC Adapter

We want to use that COM port, "/dev/ttyACM0".

For example, let's query the menu language of the TV connected to the first CEC adapter.

$ echo 'tx 10 91' | cec-client /dev/ttyACM0
[ . . . ]
DEBUG:   [             492] << requesting power status of 'TV' (0)
TRAFFIC: [             492] << 10:8f
TRAFFIC: [             638] >> 01:90:00
DEBUG:   [             638] >> TV (0) -> Recorder 1 (1): report power status (90)
DEBUG:   [             638] TV (0): power status changed from 'unknown' to 'on'
DEBUG:   [             638] expected response received (90: report power status)
waiting for input
TRAFFIC: [             639] << 10:91
TRAFFIC: [             842] >> 0f:32:65:6e:67
DEBUG:   [             842] >> TV (0) -> Broadcast (F): set menu language (32)
DEBUG:   [             842] TV (0): menu language set to 'eng'

Here you see that, after a bunch of other cruft is resolved, the adapter requests the power status of the TV (10:8f) and the TV reports that is is 'on' (01:90:00). This seems to be the first action of any query. Now, since we received an expected response, it sends the menu language query (10:91). In response the TV sends "0f:32:65:6e:67". What does this mean?

The first octet "0f" means the source is the TV (0) and the destination is "broadcast to all devices in the signal path" (f). The second octet, "32", is the command code for "Set Menu Language". The next three octets "65:6e:67" are ASCII code for "eng" which is the ISO 639-2 Code for "English." So, instead of just telling us what the menu language is, the TV is responding by setting all devices to its language.

In the next two DEBUG lines it reports what I just explained.

At this point, cec-client is idling. Use Ctrl-C to end the process gracefully.

What if we want to change the power setting of the TV? In this case, libCEC has built-in commands to make it a little more intuitive.

$ echo 'standby 0' | cec-client -s /dev/ttyACM0
$ echo 'on 0' | cec-client -s /dev/ttyACM0

The first command will put the TV on standby, the second will turn it on. In this case, we don't need to specify the command source, only the destination of TV (0). Also notice that we used the -s argument to cec-client and it exited as soon as it sent the command. -s is short for --single-command, which sends a command and then exits. We didn't use -s in the above menu language query because it causes cec-client to exit before we get the response back from the TV! For automated cec-client commands, such as automatic screen sleeping at night, -s is quite useful since the process doesn't 'hang' after execution.

These are the basics of cec-client. For more information on the app itself, you can consult the cec-client manpage or visit the libcec project on GitHub.

Happy hacking!


ZoFreX said...

Perfect! I was looking for idiot-proof instructions and these worked a charm. Thank you!

Adrian Spiegel said...

Hi, is it possible to control the volume with the cec-client?


Josh Ausborne said...

I've not worked with audio controls via cec-client, but I did find this other page where he mentions doing so:

Hopefully it is helpful.

Oswaldo Cunha said...

Is there an way to implement HDMI-CEC into an ordinary TV that does not count whit it ? An external "blackbox" for instance ?

Bryan Berry said...

Oswaldo, we at Liquid Galaxy, use these external adapters:

I hope this helps!

Oswaldo Cunha said...

Thanks a lot but I have already sented an email to them and Mr. Martin said that the TV must have HdMI-Cec protocol into its firmware .

Volker said...

Although this post is already several years old, I hope to get an answer anyway.
I understand that your examples are for Linux systems. I need to send some commands to the USB-CEC adapter from a Windows 7 computer in order to control a connected TV. Very simple stuff, but I don't how to do it.
This is for an elderly person with sight limitations.
1)I want to read and save the actual status of the person's TV (e.g. "standby" or "showing tv channel 3", switch the TV on if it is in standby, and change the source to the HDMI to which the PC is connected (e.g. HDMI 1), so that the PC screen is shown on the TV screen and audio from the PC goes to the TV. I will do this in order to start a Skype video conversation with the person at the TV set. The pc is remotely controlled by me via LogMeIn. After the conversation I want to reverse the source and status of the TV to the previous (that's why I want to read it first).
2) I want to dedicate one button of the TV remote to switch the TV source to HDMI 1 and then start a skype call to my address.

My main problem is that I have not yet found a step by step description of how to create and start a script in Windows that sends these commands to the adapter. I guess I will have to write "tx" command lines to a text file, rename it (to what?) in order to make it executable and then deposit it in a certain folder (where?). How do I start the script? How do I program the remote button?
Hopefully you can explain this to me or direct me to a source that describes everything for dummies. Unfortunately PulseEight seems to assume that everybody already knows how to use their device. I am not an expert in programming, don't know C, Python or anything similar. I know basic structures of programming languages but don´t have the time to study any of them well enough to become an expert.

Jules Paget said...

Hi everyone, is it possible to switch tv to box and box to tv ?