September 25, 2018

Playing with Intel NUC6CAYH

A first look at setting up an Intel NUC for software control of the LEDs

Playing with Intel NUC6CAYH

Part 1 of a series on messing with controlling NUC LEDs

For much of my docker experimentation, I had been running most of my containers off of a RPi3. I had to look for ARM compatible docker images and Home Assistant struggled with some beefier components, among various issues. This summer I had finally had enough and gave in and bought an Intel NUC6CAYH. It's a nice little box which I can pop on a shelf somewhere and forget about while still being x86_64.

One thing I was quite excited about were the LEDs on the front, as well as acting as power or hard drive activity monitors, these LEDs can be set in the BIOS to be controlled directly by software. This gives interesting applications for more interesting behaviour such as Home Assistant telling the ring to flash the night before bin collection to remind me to take out the rubbish.

LED Drivers

This isn't a feature which is pushed by Intel very hard. In my googling on the subject I could only find this article on the topic. Unfortunately this only documents a WMI interface which seemed to be windows specific. Not very encouraging to me, someone wanting to run this in a Linux environment.

However, I stumbled across a forum post

The documentation (here: Using WMI to Program the Ring and Button LEDs on Intel NUC Kits NUC7i#BN and NUC6CAY) details two software layers,

  1. A lower layer, which exposes the LED programming (method) interface in
    ACPI.

  2. An upper layer, which exposes the WMI interface for invoking ACPI methods.

While the second (WMI) layer is specific to Windows, the first layer (ACPI) is O/S independent. There are implementations that support the invocation of ACPI methods in Linux. Here's a place to start that I found in a quick search: GitHub - mkottman/acpi_call: A linux kernel module that enables calls to ACPI methods through /proc/acpi/call.

This was reassuring but daunting. I knew that what I wanted to do was possible and had a framework on how to get there. But I also knew that I had no idea what I was doing messing with the kernel as I had never touched it before.

I was going to be in for a long learning experience.

However this post seemed to be ever so slightly out of date as scrolling a little further down the thread we come across a post by milesp20 who has made a neat little kernel module to control the LEDs using WMI. Googling after the fact shows that WMI has been supported in newer Linux kernels released in 2017 (however, I find reliable information on the kernel to be pretty difficult to find so who knows what the state of play is.)

After a quick poke through the code to get an idea of what was going on (It's always good to have at least a quick look at code you get from an online forum), I compiled the module using DKMS and installed it into the kernel.

(Note to anyone else using DKMS for the first time dkms install does not mean that the module is installed and running. You need to use modprobe nuc_led to add the module into the kernel)

Following the instructions from milesp20, I could then control the LED state using a command similar to

 echo '<led>,<brightness>,<blink/fade>,<color>' | sudo tee /proc/acpi/nuc_led > /dev/null

Success! I only had three problems with this implementation as is, which were:

  1. We require root privileges to change the state of the LED, prompting me for a password on each command. If I'm integrating this into a script I don't want to be forced to run it as root just to change the colour of a light.
  2. This is a rather ugly command to run, if I'm integrating this into a script I'd much prefer to hide some of how the sausage is made. Ideally being able to change a single property without explicitly copying the other properties.
  3. If I turn off my NUC the LEDs remain in their previous state before the power is cut. What use is a status indicator which can't distinguish between the NUC being on and working or turned off?

The last two points are a little more complex and involved a reasonable amount of code/learning so will be separate posts but the first was relatively simple.

LED Permissions

This was the easiest problem to fix as it was just down to the permissions on /proc/acpi/nuc_led. I wanted to make this a little more open so that my user could control with the LED without making it a free for all between all users on the machine.

To do this I set the group ownership /proc/acpi/nuc_led to be a group named led which I then added my user to. This worked fine, however on a reboot the ownership of the /proc/ entry is reset to root:root . This can be fixed with a short entry to the root crontab as below.

@reboot chown root:led /proc/acpi/nuc_led

The kernel module also supports the owning uid and gid's to be set by passing parameters to the module while using modprobe, eliminating the need for the crontab entry. If you're just going to set and forget then this is a cleaner option, however I preferred the crontab solution as there is no kernel parameter to remember when repeatedly removing and readding the module as I was doing.