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:
- 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.
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
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
card1, and I’m too
lazy to make the script any smarter.
The values written there correspond to the maximum GPU clock speed.
to be the max clock speed (in MHz) and at that speed the GPU is using a lot of
650 is the speed at which the integrated GPU is running most
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
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
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.
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
/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.
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.
If you prefer to share your thoughts on this post privately, just send me an e-mail!
Places where you can discuss this post: