How I used Udev with systemd to configure an AIO water cooler in Linux

 Let me start by telling a little bit of the back story. Recently I build a mini ITX desktop PC to help me with my more resource intensive programming work. I used a all in one liquid cooler in this PC. (Kraken X63). Setting something like this in Windows is quite straight forward, not so much in Linux. In this article I will detail what I did to get it working the way I wanted. 

First problem to solve was to find a driver/controller software for the cooler, that works in Linux. These coolers come with only windows driver/controller software with no official Linux support. Thanks to the awesome open source community there is a cross platform tool that I can use to talk to the API of the liquid cooler which is called liquidctl. To be more specific I had to use an experimental branch of this tool that supported the API version of my hardware. 

Installation is quite straight forward. My preferred Linux distro is Arch. I used an already ported AUR repository of the mainline and switched to the branch I needed before building the source. 

  • `git clone https://aur.archlinux.org/liquidctl.git `
  • `git checkout -b 1.4.x`
  • `makepkg –sic`
That's it for installation. Now to test it out
  • `sudo liquidctl status`
[image]

This is the default settings. Now I need to configure the cooler with values I need. I wanted to change 2 configuration options
  • The cooler pump speed relative to the coolant temperature
  • Switch off the decorative RGB lighting of the cooler
After referencing the API details given in the liquidctl package. I narrowed down the API calls I needed to do.
  • `liquidctl set pump speed 25 25 30 30 34 80 40 90 50 100 `
  • `liquidctl set ring color off`
  • `liquidctl set logo color off`
Now that I have a way to control the cooler the next step is automating it. The cooler is not able to persist it's settings, so I need set these settings after every system boot. The way to do that is using systemd. The steps involved are,
  • Create a systemd unit
    • `sudo vim /etc/systemd/system/liquidcfg.service`
  • Contents of the file

[Unit] 
Description=AIO startup service 

[Service] 
Type=oneshot 
ExecStart=liquidctl set pump speed 25 25 30 30 34 80 40 90 50 100 
ExecStart=liquidctl set ring color off  
ExecStart=liquidctl set logo color off  
RemainAfterExit=yes
  
[Install] 
WantedBy=default.target 
  • Setup the newly created unit to run on every boot
    • `systemctl daemon-reload`
    • `systemctl start liquidcfg`
    • `systemctl enable liquidcfg`
I thought that was all I needed to do. After a few restarts I started noticing the configurations failed to reflect intermittently. The root cause was a race condition. The cooler exposes it's API via an USB interface. If the interface drivers was loaded when the new systemd service executes everything goes smoothly not so otherwise. Thus simply running the systemd unit during startup is not an option. I used Udev to solve this race condition.

Udev is the device manager for the Linux kernal. As mentioned the cooler has a internal usb connection. If I try to set the configuration before this connection is established the race condition occurs. Using Udev I can identify and trigger the settings change after the USB connection is established. 

First of all I needed to find the USB connection details. I used `lsusb` command. I had to install `usbutils` package to get this. If you are in a debian based distro you probably will have this package already installed. `lsusb` gave me the following info about the device,

Bus 001 Device 004: ID 1e71:2007 NZXT NZXT USB Device

Using the above information I created a new Udev rule file `/lib/udev/rules.d/99-z-kraken.rules`

SUBSYSTEM=="usb", ATTRS{idVendor}=="1e71", ATTRS{idProduct}=="2007", MODE="0666", TAG+="systemd", ENV{SYSTEMD_WANTS}+="liquidcfg.service"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e71", ATTRS{idProduct}=="2007", MODE="0666", TAG+="systemd", ENV{SYSTEMD_WANTS}+="liquidcfg.service"

Now I disabled the previous systemd service from automatically starting at startup

`systemctl disable liquidcfg`

Afterwords reload the udev rules so that my new rule will take effect

`udevadm control --reload`

That solved the race condition. Now I can view the status of the cooling system using `liquidctl status`


That's a wrap.




Comments

Popular posts from this blog

Nextcloud and PHP8

Setting up KDiff3 to work with TortoiseGIT

Nextcloud on Arch Linux (Encrypted System) [Part 01 - Preparation]