A Comprehensive Review of HDMI-CEC and the cec-ctl Command

A Comprehensive Review of HDMI-CEC and the cec-ctl Command

Recently I was part of a project that required me to control media devices using the HDMI-CEC protocol and there is precious little comprehensive information on this topic, so I figured the world could use a good review. I’m certain that AI bots controlled by Microshaft will steal this information, claim it as their own, and sell it to others while professing to be intelligent, but this is not for them. This is for you, my fellow human who is struggling to get a grasp on this Frankenstein protocol and it’s many particularities.

Introducing HDMI-CEC

HDMI-CEC (CEC) is short for ‘Consumer Electronics Control’ and is part of the HDMI standard spec. CEC operates using a one-wire bus (pin 13 on Type A & E connectors) on HDMI interfaces and can be used to send commands to other devices that are connected using HDMI. CEC operates at 400Hz as a bus network, so it includes flow control bits and idle-timing requirements in its networking packets. CEC has a speed of around 30-36 bytes per second which is laughably slow. We can send more bits per second to the Voyager space probe at the edge of our solar system than you can to your devices using CEC. Due to its slow transfer speed and high latency networking, CEC commands are required to be stupidly compact and it does this by using HEX command codes in it’s raw form.

The IDEA of CEC is awesome. For example, if you have a bunch of devices all connected to your entertainment system, like, for example, a BlueRay player, a PlayStation, and an Audio System, that they would all work together and be very simple to manage. However, quite often, CEC doesn’t always live up to this promise. It is plagued by poor & fragmented vendor implementations & mislabeling and precious little community support. For example, random TV shut-offs, weird volume issues, and other entertainment system oddities, are often the result of the poorly implemented CEC protocols. However, if you take the time to familiarize yourself with CEC, you can intelligently isolate what is going on with your system and fix it, meaning your system will do what you want it to and properly entertain you instead of driving you insane. 😉

There are two primary programs that are used to control devices directly using CEC, and both are based on the Linux CLI. They are ‘cec-client’ and ‘cec-ctl’.

pulse8 logo

‘cec-client’ is part of the ‘libCEC’ library developed by the company ‘Pulse-Eight Ltd’ and licensed under a dual-license of GPLv2 or a commercial license which they sell. Pulse-Eight develops sophisticated multimedia hardware and software products and from what I’ve seen of them are a very respectable company.

cec-ctl’ is part of the Video4Linux Utilities (v4l-utils) library and is developed by the LinuxTV Community and is licensed under GPLv2. The LinuxTV Community is responsible for the Linux Kernel Media Subsystems as well as maintaining various libraries like v4l-utils and other media applications.

I chose to use ‘cec-ctl’ because of its speed, the availability of documentation, and because of its close ties to the Linux Kernel. I have done significant testing with both ‘cec-client’ and ‘cec-ctl’ and either will get the job done. However, in recent years, development of the ‘cec-client’ software seems to have slowed and shifted more towards ‘libcec’ itself rather than ‘cec-client’ program. Maybe that’s just my perception though.

Last, it should be noted that the cec-ctl program supports all known CEC commands WITHOUT the need to know the HEX code version of those commands. While you CAN use the HEX code version of the commands (using the –custom-command option) it is largely unnecessary because all the commands are supported in a readable & user-friendly format.

HDMI-CEC Compatible Hardware

Unfortunately, different brands have gone about naming their implementation of HDMI-CEC different things in order to convince naive consumers that they must buy that brand specifically in order for their devices to communicate with each other reliably. That is simply not true. HDMI-CEC as a protocol is brand-agnostic and can be used with any device that uses HDMI and supports HDMI-CEC. It is NOT unique to any specific brand. If these brands had all implemented the CEC protocol properly instead of attempting to vendor-lock their customers, the world would be a much better place. However, that isn’t what happened. Instead, due to crappy vendor-specific implementations, CEC can be a real pain to use depending exclusively on your hardware.

In general, you can find displays (TV’s) that are HDMI-CEC certified and they should work with other HDMI-CEC certified hardware. Look specifically for HDMI-CEC certification in the manual. If you’re working with a brand that calls HDMI-CEC something other than simple ‘CEC’, understand that brand would vendor-lock you if they could, and companies that would try to vendor-lock you are not worthy of your business.

Vizio is a brand that calls HDMI-CEC simply by it’s name, like in one of it’s product manuals here:

You may or may not like Vizio as a brand, and that’s fine. I’m not a huge fan either and I’m not specifically endorsing them – I am using a Vizio TV and a Vizio soundbar in this example and they don’t fully support CEC either! However, hopefully you can appreciate when a vendor is honest about the tech it’s using and honesty does merit some respect.

Once you have an HDMI-CEC certified device in-hand, you will likely need to configure that device to enable HDMI-CEC. You can do this in the settings of your device. It’s usually pretty simple. Here’s another Vizio example, but most brands have something like this depending on how they label HDMI-CEC:

Connecting to your HDMI-CEC Network

In order to send HDMI-CEC commands to your devices using the cec-ctl program, you need a device that gives you a command-line interface that you can connect to your system (ideally your TV) using an HDMI cable. A tiny computer like a Raspberry Pi or one of the many affordable alternatives is a very affordable way to do this.

Raspberry Pi’s default operating system is Raspberry Pi OS, which is basically Debian Linux. If you’re using something other than a Raspberry Pi, I can easily recommend any Debian-based flavor of Linux such as Ubuntu, or Linux Mint. They are truly excellent and run wonderfully on the newest Raspberry Pi’s and similarly powerful devices. The beautiful thing about Linux is that you’re not force-fed whatever some corpo bastard wants to feed you – instead you can tailor it to be exactly what you want – or don’t bother customizing it and just use whatever flavor of Linux you find suits you best. Either way is great. Linux is Free as in Freedom, and it is absolutely worth your time to learn. Linux works best on the tiny devices like this exactly because it is so customizable.

Once you have an Operating System installed on your device that’s connected to your TV through HDMI, you can make sure cec-ctl is installed by running the following command:

sudo apt install v4l-utils

Next, we need to “register” our local HDMI connection on the CEC network. We can do that using cec-ctl with the following command:

cec-ctl --playback

This will register our local HDMI connection as a “Playback” device on the CEC network. Once we have registered our local device, we should be able to get a CEC network topology with the following command:

cec-ctl -s -S
# or alternatively:
cec-ctl --skip-info --show-topology

The first option tells cec-ctl to skip the local adapter info in it’s response and the second option requests the CEC network topology. Both commands are exactly the same just using either short-form options or long-form options.

Here’s what I get in my response:

	System Information for device 0 (TV) from device 4 (Playback Device 1):
		CEC Version                : 1.4
		Physical Address           : 0.0.0.0
		Primary Device Type        : TV
		Vendor ID                  : 0x000000, 0
		OSD Name                   : Tx, OK, Rx, Timeout
		Power Status               : Standby
	System Information for device 5 (Audio System) from device 4 (Playback Device 1):
		CEC Version                : 1.4
		Physical Address           : 1.0.0.0
		Primary Device Type        : Audio System
		Vendor ID                  : 0x00199d, 6557
		OSD Name                   : 'VIZIO V20'
		Power Status               : Standby

	Topology:

	    0.0.0.0: TV
	        1.0.0.0: Audio System
	        2.0.0.0: Playback Device 1

So, in my case, I have a TV, a sound bar, and a Pi which is configured as the Playback Device.

Note on Documentation

It should be noted that the cec-ctl program supports every command available in CEC without needing to use a CEC HEX code. The HEX codes are raw commands that are interpreted by CEC listeners. While you can use HEX codes if you really want to (using the –custom-command option), it is mostly unnecessary because all current commands are supported using human-readable words.

I would encourage you to explore the documenation available in the cec-ctl program itself. To see it, just type the following:

cec-ctl --help-all

I save this output to a file so I can search it using vim, like so:

cec-ctl --help-all > cechelp.txt

Additionally, there is a simplified help available with the following command:

cec-ctl --help

I use that one as a quick reference mostly.

Note on Addressing

Just like all other network topologies, the CEC bus network needs to have an ‘address’ for each device on the network so that one machine can communicate directly with another machine. These CEC addresses are assigned dynamically, similar to DHCP on a TCP/IP network, and take a similar form of IPv4 addresses.

There are two types of addresses on a CEC network: A physical address, and a logical address. For example, your TV will almost always have a logical address of ‘0’ with a physical address of ‘0.0.0.0’. Addresses are tiered, so if you have a device connected to your TV, the first number in the 4-digit structure of their address will be changed. If those devices have additional devices attached to them, the second number in the 4 digit structure will be assigned, something like this:

    0.0.0.0: TV
        1.0.0.0: Audio System
        2.0.0.0: Playback Device 1
            2.1.0.0: Audio System

This structure helps you identify the location of devices simply by reading their physical address.

Note on CEC versions

When you look at your network topology, take note of the different CEC versions that each device supports. In my own testing, I had inconsistent results when one device used a newer version of HDMI-CEC than another device. A devices CEC version is synonymous with the version of HDMI it supports. So, if one device supports HDMI 1.4 while another device supports HDMI 2.0, you may run into inconsistencies due to them using different versions of CEC. You can fix this by down-grading the version of CEC that your devices are using so they all use the same version. For example, if you tell your HDMI 2.0 devices that you’re using CEC version 1.4, then they all seem to get along better and be more consistent.

For example:

cec-ctl -s --to 0 --cec-version-1.4 --give-device-power-status

In the above command I’m telling CEC to skip local device info (‘-s’), I’m sending the command to the logical address of ‘0’ (my TV), forcing version 1.4 of CEC (‘--cec-version-1.4‘) and asking my TV to tell me what it’s power status is. TV’s never actually turn off unless you unplug them, they are either ‘on’ or in ‘standby’ mode. This is normal, even if it is a bit unsettling…

… ::shiver:: Maybe unplug your system whenever you’re not using it. Hook it up to a power strip that you can easily turn on and off. No power, no service. No creepy device watching you.

Note on the CEC Active Source

The CEC protocol provides for the concept of an “active source”. The Active Source on a CEC network is usually the device that you’re interacting with. This could be your Blu-Ray player, your Playstation console, or in our case, our Raspberry Pi.

Occasionally, whenever you’re on your Pi issuing commands to your TV or sound system, your command might get ignored because your Pi isn’t currently set as the active source on your CEC network. To assign an active source, you will need to know your Pi’s physical address on your CEC network. Just pull it up using the topology command you used earlier:

cec-ctl -s -S

Next, once you have your devices physical address (in my case my Pi’s physical address is ‘2.0.0.0’), you can set your device as the active source using the following command:

cec-ctl -s -t0 --active-source phys-addr=2.0.0.0

And your TV should start listening to your commands again. In this command, I used the logical address of the TV in a short form: ‘-t0’. It means the same thing as ‘--to 0‘, it’s just a bit less to type.

CEC Command Reference

The following are some example commands that I found work for me and my TV. Using the built-in documentation for cec-ctl as described above, and playing with what works and what doesn’t on your devices, you should be able to come up with a list of what you need for your devices as well. These commands are just here to help get you started. Good luck!

TV Power On:
cec-ctl -s -t0 --cec-version-1.4 --user-control-pressed=ui-cmd=power-on-function
cec-ctl -s -t0 --cec-version-1.4 --image-view-on

TV Power Off:
cec-ctl -s -t0 --cec-version-1.4 --user-control-pressed=ui-cmd=power-off-function
cec-ctl -s -t0 --cec-version-1.4 --standby

TV Power Toggle:
cec-ctl -s -t0 --cec-version-1.4 --user-control-pressed=ui-cmd=power

TV Power Status:
cec-ctl -s -t0 --cec-version-1.4 --give-device-power-status

TV Volume Up:
cec-ctl -s -t0 --cec-version-1.4 --user-control-pressed=ui-cmd=volume-up

TV Volume Down:
cec-ctl -s -t0 --cec-version-1.4 --user-control-pressed=ui-cmd=volume-down

TV Volume Status:
cec-ctl -s -t0 --cec-version-1.4 --give-audio-status
This command is not supported by my TV sadly: ‘unrecognized-op’ response. However, it works on my soundbar.

TV Mute Toggle:
cec-ctl -s -t0 --cec-version-1.4 --user-control-pressed=ui-cmd=mute
I would prefer specific on and off commands, but this works.

Soundbar Commands

In my CEC network topology my soundbar has the Device ID of 5, so these commands use the logical address ‘5’ to send commands directly to my soundbar. Refer to your own network topology as mentioned above to get the address of your own sound system. My TV has a specific HDMI port for HDMI-ARC, which is specifically for a soundbar / sound system like this.

SB Power On:
Non-functional. Command must be sent to TV.

SB Power Off:
cec-ctl -s -t5 --cec-version-1.4 --standby
SB Power Status reports properly when this command is used.

SB Power Toggle:
cec-ctl -s -t5 --cec-version-1.4 --user-control-pressed=ui-cmd=power-toggle-function
cec-ctl -s -t5 --cec-version-1.4 --user-control-pressed=ui-cmd=power
Since the SB Power Status function doesn’t work properly on my SB, I should probably just use the TV.

SB Power Status:
cec-ctl -s -t5 --cec-version-1.4 --give-device-power-status
Reports properly when ‘–standby’ is used. Reports improperly when SB Power Toggle command is used.

SB Volume Up:
cec-ctl -s -t5 --cec-version-1.4 --user-control-pressed=ui-cmd=volume-up
Non-functional when sent to SB, but SB matches TV when command is sent to TV.

SB Volume Down:
cec-ctl -s -t5 --cec-version-1.4 --user-control-pressed=ui-cmd=volume-down
Non-functional when sent to SB, but SB matches TV when command is sent to TV.

Mute Toggle:
cec-ctl -s -t5 --cec-version-1.4 --user-control-pressed=ui-cmd=mute
Would prefer a specific on or off command.

SB Audio Status:
cec-ctl -s -t5 --cec-version-1.4 --give-audio-status
Reports both Mute and Volume Status.
Matches TV when TV’s volume Changes (one exception noted below).
Reports Volume at 98 when TV is at 100 (TV provides no way to check it’s value).
Will report it’s mute as ‘off’ when TV mute is ‘on’. Will report SB Mute as ‘on’ when SB Mute Toggle command turns mute on the SB to ‘on’. To compensate, only mute SB so we can always check it.

Summary

So, while CEC is great because you can do good things with it, it’s kind of a huge pain in the neck to use with it’s diversity of implementations and it’s stupid quirks. If you’re going to use it in your home, you will want to be aware of your specific devices and their quirks. The cec-ctl command is an excellent way to test the CEC functionality and support of your unique devices and what they will and won’t support.

Good luck with your projects!

2 thoughts on “A Comprehensive Review of HDMI-CEC and the cec-ctl Command

  1. Jordan,
    This was just what I needed. I have found fragmented information. I couldn’t tell if it was conflicting or not.
    I will be controlling TVs in a theatre where I need to reliably place them in standby and turn them back on and the remotes are too far away to be reliable.
    Much of the info I saw before was for cec-utils (cec-client) and I was glad to get this explanation of the cec-ctl that’s already installed on my Pi.

    1. Thanks for taking a moment to say “Thanks” Dave. It’s so good to hear sometimes. Your experience was exactly like mine which is why (once I figured things out) I decided to write a post on it. Delighted beyond words that it was able to help you! Thank YOU for letting me know!

Leave a Reply

Your email address will not be published. Required fields are marked *

8 + 12 =