The minimum viable fan control script
I’ve always been a fan of tinkering with cooling setups on my computers. I’ve even went as far as writing crappy solutions to make up for deficiencies on the hardware level. After years of dumb experiments I’ve seen how little you can get away with in cooling and how to run your machines as quietly as possible without giving up too much performance.
I also appreciate simplicity, which is why I’m currently running the simplest damn solution you can imagine to control how my ThinkPad T430 and ASRock DeskMini X300 run: a shell script.
It also comes with some added benefits:
- self-documenting
- highly customizable
- low resource usage
These scripts are not “battle-hardened” and using them incorrectly may or may not result in hardware failure. Use at your own risk. The only guarantee I can give is that it works on my machine™.
If you’re someone who doesn’t like tinkering with their computers and wants their machines to “just work”, then this article likely isn’t for you.
ThinkPad T430
With the ThinkPad T430, the script does two things:
- control the fan speed
- preemptively throttle the CPU and GPU
This type of behaviour is optimized for mostly quiet operation. The fan is running at its lowest speed when under a small load, and the CPU/GPU throttling has an added benefit of reducing the overall power usage, too. The difference between non-turbo and turbo speeds on the Intel i7-3820QM can be as much as 13-15 watts.
To enable fan control on ThinkPads,
you need to have a file at /usr/lib/modprobe.d/thinkpad_acpi.conf
with the
following contents:
options thinkpad_acpi fan_control=1
Reboot, and you can now have full control over the fan speed in your ThinkPad!
The script itself looks like this:
#!/bin/bash
set -e
while true; do
temp=$(cat /sys/class/thermal/thermal_zone1/temp)
if ((temp > 90000)); then
echo level 7 >/proc/acpi/ibm/fan
elif ((temp > 75000)); then
echo level 5 >/proc/acpi/ibm/fan
elif ((temp > 60000)); then
echo level 3 >/proc/acpi/ibm/fan
elif ((temp > 30000)); then
echo level 1 >/proc/acpi/ibm/fan
else
echo level 0 >/proc/acpi/ibm/fan
fi
if ((temp > 97000)); then
echo 350 >/sys/class/drm/card*/gt_max_freq_mhz
echo 350 >/sys/class/drm/card*/gt_boost_freq_mhz
elif ((temp > 90000)); then
echo 650 >/sys/class/drm/card*/gt_max_freq_mhz
echo 650 >/sys/class/drm/card*/gt_boost_freq_mhz
else
echo 1250 >/sys/class/drm/card*/gt_max_freq_mhz
echo 1250 >/sys/class/drm/card*/gt_boost_freq_mhz
fi
if ((temp > 70000)); then
echo 1 >/sys/devices/system/cpu/intel_pstate/no_turbo
else
echo 0 >/sys/devices/system/cpu/intel_pstate/no_turbo
fi
sleep 0.5
done
What we’re doing is reading the temperature of the CPU package and then setting the fan speed and throttling based on that. Note that the temperatures are represented without any decimal places. A reading of 78000 means that the chip is running at 78 °C.
On my ThinkPad T430, the fan speed levels can be roughly described as such:
- level 1: lowest speed, barely audible
- level 3: audible, but tolerable
- level 5 and above: it’s probably loud enough to bother you
The second section controls the integrated GPU. What I’ve found in my testing
is that a weak integrated GPU can still chug up to 20 watts, which is a
considerable amount, especially on a laptop. The card*
wildcard is there
because the integrated GPU may change between card0
and card1
, and I’m too
lazy to make the script any smarter.
The values written there correspond to the maximum GPU clock speed. 1250
happens
to be the max clock speed (in MHz) and at that speed the GPU is using a lot of
power. 650
is the speed at which the integrated GPU is running most
efficiently. 350
is the lowest speed, and you will notice it due to the
performance being extra crappy.
With the CPU, I chose to just turn the turbo boost on or off. With turbo boost off, the CPU will top out at 2.7 GHz and around 22 watts of power usage. With turbo boost, the CPU can run anywhere between 3.4-3.7 GHz, depending on the number of cores under load. You’ll likely notice much higher CPU temperatures and power usage climbing to around 35 watts. Fast at short and bursty workloads, and yet efficient and relatively cool at sustained loads.
And at the very end, you have a simple sleep
statement. I’ve set it to half a
second, but feel free to set it as you see fit. Higher intervals might not be
that good of an idea because the script may not be able to respond quickly enough
to changing thermals, just keep that in mind.
ASRock DeskMini X300
Although I’ve opted for a more experimental setup with my home server and the ASRock DeskMini X300 is no longer running as one, I think it’s still worthwhile to share how I configured the fan control logic on this machine.
With the ASRock DeskMini X300, my goal was simple: let it run as quietly as reasonably possible without losing too much performance.
This machine was running 24/7 as a server and has a powerful 8-core AMD Ryzen 7 5700G CPU in it. One thing this script is heavily relying on is the fact that Ryzen CPU-s have well-engineered boosting logic in them, meaning that they’re already designed to run at the highest speed possible and only limit their speed if they hit power or thermal limits.
One thing you may need to do first is installing lm-sensors
and running
sudo sensors-detect
. By default the CPU fan speed controls were not properly
exposed, but after running sensors-detect
and saying yes at every prompt,
they were picked up.
The script looks like this:
#!/bin/bash
set -e
# /sys/devices/platform/nct6775.656/hwmon/hwmon3
echo 1 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2_enable
while true; do
temp=$(cat /sys/class/hwmon/hwmon4/temp1_input)
if ((temp > 94000)); then
echo 170 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
elif ((temp > 90000)); then
echo 140 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
elif ((temp > 70000)); then
echo 120 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
elif ((temp > 40000)); then
echo 30 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
else
echo 0 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
fi
sleep 5
done
The hwmon
values and which fan you’re controlling is likely different on your
own PC. Take a look at the paths found in the script and poke around while also
physically looking at your PC. Try enabling PWM control by writing to pwm*_enable
handles and then setting the fan speed. As a result of your poking, you should
see the CPU fan change its speed. If it doesn’t, try another value.
The values you can write are in the range 0-255, where the higher value corresponds to a higher speed.
I’ve limited the TDP of my CPU to 35 watts using a setting found in UEFI settings. This results in the CPU running cool and quiet even under a full load.
systemd service
To run your fan control script as a systemd service, drop the script to your
preferred location (I placed it in /root/.local/bin/fancontrol
), then create
a file /etc/systemd/system/fancontrol.service
with the following contents:
[Unit]
Description=Quick fan control software
[Service]
ExecStart=/root/.local/bin/fancontrol
[Install]
WantedBy=multi-user.target
And once that is done, enable and start your fan control service with
sudo systemctl enable --now fancontrol.service
.
If you encounter issues or something isn’t working right, check the status of
the service with systemctl status fancontrol.service
or full logs with
journalctl -u fancontrol.service
.
Conclusion
If you want something that works out of the box and does the smart stuff for you, then give this Arch Linux wiki page a look. If you’re not into that and want to easily tune the behaviour of your machine, then feel free to use these scripts as a baseline for your own experimentation.
Subscribe to new posts via the RSS feed.
Not sure what RSS is, or how to get started? Check this guide!
You can reach me via e-mail or LinkedIn.
If you liked this post, consider sharing it!