A few weeks ago a family member brought up the idea of LED backlighting for TV viewing and that prompted the gears to start turning in my head. I looked into all the popular commercial solutions (Phillips Hue, Lytmi, Govee, Nanoleaf, and Fancy LEDs) and found them lacking:

  • Phillips Hue's solution starts at $399 without the lights...
  • Fancy LEDs uses wifi and the Tuya cloud which is a big no from me trust-wise. I just eliminated Tuya from my LAN recently.
  • Lytmi is also Tuya cloud-based. Nope nope nope.
  • Nanoleaf's solution is also, as I understand it, cloud-based (though not Tuya) and uses an actual camera to scan the screen. Placement of a camera with cats in the house is unlikely to stay working.
  • Govee's TV backlights use a camera as well, and rely on Govee's proprietary cloud service and app.

It struck me that there must be open source solutions to the problem. First, I tried out Hyperion which seemed good on paper but between a glitchy web UI (it regularly forgot I'd set a password, among other quirks), overhead of software-controlling LEDs through pins on the Raspberry Pi, and lack of HDR video support I kept looking.

Then I found HyperHDR which seemed like it answered all my problems - it supports all the same LED-driving methods as Hyperion but recommends the use of a separate microcontroller running LED-driving firmware (including HyperSerialPico, HyperSerialESP32, HyperSerialESP8266, and even their own fork of WLED for ESP32) to offload and separate the process from the system running HyperHDR, allowing it to run on devices other than just Raspberry Pi.

I quickly set up a proof-of-concept implementation:

This all worked wonderfully, gaining me quick approval from my housemates, so I moved on to building the setup for the actual TV. The major changes needed would be:

  • switching from ws2812b RGB LED strips to sk6812 RGB+"Cold White" LED strips for more accurate whites and colour alignment to those of the TV
  • figuring out the best supported, stable, inexpensive capture dongle to use
  • finding a way to auto-switch the LEDs between SDR and HDR modes when watching content of each type

The last bullet point would lead me down a rabbit hole lasting over a week. It turned out to be a very complex problem.

Detecting SDR vs. HDR modes using HDMI-CEC? (tl;dr: this was an unnecessary rabbit hole)

The only out-of-the-box option (as of HyperHDR v20) for detecting SDR and HDR mode changes is HDMI-CEC codes, though as I later learned, it's unusual for these messages to be sent over HDMI-CEC (more typically, the HDR mode is expressed in the content metadata alone). For the built-in support, a Pulse-Eight USB - CEC adapter is required. I didn't want to buy one of these ($60 CAD) adapters so I looked into alternate ways to see these messages, knowing I could easily write my own scripts if needed.

Microsoldered connection to pin 13 of my HDMI splitter's Output 1, for relaying HDMI-CEC traffic.

I did some soldering with the microscope on my HDMI splitter to break out a CEC lead I could monitor with and I soon turned up the Linux kernel's own cec-gpio support which would allow using pins on the Raspberry Pi 4 to monitor (or send) HDMI-CEC messages. Unfortunately, this support is not built-in to the Raspberry Pi OS kernel so I would need to recompile.

I won't get too into the weeds of the kernel recompile here because HDMI-CEC support wasn't needed for the solution I settled upon in the end, but the two kernel config options that needed to be changed in the kernel to make cec-gpio work were:

  • CONFIG_DRM_VC4_HDMI_CEC - this is the CEC for the HDMI ports on the RPi4. This option must be disabled as it conflicts with cec-gpio.
  • CONFIG_CEC_GPIO - this is the baseline kernel support for cec-gpio. This option must be enabled (=y)

I then needed to make an overlay to map the pins (GPIO20 and GPIO21 in my case). I've posted the .dts file I used in a gist on Github.

Pin monitoring from the cec-ctl utility in Linux showing communication between HDMI devices.

I was now able to monitor HDMI-CEC traffic between devices though, much to my disappointment, the SHIELD TV gave no indicators of graphical mode switches over this channel. I wouldn't quite call it a wasted effort as I learned a ton about HDMI-CEC, but it wouldn't work for my purposes except as a way to detect when the LEDs should be powered on or off.

Detecting SDR vs. HDR modes using the TV's WebOS API? (tl;dr: this didn't work)

It's at this stage that I truly got frustrated, not by effort but by apparently intentional limitations imposed by a vendor: my LG smart television has an API that's usable by tools such as Home Assistant. However, that API does not seem to provide any means of discerning the picture mode, be it an SDR mode or HDR mode. There are certainly calls available to set the picture mode, but seemingly none available to get the current picture mode.

It's hard to see this as anything but an intentional choice on LG's part since, programmatically-speaking, getters are usually built and in place before setters.

The WebOS app API seems to be similarly stunted in its ability to discern the picture mode and provide that information to an external device.

Detecting SDR vs. HDR modes using the Android Debugger!

Left with few options for automating this detection, I brainstormed last-ditch effort ideas and came up with a long-shot of using the debugging tool (adb) from the Android SDK's platform tools to continuously monitor logs on the Nvidia SHIELD TV Pro I'm using as my primary video source.

I enabled Developer Mode on my SHIELD TV Pro and turned on network debugging, then ran adb on my RPi4 and scoured logs for messages indicating the change in SDR/HDR status. To my great satisfaction, I found the right messages and started working on a Python program to handle monitoring the logs and making calls to the HyperHDR API when the right conditions were met.

I'm still working on making this program more resilient in the face of network failures and the SHIELD TV rebooting but I'm nearing a point where it can be safely left alone to do its thing.

I've gone back to using an even cheaper MS2109-based video grabber which has its LUT preconfigured in one of the HyperHDR one-click downloads.

I finalized my install with SK6812 LEDs and in either SDR or HDR mode, this setup looks very good.