271

How can I control the computer's fan speed?

On Windows there is a wonderful program called SpeedFan.

2
  • 1
    I put simple cron solution for ATI cards here askubuntu.com/a/875241/375427 Jan 23, 2017 at 12:54
  • 4
    On linux, fancontrol is configured using pwmconfig. Use watch sensors to observe sensors under CPU and GPU load. Use those values at idle and full load to set MINTEMP and MAXTEMP respectively, with INTERVAL=1. This ramps your fans in realtime as load increases. This is the most active cooling, providing a baseline for further tweaking. Run pwmconfig once, and then edit /etc/fancontrol directly. Run sudo service fancontrol restart after each tweak. May 29, 2017 at 17:32

8 Answers 8

220

Note before starting:

This functionality depends on both your hardware and software. If your hardware doesn't support fan speed controls, or doesn't show them to the OS, it is very likely that you could not use this solution. If it does, but the software (aka kernel) doesn't know how to control it, you are without luck.


  1. Install the lm-sensors and fancontrol packages.

  2. Configure lm-sensors as follows:

    1. In terminal type sudo sensors-detect and answer YES to all YES/no questions.
      (Potentially, this can damage your system or cause system crash. For a lot of systems, it is safe. There is no guarantee that this process will not damage your system permanently, I just think that chance of such critical failure is really really low. Saving all your work for eventual crashes/freezes/restarts before handling system configuration is always good idea. If you feel unsure, read the comments and try to search a web and get some high-level overview before YES-ing everything, maybe being selective with your YES-es will still be enough)

    2. At the end of sensors-detect, a list of modules that need to be loaded will be displayed. Type "yes" to have sensors-detect insert those modules into /etc/modules, or edit /etc/modules yourself.

    3. Run sudo service kmod start This will read the changes you made to /etc/modules in step 2, and insert the new modules into the kernel.
      Note: If you're running Ubuntu 12.04 or lower, this 3rd step command should be replaced by sudo service module-init-tools restart

  3. Configure fancontrol

    1. In terminal type sudo pwmconfig . This script will stop each fan for 5 seconds to find out which fans can be controlled by which PWM handle. After script loops through all fans, you can configure which fan corresponds to which temperature.
    2. You will have to specify what sensors to use. This is a bit tricky. If you have just one fan, make sure to use a temperature sensor for your core to base the fancontrol speed on.
    3. Run through the prompts and save the changes to the default location.
    4. Make adjustments to fine-tune /etc/fancontrol and use sudo service fancontrol restart to apply your changes. (In my case I set interval to 2 seconds.)
  4. Set up fancontrol service

    1. Run sudo service fancontrol start. This will also make the fancontrol service run automatically at system startup.

In my case /etc/fancontrol for CPU I used:

Settings for hwmon0/device/pwm2:
(Depends on hwmon0/device/temp2_input) (Controls hwmon0/device/fan2_input)

INTERVAL=2
MINTEMP=40  
MAXTEMP=60  
MINSTART=150  
MINSTOP=0  
MINPWM=0  
MAXPWM=255 

and on a different system it is:

INTERVAL=10
DEVPATH=hwmon1=devices/platform/coretemp.0 hwmon2=devices/platform/nct6775.2608
DEVNAME=hwmon1=coretemp hwmon2=nct6779
FCTEMPS=hwmon2/pwm2=hwmon1/temp2_input
FCFANS=hwmon2/pwm2=hwmon2/fan2_input
MINTEMP=hwmon2/pwm2=49
MAXTEMP=hwmon2/pwm2=83
MINSTART=hwmon2/pwm2=150
MINSTOP=hwmon2/pwm2=15
MINPWM=hwmon2/pwm2=14
MAXPWM=hwmon2/pwm2=255

This man page gives some useful info on the settings and what they really do.

22
  • 176
    I tried to follow your guide, but got stuck at step three with this error: /usr/sbin/pwmconfig: There are no pwm-capable sensor modules installed
    – tamale
    Dec 12, 2011 at 15:54
  • 8
    I had the same problem with pwmconfig until I ran sudo sensors-detect Jan 1, 2012 at 12:16
  • 7
    What to do if sudo sensors-detect doesn't come up with any sensors? --> Sorry, no sensors were detected. Either your system has no sensors, or they are not supported, or they are connected to an I2C or SMBus adapter that is not supported. If you find out what chips are on your board, check lm-sensors.org/wiki/Devices for driver status.
    – H3R3T1K
    Aug 1, 2012 at 8:23
  • 5
    If you're having trouble, be sure to check your dmesg for error messages. In my case, recent kernel changes prevented me from loading the necessary driver, and I had to add acpi_enforce_resources=lax to my kernel options to get the old functionality back. See also: goo.gl/tUcr36
    – bukzor
    Dec 23, 2013 at 17:49
  • 35
    I've tried this and I can't get any further than sudo pwmconfig, because I get: "There are no pwm-capable sensor modules installed". This is after the sensors-detect command does find an 'Intel digital thermal sensor'. I've also tried that kernel parameter. Can anyone suggest a solution?
    – Sman789
    Jun 2, 2014 at 0:05
60

If you own a ThinkPad, there's a piece of software called thinkfan that does exactly this. As the name obviously suggests, it is specifically made for ThinkPads (thinkpad_acpi).

The thinkfan software is available in the standard ubuntu software repositories, but it does require a few steps to configure.

Here's an easy step-by-step guide:

http://www.lxle.net/forums/discussion/821/how-to-set-up-thinkfan-on-a-lenovo-thinkpad-t430/p1

(which is basically a translated version of this German guide: http://thinkwiki.de/Thinkfan)


Relevant Information from Post:

Step 1. Install the thinkfan software and the sensors:

sudo apt-get install thinkfan lm-sensors

Step 2. Make sure that the daemon controls the fan by editting the thinkpad.conf file:

sudo nano /etc/modprobe.d/thinkfan.conf

by adding the following line:

options thinkpad_acpi fan_control=1

Step 3. Make the daemon load automatically at start-up by editting the file:

sudo nano /etc/default/thinkfan

making sure that the START key is set to yes, i.e. there should be a line that says:

START=yes

Step 4. Detect your laptop's sensors:

sudo sensors-detect

and just choose the default answers whenever you're prompted by hitting Enter.

Step 5. Load the new modules. From ubuntu 13.10 this done by:

sudo service kmod start

while for previous versions like 13.04 you instead will need to do:

sudo service module-init-tools start

Step 6. Figure out which sensors are in use:

sensors

(the ones that indicate 0 degrees are not in use, I don't know why those are "detected" too). Remember which ones are in use.

Step 7. Find out the full paths of these sensors:

find /sys/devices -type f -name "temp*_input"

The output should be a list of paths like /sys/devices/...

Step 8. Copy-paste the paths to the sensors into the configuration file /etc/thinkpad.conf. To do this, first open up the file:

sudo nano /etc/thinkfan.conf

There should already be a line like

#sensor /proc/acpi/ibm/thermal (0, 10, 15, 2, 10, 5, 0, 3, 0, 3) 

(the #-symbol means that that line is commented out). Add a line starting with sensor (without the #-symbol) and copy-paste you first sensor. Repeat this if you have more than one sensor. For example, on my machine, the output in step 7 yields

/sys/devices/virtual/hwmon/hwmon0/temp1_input
/sys/devices/platform/thinkpad_hwmon/temp3_input
/sys/devices/platform/thinkpad_hwmon/temp4_input
/sys/devices/platform/thinkpad_hwmon/temp5_input
/sys/devices/platform/thinkpad_hwmon/temp6_input
/sys/devices/platform/thinkpad_hwmon/temp7_input
/sys/devices/platform/thinkpad_hwmon/temp1_input
/sys/devices/platform/thinkpad_hwmon/temp8_input
/sys/devices/platform/thinkpad_hwmon/temp2_input
/sys/devices/platform/coretemp.0/temp4_input
/sys/devices/platform/coretemp.0/temp2_input 

The ones that are in use in my machine are the ones in the first and the last two lines, so I added the three lines:

sensor /sys/devices/virtual/hwmon/hwmon0/temp1_input
sensor /sys/devices/platform/coretemp.0/temp4_input
sensor /sys/devices/platform/coretemp.0/temp2_input 

Step 9. Finally we can set the fan speed levels in the configuration file. Open the /etc/thinkpad.conf file if it wasn't open already.

sudo nano /etc/thinkfan.conf

The fan levels I use on my ThinkPad x201 are:

(0, 0, 51)
(1, 50, 52)
(2, 51, 55)
(3, 54, 58)
(4, 56, 63)
(5, 60, 70)
(6, 66, 79)
(7, 74, 92)
(127, 85, 32767) 

The last line ensures full fan speed (127 = "disengaged" i.e. unregulated). You can fiddle with these levels to fit your needs/wishes, but PLEASE BE CAREFUL!

Step 10. Reboot. Everything should work now. In order to check whether thinkpad is runnning correctly, use

sudo thinkfan -n

which starts thinkfan in verbose mode. You might want to stop the thinkfan daemon first:

sudo /etc/init.d/thinkfan stop

If you want to start the thinkfan daemon again, type:

sudo /etc/init.d/thinkfan start

Just to be complete, my /etc/thinkfan.conf configuration file is:

# IMPORTANT:
#
# To keep your HD from overheating, you have to specify a correction value for
# the sensor that has the HD's temperature. You need to do this because
# thinkfan uses only the highest temperature it can find in the system, and
# that'll most likely never be your HD, as most HDs are already out of spec
# when they reach 55 °C.
# Correction values are applied from left to right in the same order as the
# temperatures are read from the file.
#
# For example:
# sensor /proc/acpi/ibm/thermal (0, 0, 10)
# will add a fixed value of 10 °C the 3rd value read from that file. Check out
# http://www.thinkwiki.org/wiki/Thermal_Sensors to find out how much you may
# want to add to certain temperatures.

# Syntax:
# (LEVEL, LOW, HIGH)
# LEVEL is the fan level to use (0-7 with thinkpad_acpi)
# LOW is the temperature at which to step down to the previous level
# HIGH is the temperature at which to step up to the next level
# All numbers are integers.
#

# I use this on my T61p:
#sensor /proc/acpi/ibm/thermal (0, 10, 15, 2, 10, 5, 0, 3, 0, 3)

#(0, 0, 55)
#(1, 48, 60)
#(2, 50, 61)
#(3, 52, 63)
#(4, 56, 65)
#(5, 59, 66)
#(7, 63, 32767)


# My settings for my ThinkPad X201: (kris)

sensor /sys/devices/virtual/hwmon/hwmon0/temp1_input
sensor /sys/devices/platform/coretemp.0/temp4_input
sensor /sys/devices/platform/coretemp.0/temp2_input

(0, 0, 51)
(1, 50, 52)
(2, 51, 55)
(3, 54, 58)
(4, 56, 63)
(5, 60, 70)
(6, 66, 79)
(7, 74, 92)
(127, 85, 32767)
18
  • 3
    One comment: you want to have larger overlap of temperature values as in the table commented out. Especially in the 45-55 range. Otherwise, the fan speed keeps flipping. That's very disturbing. (Constant fan noise does not disturb so much as fluctuating noise.)
    – HongboZhu
    Feb 6, 2015 at 16:36
  • 11
    We (the linux community) really suck at UX. :( Setting a fan speed shouldn't be a 10 step process.
    – Alexander
    Feb 28, 2017 at 22:53
  • 4
    @Alexander If you feel strongly about this, you're free to write and maintain a little program to automate this process.
    – Kris
    Mar 2, 2017 at 22:14
  • 2
    @Kris I'd be more inclined to modify the original program to not have such a nonsensical configuration process. Alas, I dont have a thinkpad, I was just passing by this post looking for thr fan control options available out there.
    – Alexander
    Mar 2, 2017 at 22:18
  • 3
    How do you know which sensors are used by your machine and which ones aren't?
    – sequence
    Aug 8, 2021 at 8:48
23

For several Dell computers you can install i8kutils package:

sudo apt install i8kutils

If you have a non-tested Dell (like Dell XPS 14z, Dell XPS 15 9550 or even Alienware M15 R4), you might have to force loading of kernel module:

sudo modprobe i8k force=1

If you want to control the fans you might need to follow a more thorough guide: Fan switches between full speed and off on Dell G5

9
  • 2
    I have Dell XPS 14 L421X. <code>sensors</code> showed no fans. after installing i8kutils package, I get sensors coretemp-isa-0000 Adapter: ISA adapter Physical id 0: +54.0°C (high = +87.0°C, crit = +105.0°C) Core 0: +50.0°C (high = +87.0°C, crit = +105.0°C) Core 1: +52.0°C (high = +87.0°C, crit = +105.0°C) i8k-virtual-0 Adapter: Virtual device Right Fan: 0 RPM CPU: +55.0°C Jan 8, 2017 at 22:21
  • Does this work for hp?
    – Sensebe
    Sep 2, 2017 at 7:35
  • 1
    @immortal-player , I think it won't. The kernel module is specifically designed for Dell machines.
    – morhook
    Sep 10, 2017 at 13:06
  • 1
    You will have three comands via the apt package: i8kctl, i8kfan, i8kmon @Michael
    – morhook
    Jun 7, 2020 at 15:18
  • 1
    Nice answer @KyleBaker ! I've done some of the things you mention there on my Dell XPS 15 9550
    – morhook
    Mar 3, 2021 at 19:49
10

Here is an updated answer, based on the answer recommending thinkfan but working with Ubuntu 19.04, and also on other computers than just ThinkPads.

1. Setup

The instructions below apply to any ThinkPad that has the /proc/acpi/ibm/fan and /proc/acpi/ibm/thermal "legacy" devices. This avoids having to install the lm-sensors package.

However, you can still opt to install lm-sensors. It will give you more temperature sensors and more fine-grained control over your fans, namely in 256 PWM steps rather than the fixed steps "0-7 and 127" with the legacy devices. Also, since thinkfan now can work with lm-sensors sensors, it is no longer specific for IBM / Lenovo ThinkPad computers. Please refer to man thinkfan for using these "extended" sensor devices. Also, there is up-to-date information in the German Thinkwiki.

  1. Install the required package:

    sudo apt install thinkfan
    
  2. Add the following line to /etc/modprobe.d/thinkfan.conf:

    options thinkpad_acpi fan_control=1
    
  3. Configure the thinkfan service to start automatically at system start:

    1. Add a line START=yes to /etc/default/thinkfan

    2. Execute the following command (source):

      sudo systemctl enable thinkfan.service
      
  4. Adapt /etc/thinkfan.conf with the right pointers to devices and the fan levels you want. The comments there provide documentation. On any Thinkpad with the legacy fan and thermal devices, the following should provide a good starting point (also should be safe for the hard disk, see comments in the file for more information). Note that this uses new keywords – the ones in the other answer are now deprecated.

    tp_fan /proc/acpi/ibm/fan
    tp_thermal /proc/acpi/ibm/thermal
    
    (0, 0, 47)
    (1, 43, 52)
    (2, 48, 55)
    (3, 51, 58)
    (4, 54, 63)
    (5, 59, 70)
    (6, 66, 79)
    (7, 74, 92)
    (127, 85, 32767) 
    

2. Test

To test your setup (after a reboot), you can do as follows:

  • In one terminal window, run thinkfan in non-daemonized mode so you can see its messages:

    sudo service thinkfan stop && sudo thinkfan -n
    
  • In another terminal window, keep an eye on the current temperature values, supplying your temperature probe device if different:

    while true; do sleep 1; cat /proc/acpi/ibm/thermal; done
    
  • In yet another terminal window, make some heat in the CPU (and interrupt it with Ctrl + C in case something in the thermal management does not function as expected):

    sudo apt install stress-ng
    stress-ng --cpu-1
    
5
  • 1
    Awesome. This solved it for my T490 with Pop_OS 19.04. The fan still comes on, but not the whole time.
    – Nitai
    Sep 19, 2019 at 14:56
  • 1
    Newer ThinkPad devices don't have /proc/acpi/ibm/thermal, so it's necessary to do some extra work for that case (conveniently listed on that wonderful German ThinkWiki page). I seem to have gotten it working for me, so I may update your answer or create another one. Thanks!
    – Cliff
    Oct 10, 2019 at 1:06
  • 2
    Cliff, what is the correct folder for this? I'm getting this error: /proc/acpi/ibm/thermal: No such file or directory add_sensor: Error getting temperature. Refusing to run without usable config file!
    – Kendor
    Oct 28, 2019 at 16:17
  • 1
    OK.. I figured it out. I used this for my T480: wmon /sys/devices/platform/coretemp.0/hwmon/hwmon1/temp1_input hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon1/temp5_input hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon1/temp2_input (0, 0, 47) (1, 43, 52) (2, 48, 55) (3, 51, 58) (4, 54, 63) (5, 59, 70) (6, 66, 79) (7, 74, 92) (127, 85, 32767)
    – Kendor
    Oct 28, 2019 at 16:58
  • I could not find /etc/modprobe.d/thinkfan.conf, only /etc/default/thinkfan.conf
    – Paul Jones
    Aug 23, 2020 at 17:27
9

Install and configure the lm-sensors and fancontrol packages:

sudo apt-get install lm-sensors fancontrol

Documentation for configuring them is available on their man pages.

This is a function that is supposed to be provided by a ACPI-compliant BIOS, but it seems that most motherboard vendors don't bother to follow the standard.

4
  • 20
    Can you provide more detailed instructions on how to install and use them? Jan 18, 2011 at 14:43
  • 1
    You can find that in the man page, or at www.lm-sensors.org.
    – psusi
    Jan 18, 2011 at 16:39
  • 61
    I know, I just thought this answer could be better :) Jan 19, 2011 at 13:22
  • The link that @psusi gave doesn't work, the new link is here: github.com/groeck/lm-sensors Apr 12, 2018 at 0:13
7

Try looking in the UEFI setup (or BIOS setup) of your PC. There might be a setting for fan control where you can put your fan into Silent Mode, Performance Mode, Full Speed, Customize, etc.

How to access the UEFI settings: Reboot your PC and the key for entering them will likely show up in one of the corners (e.g. Press F2 to enter UEFI setup)

ASRock UEFI setup for fan control

4
  • 5
    This is AskUbuntu not AskUEFI :)
    – Maarten
    Dec 13, 2018 at 17:39
  • 14
    @Maarten Sure, this solution is OS-independent, simple and does not require installing additional software.
    – xjcl
    Dec 3, 2019 at 15:24
  • @xjcl The link in your answer is dead. Feb 20, 2020 at 7:54
  • Wayback machine to the rescue!
    – xjcl
    Jun 27, 2020 at 19:25
4

The script I run every few seconds to keep my Dell server cool and quiet. Update the get_temp for your hardware.

#!/bin/env python3

import os
import json

MIN_FAN = 5
MAX_FAN = 100

MIN_TEMP = 50 # fans at min at this temp
MAX_TEMP = 80 # fans at max at this temp

TEMP_POW = 3 # decrease for cooler server, increase for quiter

def get_temp():
    sensors = json.loads(os.popen('/usr/bin/sensors -j').read())
    temp0 = sensors["coretemp-isa-0000"]["Package id 0"]["temp1_input"]
    temp1 = sensors["coretemp-isa-0001"]["Package id 1"]["temp1_input"]
    return max(temp0, temp1)

def determine_fan_level(temp):
    x = min(1, max(0, (temp - MIN_TEMP) / (MAX_TEMP - MIN_TEMP)))
    return int(min(MAX_FAN, max(MIN_FAN, pow(x, TEMP_POW)*(MAX_FAN-MIN_FAN) + MIN_FAN)))

def set_fan(fan_level):
    # manual fan control
    os.system("ipmitool raw 0x30 0x30 0x01 0x00")
    # set fan level
    cmd = "ipmitool raw 0x30 0x30 0x02 0xff " + hex(fan_level)
    os.system(cmd)

temp = get_temp()
fan = determine_fan_level(temp)
print("temp", temp, "fan", fan)
set_fan(fan)
2
  • Traceback (most recent call last): File "/home/user/tmp/fan.py", line 31, in <module> temp = get_temp() ^^^^^^^^^^ File "/home/user/tmp/fan.py", line 17, in get_temp temp1 = sensors["coretemp-isa-0001"]["Package id 1"]["temp1_input"] ~~~~~~~^^^^^^^^^^^^^^^^^^^^^ KeyError: 'coretemp-isa-0001'
    – alfC
    Feb 6, 2023 at 23:16
  • @alfC read "Update the get_temp for your hardware." Obviously your stuff is named differently.
    – sjngm
    Oct 12, 2023 at 6:31
2

For a HP laptop the following scripts (which I'm not an author of) seem to work for me, but...

... link to the license first (it's too big to be included here): https://www.gnu.org/licenses/gpl-3.0.txt .

#!/usr/bin/perl -w

# Copyright (C) 2007  Michael Kurz     michi.kurz (at) googlemail.com
# Copyright (C) 2007  Petr Tomasek     tomasek (#) etf,cuni,cz
# Copyright (C) 2007  Carlos Corbacho  cathectic (at) gmail.com
#
# Version 0.6.1 (2007-11-08)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


require 5.004;

use strict;
use Fcntl;
use POSIX;
use File::Basename;

sub initialize_ioports
{
  sysopen (IOPORTS, "/dev/port", O_RDWR)
    or die "/dev/port: $!\n";
  binmode IOPORTS;
}

sub close_ioports
{
  close (IOPORTS)
    or print "Warning: $!\n";
}



sub inb
{
  my ($res,$nrchars);
  sysseek IOPORTS, $_[0], 0 or return -1;
  $nrchars = sysread IOPORTS, $res, 1;
  return -1 if not defined $nrchars or $nrchars != 1;
  $res = unpack "C",$res ;
  return $res;
}

# $_[0]: value to write
# $_[1]: port to write
# Returns: -1 on failure, 0 on success.
sub outb
{
  if ($_[0] > 0xff)
  {
    my ($package, $filename, $line, $sub) = caller(1);
    print "\n*** Called outb with value=$_[1] from line $line\n",
          "*** (in $sub). PLEASE REPORT!\n",
          "*** Terminating.\n";
    exit(-1);
  }
  my $towrite = pack "C", $_[0];
  sysseek IOPORTS, $_[1], 0 or return -1;
  my $nrchars = syswrite IOPORTS, $towrite, 1;
  return -1 if not defined $nrchars or $nrchars != 1;
  return 0;
}

sub wait_write
{
    my $i = 0;
    while ((inb($_[0]) & 0x02) && ($i < 10000)) {
        sleep(0.01);
        $i++;
    }
    return -($i == 10000);
}

sub wait_read
{
    my $i = 0;
    while (!(inb($_[0]) & 0x01) && ($i < 10000)) {
        sleep(0.01);
        $i++;
    }
    return -($i == 10000);
}

sub wait_write_ec
{
    wait_write(0x66);
}

sub wait_read_ec
{
    wait_read(0x66);
}

sub send_ec
{
    if (!wait_write_ec()) { outb($_[0], 0x66); }
    if (!wait_write_ec()) { outb($_[1], 0x62); }
}

sub write_ec
{
    if (!wait_write_ec()) { outb(0x81, 0x66 ); }
    if (!wait_write_ec()) { outb($_[0], 0x62); }
    if (!wait_write_ec()) { outb($_[1], 0x62); }
}

sub read_ec
{
    if (!wait_write_ec()) { outb(0x80, 0x66 ); }
    if (!wait_write_ec()) { outb($_[0], 0x62); }
    if (!wait_read_ec())  { inb(0x62); }
}

sub write_kc
{
    if (!wait_write(0x64)) { outb($_[0], 0x64); }
    if (!wait_write(0x64)) { outb($_[1], 0x60); }
}

sub print_regs
{
    initialize_ioports();

    my @arr = ("00","10","20","30","40","50","60","70","80","90","A0","B0","C0","D0","E0","F0", "");

    my $i = 0;
    my $t = 0;
    print "\n  \t00\t01\t02\t03\t04\t05\t06\t07\t|\t08\t09\t0A\t0B\t0C\t0D\t0E\t0F\n";
    print "  \t__\t__\t__\t__\t__\t__\t__\t__\t|\t__\t__\t__\t__\t__\t__\t__\t__\n";
    print "00 |\t";
    for ($i = 0; $i < 256; $i++)
    {
        $t = read_ec($i);
        print $t;
        print "\t";
        if ((($i + 1) % 8) == 0){
            if ((($i + 1) % 16) == 0) {
                if ($i != 255) { print "\n$arr[(($i-(($i + 1) % 16)) / 16) + 1] |\t"; }
            } else {
                print "|\t";
            }
        }
    }
    
    print "\n";
    
    close_ioports();
}

sub write_temp
{
    initialize_ioports();
    write_ec($_[0],$_[1]);
    close_ioports();
}

sub testnum
{
    my $i;
    for ($i = 0; $i<256;$i++) {
        if ($_[0] eq "$i") { return 1 };
    }
    return 0;
}

my $ii;

if (!$ARGV[0]){
        print "wrong arguments!\n";
    print "usage:\n";
    print "\'probook_ec regs\' \t\t\t\tdumps all ec registers\n";
    print "\'probook_ec ledon\' \t\t\t\tswitch on 'mail LED' (WMID)\n";
    print "\'probook_ec ledoff\' \t\t\t\tswitch off 'mail LED' (WMID)\n";
    print "\'probook_ec getled\' \t\t\t\tget 'mail LED' status (WMID)\n";
    print "\'probook_ec getled2\' \t\t\t\tget 'mail LED' status(AMW0)\n";
    print "\'probook_ec getwireless\' \t\t\t\tget 'wireless' status (AMW0)\n";
    print "\'probook_ec gettouch\' \t\t\t\tis the touchpad disabled?\n";
    print "\'probook_ec setfanthresh <temp>\' \t\t\t\tset temperature threshhold to <temp>, DANGEROUS!\n";
    print "\'probook_ec getfanthresh\' \t\t\t\tget temperature threshhold\n";
    print "\'probook_ec <temp-number> <temperature>\' \tfor setting a temperature\n";
    print "where <temp-number> is from 0-7, and <temperture> is from 0-255\n";
    print "\'probook_ec ?= <reg>\' \t\tQuery register's value\n";
    print "\'probook_ec := <reg> <val>\' \tSet register's value\n";
    print "\'probook_ec +f <reg> <val>\' \tOr register's value with val (to set flags)\n";
    print "\'probook_ec -f <reg> <val>\' \tAnd register's value with ~val (to clear flags)\n";
    print "\'forcekc\' \tTry all possible values on writeable RAM of keyboard controller\n";
    print "\'kcw <cmd> <val>\' \tWrite a command and a value to the keyboard controller\n";
} elsif ($ARGV[0] eq "regs") {
    print_regs();
} elsif ($ARGV[0] eq "getled") {
    # TM2490 only (WMID)
    initialize_ioports();
    if (read_ec(0x9f)&0x01) {
        print "Mail LED on\n";
    } else {
        print "Mail LED off\n"; }
    close_ioports();
} elsif ($ARGV[0] eq "getled2") {
    # Aspire 5020 only (AMW0)
    initialize_ioports();
    if (read_ec(0x0A)&0x80) {
        print "Mail LED on\n";
    } else {
        print "Mail LED off\n"; }
    close_ioports();
} elsif ($ARGV[0] eq "getwireless") {
    # Aspire 5020 only (AMW0)
    initialize_ioports();
    if (read_ec(0x0A)&0x4) {
        print "Wireless on\n";
    } else {
        print "Wireless off\n"; }
    close_ioports();
} elsif ($ARGV[0] eq "gettouch") {
    # TM2490 only - needs testing
    initialize_ioports();
    if (read_ec(0x9e)&0x08) {
        print "touchpad disabled\n";
    } else {
        print "touchpad enabled\n"; }
    close_ioports();
} elsif ($ARGV[0] eq "?=") {
    initialize_ioports();
    my $r = hex($ARGV[1]);
    printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r));
    close_ioports();
} elsif ($ARGV[0] eq ":=") {
    initialize_ioports();
    my $r = hex($ARGV[1]);
    my $f = hex($ARGV[2]);
    my $val = read_ec($r);
    #printf("REG[0x%02x] == 0x%02x\n", $r, $val);
    #printf("REG[0x%02x] := 0x%02x\n", $r, $f);
        write_ec( $r, $f);
    #printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r));
    #echo "$r:$f\n" >> regs_set
    close_ioports();
} elsif ($ARGV[0] eq "FANCTL") {
        initialize_ioports();
        write_ec(0x22,1);
        write_ec(0x26,0x1F);
        #write_ec(0x2F,0xFF);
        #printf("REG[0x%02x] == 0x%02x\n", 0x1E, read_ec(0x1E));
        close_ioports();
        #`notify-send FANOFF`;
} elsif ($ARGV[0] eq "FANOFF") {
    initialize_ioports();
    write_ec(0x22,1);
    write_ec(0x26,0x1F);
    write_ec(0x2F,0xFF);
    #printf("REG[0x%02x] == 0x%02x\n", 0x1E, read_ec(0x1E));
    close_ioports();
        #`notify-send FANOFF`;
} elsif ($ARGV[0] eq "FANRST") {
        initialize_ioports();
        write_ec(0x22,0x01);
    write_ec(0x26,0x00);
        #printf("REG[0x%02x] == 0x%02x\n", 0x22, read_ec(0x22));
        close_ioports();
} elsif ($ARGV[0] eq "+f") {
    initialize_ioports();
    my $r = hex($ARGV[1]);
    my $f = hex($ARGV[2]);
    my $val = read_ec($r);
    printf("REG[0x%02x] == 0x%02x\n", $r, $val);
    printf("REG[0x%02x] := 0x%02x\n", $r, $val | $f);
        write_ec( $r, $val | $f);
    printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r));
    close_ioports();
} elsif ($ARGV[0] eq "-f") {
    initialize_ioports();
    my $r = hex($ARGV[1]);
    my $f = hex($ARGV[2]);
    my $val = read_ec($r);
    printf("REG[0x%02x] == 0x%02x\n", $r, $val);
    printf("REG[0x%02x] := 0x%02x\n", $r, $val & ~$f);
        write_ec( $r, $val & ~$f);
    printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r));
    close_ioports();
} elsif ($ARGV[0] eq "ledon") {
    # TM2490 only - needs testing
    initialize_ioports();
    if (!wait_write(0x64)) { outb(0x59, 0x64); }
    if (!wait_write(0x64)) { outb(0x92,   0x60); }
    close_ioports();
} elsif ($ARGV[0] eq "ledoff") {
    # TM2490 only - needs testing
    initialize_ioports();
    if (!wait_write(0x64)) { outb(0x59, 0x64); }
    if (!wait_write(0x64)) { outb(0x93,   0x60); }
    close_ioports();
} elsif ($ARGV[0] eq "getfanthresh") {
    initialize_ioports();
    $ii=read_ec(0xa9);
    close_ioports();
        print "Temperature threshhold: $ii (celsius)\n";
} elsif (($ARGV[0] eq "setfanthresh") && testnum($ARGV[1])) {
    write_temp(0xA9,$ARGV[1]);
} elsif ($ARGV[0] eq "setbright") {
    # Aspire 5020 only (AMW0)
    if ($ARGV[1] >= 0 && $ARGV[1] <= 15) {
        write_temp(0x83, $ARGV[1]);
    } else {
        print "second argument must be a number between 0 and 15\n";
    }
} elsif ($ARGV[0] eq "forcekc") {
    # Be smart - we only send the commands for writing to keyboard RAM
    initialize_ioports();
    my ($kbdata, $cont, $kbreg);
    for ($kbreg = 0x40; $kbreg <= 0x5f; $kbreg++) {
        for ($kbdata = 0; $kbdata < 256; $kbdata++) {
            write_kc($kbreg, $kbdata);

            print sprintf("%0#4x", $kbreg), ", ", sprintf("%0#4x", $kbdata), "\n";
            print "Continue? y/n: ";
            $cont = <STDIN>;
            if ($cont eq "n") {
                last;
            }
        }
    }
    close_ioports();
} elsif ($ARGV[0] eq "kcw") {
    initialize_ioports();
    write_kc($ARGV[1], $ARGV[2]);
    close_ioports();
} else {
    print "wrong arguments!\n";
}
#!/bin/bash

# Copyright (C) 2017  Gregor Bonney
#
# Version 2.0
#
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

LOGFILE=/var/log/fanspeed.log
ECFILE=/etc/fanspeed/probook_ec.pl
COUNTER=0

######################################################

initFan()
{
  setFan 80
#  sleep 2
  $ECFILE FANOFF
}

setFan()
{
  SPEED=$1
  $ECFILE := 0x2F $SPEED
}

getTempAvg()
{
  CUR=0
  CPUTEMP=0

  while [ $CUR -lt $INTERVAL ]
  do
    #TMP=$(sensors | grep Physical | cut -b 18-19)
    TMP=$(cat /sys/class/hwmon/hwmon*/device/temp1_input | cut -b1-2)
    #TMP=$(sensors | grep "Physical" | awk '{print $4}' | tr -d '+°C' | cut -b 1-2)
    ((CPUTEMP=$CPUTEMP+$TMP))
    ((CUR=$CUR+1))
    sleep 1
  done
  ((CPUTEMP=$CPUTEMP/$INTERVAL))
}

detectNewSpeed()
{
  ((COUNTER=$COUNTER+$INTERVAL))

  if [ $CPUTEMP -le $TEMPFANSTARTSPEED ]
  then
    # CPU COOLED DOWN
     ((INTERVAL=$STEPS))
    if [ $LASTSPEED -gt 75 ]
    then
      if [ $COUNTER -gt $THROTTLEOFF ]
      then
        # STOP FAN AFTER CPU COOLED DOWN
        NEWSPEEDF=FF
      fi
    elif [ "$NEWSPEEDF" == "FF" ]
    then
      NEWSPEEDF=FF
    else
      ((NEWSPEEDF=$NEWSPEEDF+$INTERVAL))
    fi
  elif [ $CPUTEMP -gt $TEMPFANMAXSPEED ]
  then
    # CPU VERY HOT!!!
    NEWSPEEDF=0
    resetCounter
    ((INTERVAL=10))
  else
    # CPU TEMP IS BETWEEN START AND MAX SPEED
    if [ $CPUTEMP -le $LASTTEMP ]
    then
      if [ $COUNTER -gt $THROTTLESEC ]
      then
        #calculate fanspeed after $THROTTLESEC seconds
        calculateFanSpeed
        resetCounter
      fi
    else
      # CPU TEMP RAISED -> CALCULATE AGILITY
      calculateFanSpeed
      calculateNextInterval
      resetCounter
    fi
  fi

  echo $TEMPFANSTARTSPEED:$CPUTEMP:$TEMPFANMAXSPEED:$NEWSPEEDF:$NEXTINT:$INTERVAL:$THROTTLESEC:$COUNTER
  #printf "TMIN:%d TCUR:%d TMAX:%d FSPEED:%s SLEEP:%d CNT:%d\n" $TEMPFANSTARTSPEED $CPUTEMP $TEMPFANMAXSPEED $NEWSPEEDF $INTERVAL $COUNTER

  if [ "$LASTSPEED" != "$NEWSPEEDF" ]
  then
    setFan $NEWSPEEDF
  fi

  ((LASTTEMP=$CPUTEMP))
  ((LASTSPEED=$NEWSPEEDF))
}

calculateFanSpeed() {
  ((LASTSPEED=$NEWSPEEDF))
  ((NEWSPEEDF=($CPUTEMP-$TEMPFANMAXSPEED)*($CPUTEMP+$TEMPFANMAXSPEED)))
  ((NEWSPEEDF=$NEWSPEEDF*-24/1000))
  #if [ "$LASTSPEED" != "FF" ]; then
  #  ((TST=$LASTSPEED+$INTERVAL))
  #  if [ $TST -le $NEWSPEEDF ]
  #  then
  #    ((NEWSPEEDF=$LASTSPEED+$INTERVAL))
  #  fi
  #fi
}

resetCounter() {
  ((COUNTER=0))
}

calculateNextInterval() {
  #calculate next interval
  NEXTINT=0
  for i in `seq 0 $STEPS`;
  do
    if [ $NEXTINT -le $CPUTEMP ]
      then
      ((NEXTINT=($TEMPFANSTARTSPEED+(($TEMPFANMAXSPEED-$TEMPFANSTARTSPEED)*$i/$STEPS))))
      ((INTERVAL=1+$STEPS-$i))
    fi
  done
}

##################################################


sleep 10

initFan

#use the average temperature of $INTERVAL seconds
INTERVAL=5
#throttle down after $THROTTLESEC seconds
THROTTLESEC=30
#set fan off after on lowest setting for $THROTTLEOFF seconds
THROTTLEOFF=80
#temperatures in celsius
TEMPFANSTARTSPEED=52 #56
TEMPFANMAXSPEED=80

STEPS=8


NEWSPEEDF=74
LASTTEMP=55
LASTSPEED=75

while [ true ]
do
  getTempAvg
  detectNewSpeed
done

What I've modified in "fanslow.sh" though was at least:

  • Changed ECFILE=/etc/fanspeed/probook_ec.pl to match my path.
  • Added TMP=$(sensors | grep "Package id 0:" | awk '{print $4}' | tr -d '+°C' | cut -b 1-2) after #TMP=$(sensors | grep "Physical" | awk '{print $4}' | tr -d '+°C' | cut -b 1-2) and commented out (added "#" at the beginning of the line) a default TMP=$(cat /sys/class/hwmon/hwmon*/device/temp1_input | cut -b1-2).
  • Changed value in TEMPFANSTARTSPEED=52 #56 at the end of the script.
  • Changed value in TEMPFANMAXSPEED=80 at the end of the script.

There is one more script that I haven't used I believe, but maybe it'll end up being helpful for someone:

#! /bin/sh
#
# Copyright (C) 2017  Gregor Bonney
#
# Version 2.0
#
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

case "$1" in
start)
echo "Starting " $0

cd /etc/fanspeed/
./fanslow.sh >> /var/log/fanspeed &>> /dev/null &

;;
stop)
echo "Stopping " $0
killall fanslow.sh
/etc/fanspeed/probook_ec.pl FANRST
#
# Do something to stop the target platform here
#
;;
restart)
echo "Restarting " $0
$0 stop
$0 start
;;
status)
echo  "Status Not Implemented"
#
# Do something to process status checks here.
#
;;
*)
echo "Usage: /etc/init.d/$0 {start|stop}"
exit 1
;;
esac
exit 0

Source: https://ubuntuforums.org/showthread.php?t=2008756&s=43a5ddf387823582cf47e3b25dbab100&p=12422983#post12422983 , https://ubuntuforums.org/showthread.php?t=2008756&page=4&p=13447421#post13447421 .

2
  • 1
    It works! Amazing, thank you very much for these links. Have an oldie HP 8460p, can turn it into a silent home server at last :). You also mentioned you changed TEMPFANSTARTSPEED & TEMPFANMAXSPEED, don't recall which values did you choose?
    – A S
    May 3, 2022 at 17:36
  • 1
    @AS , about the specific values... I've experienced with that. To be honest I don't really use the script right now, but it seems I ended up with TEMPFANSTARTSPEED=72 (but I've tried more: 70, 68, 80, 52, 56 possibly at least) and TEMPFANSTARTSPEED=72 (similarly: 80, 0, 50). I believe it's worth checking out what temperature is displayed as "crit" when You run sensors as I think it may vary from processor to processor. And You're welcome, I'm really glad it helped, to God be the glory. ^^ Thank You for letting me know.
    – kcpr
    May 3, 2022 at 18:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .