Adapting CEC controls to RS-232

In this article, we will instill a Raspberry Pi with some magic in order for a TV to talk to a hifi amplifier. By the end of the journey, we’ll have the Pi pretending to be an AV receiver, intercepting commands from the HDMI CEC bus, and relaying them to the amp via a combination of serial and IR control outputs.

Don’t feel like reading? Here’s the GitHub link.

I have always been a fan of the sound of an analog amplifier. I’m not the kind of audiophile who is going to claim that I can discern the imperfections in audio reproduction from a standard home theater receiver, but they certainly lack a warmth that analog equipment offers.

Because of this, I’ve always used vintage audio gear. For the past five years or so, my main integrated amplifier has been a Yamaha A-760. Unfortunately, it has been showing its age, needing repairs of some sort every six months or so. I have finally grown tired of opening it up and decided to modernize my setup.

I found a modern hifi amp from Cambridge Audio, the CXA61. It has a TOSLINK DAC, A/B stereo pairs, a builtin bluetooth receiver, and a design that meets my aesthetic preferences. It was missing two things from my wishlist: CEC control and a phono preamp. The phono preamp was an easy fix, just requiring adding a separate component to the setup. Achieving CEC control, however, would be a small challenge.

It’s worth noting that previously, I was using a standalone ARC DAC which attenuated the line-level output based on CEC volume controls. The amplifier itself predated even CDs, so full digital control was out of the question. Therefore, the feature was limited. Migrating from ARC to TOSLINK caused me to lose even that, though at least now I could benefit from a much better DAC.

Anyway, the CXA61 has an RS-232 serial port. Unfortunately, it only exposes power, source and mute controls over this interface. The IR remote control, however, is capable of controlling volume. I wrote to Cambridge Audio about this discrepancy, and apparently it is due to there being no feedback capability from the volume knob, so it might not behave properly in some serial control systems. At least it has a 3.5mm TRS jack for IR repeater input, so the remote control capabilities can be emulated relatively easily. I figured by hacking both the IR and serial inputs, I should be able to create a full-featured CEC bridge.

My requirements were as follows:

  • When the TV turns on, the amp should turn on and switch to the correct input.
  • When the TV turns off, the amp should turn off.
  • Volume and mute controls on the original TV remote work as intended.

The HDMI CEC standard defines a common protocol for devices to interoperate and handle these inputs. CEC is a 1-wire serial bus that sits on its own pin in an HDMI port, and seemed relatively easy to interface with. I was originally planning building some AVR-based monstrosity to interact with the CEC bus, but then I realized I still had a spare Raspberry Pi sitting on the bench. The Raspberry Pi’s HDMI port has built-in CEC support. There would be no real hardware work required for this project.

I hooked up the Pi to my TV, and used cec-client to figure out what messages it emitted. Decoding the messages with CEC-o-Matic, I learned that:

  • If I present as an audio system, the TV will send a System Audio Mode Request when it turns on.
  • The TV will broadcast a Standby message when it turns off.
  • Volume controls and mute will be passed through as keypresses.
  • Occasionally the TV will query for the audio system’s volume level and mute status.

I found pyCEC which would allow me to easily interface with the CEC bus, handle these commands, and send the necessary replies. Now, it was simply a matter of translating the controls to the CXA61’s native protocol.

I used pyserial with an FTDI-based USB-to-RS-232 adapter to handle the power and mute functions, and PiIR to bitbang IR codes over a GPIO pin which I spliced into a TRS cable for the IR port.

The serial protocol was fortunately well documented, and PiIR could learn the IR codes off the original remote with the help of an IR receiver module.

After implementing these controls, I just had to tie everything together with the right CEC events. Finally, I had created cec2rs232.

In theory, this project should be flexible enough to work with other devices. For the time being, I’ve only implemented the protocol of the one I own. Pull requests, however, are welcome.