Difference between revisions of "Talk:Fan control scripts"

From ThinkWiki
Jump to: navigation, search
(updated bash script for unpatched kernels)
(updated bash script for unpatched kernels)
Line 35: Line 35:
  
 
===updated bash script for unpatched kernels===
 
===updated bash script for unpatched kernels===
 +
Moved to the [[ACPI fan control script|article page]], after joint development by [[User:Spiney|Spiney]] and [[User:Thinker|Thinker]].
  
Feedback welcome.
 
 
(see below)
 
 
BTW, Thinker, I couldn't get the backgrounding of a function to work, and at the moment I'm not quiet sure that's even possible without it exiting once the main script exits.
 
 
If you think it's fine otherwise (after all your the author of the original script) we might put it into the article.
 
 
--[[User:Spiney|spiney]] 19:01, 8 Nov 2005 (CET)
 
----
 
 
How about this? Used non-exec backgrounding, option to kill daemon, handling of initial (unknown) state and a few cleanups.
 
 
<pre>
 
#!/bin/bash
 
 
# This script dynamically controls fan speed on some ThinkPad models
 
# according to user-defined temperature thresholds.  It implements its
 
# own decision algorithm, overriding the ThinkPad embedded
 
# controller. It also implements a workaround for the fan noise pulse
 
# experienced every few seconds on some ThinkPads.
 
#
 
# WARNING: This script relies on undocumented hardware features and
 
# overrides nominal hardware behavior. It may thus cause arbitrary
 
# damage to your laptop or data. Watch your temperatures!
 
#
 
# This file is placed in the public domain and may be freely distributed.
 
 
LEVELS=(    0      2      4      7)  # Fan speed levels
 
UP_TEMPS=(      52    60    68  )  # Speed increase trip points
 
DOWN_TEMPS=(  48    56    64    )  # Speed decrease trip points
 
 
ANTIPULSE=( 0      1      0      0)  # Prevent fan pulsing noise at this level
 
                                    #  (this also prevents fan speed updates)
 
 
IBM_ACPI=/proc/acpi/ibm
 
PID_FILE=/var/run/tp-fancontrol.pid
 
INTERVAL=3
 
VERBOSE=true
 
DRY_RUN=false
 
DAEMONIZE=false
 
AM_DAEMON=false
 
KILL_DAEMON=false
 
SYSLOG=false
 
LOGGER=/usr/bin/logger
 
 
usage() {
 
    echo "Usage: $0 [OPTION]..."
 
    echo
 
    echo "Available options:"
 
    echo "  -t    test mode"
 
    echo "  -q    quiet mode"
 
    echo "  -d    daemon mode, go into background (implies -q)"
 
    echo "  -l    log to syslog"
 
    echo "  -p    pid file location for daemon mode, default: $PID_FILE"
 
    echo "  -k    kill daemon (ignores all but -p)"
 
    exit 1
 
}
 
 
while getopts 'qtdlp:kh' OPT; do
 
    case "$OPT" in
 
        t) # test mode
 
            DRY_RUN=true
 
            ;;
 
        q) # quiet mode
 
            VERBOSE=false
 
            ;;
 
        d) # go into background and daemonize
 
            DAEMONIZE=true
 
            ;;
 
        l) # log to syslog
 
            SYSLOG=true
 
            ;;
 
        p) # different pidfile
 
            PID_FILE="$OPTARG"
 
            ;;
 
        k) # kill daemon
 
            KILL_DAEMON=true
 
            ;;
 
        h) # short help
 
            usage
 
            ;;
 
        \?) # error
 
            usage
 
            ;;
 
    esac
 
done
 
[[ $OPTIND -gt $# ]] || usage  # no non-option args
 
 
# no logger found, no syslog capabilities
 
$SYSLOG && [[ ! -x $LOGGER ]] && SYSLOG=false
 
 
if $DRY_RUN; then
 
    echo "$0: Dry run, will not change fan state."
 
    VERBOSE=true
 
    DAEMONIZE=false
 
fi
 
 
thermometer() { # output list of temperatures
 
    read X Y < $IBM_ACPI/thermal
 
    if ! [[ "$X" == "temperatures:" ]]; then
 
        echo "$0: Bad temperatures: $X $Y" >&2
 
        exit 1
 
    fi
 
    echo "$Y";
 
}
 
 
speedometer() { # output fan speed RPM
 
    sed -n 's/^speed:[ \t]*//p' $IBM_ACPI/fan
 
}
 
 
setlevel() { # set fan speed level
 
    $DRY_RUN || echo 0x2F $1 > $IBM_ACPI/ecdump
 
}
 
 
cleanup() { # clean up after work
 
    $AM_DAEMON && rm -f $PID_FILE 2> /dev/null
 
    $SYSLOG && $LOGGER -t "`basename $0`[$$]" \
 
              "Shutting down, switching to automatic fan control"
 
    $DRY_RUN || echo enable > $IBM_ACPI/fan
 
}
 
 
control_fan() {
 
    # Enable the fan in default mode if anything goes wrong:
 
    set -e -E -u
 
    trap "cleanup; exit 2" HUP INT ABRT QUIT SEGV TERM
 
    trap "cleanup" EXIT
 
 
    IDX=0
 
    MAX_IDX=$(( ${#LEVELS[@]} - 1 ))
 
    SETTLE=0
 
    FIRST=true
 
    $SYSLOG && $LOGGER -t "`basename $0`[$$]" "Starting dynamic fan control"
 
 
    # Control loop:
 
    while true; do
 
        TEMPS=`thermometer`
 
        $VERBOSE && SPEED=`speedometer`
 
   
 
        # Calculate new level
 
        NEWIDX=$IDX
 
        DOWN=$(( IDX > 0 ))
 
        for TEMP in $TEMPS; do
 
            # Increase speed as much as needed
 
            while [[ $NEWIDX -lt $MAX_IDX ]] &&
 
                  [[ $TEMP -ge ${UP_TEMPS[$NEWIDX]} ]]; do
 
                (( NEWIDX ++ ))
 
                DOWN=0
 
            done
 
            # Allow decrease (by one index)?
 
            if [[ $DOWN == 1 ]] &&
 
              [[ $TEMP -gt ${DOWN_TEMPS[$(( IDX - 1 ))]} ]]; then
 
                DOWN=0
 
            fi
 
        done
 
        if [[ $DOWN == 1 ]]; then
 
            NEWIDX=$(( IDX - 1 ))
 
        fi
 
   
 
        # Transition
 
        $FIRST && OLDLEVEL=unknown || OLDLEVEL=${LEVELS[$IDX]}
 
        NEWLEVEL=${LEVELS[$NEWIDX]}
 
        $VERBOSE && echo "Temps: $TEMPS  Fan: $SPEED  Level: $OLDLEVEL->$NEWLEVEL"
 
        $SYSLOG && [[ $OLDLEVEL != $NEWLEVEL ]] &&
 
                $LOGGER -t "`basename $0`[$$]" "Changing fan level: $OLDLEVEL->$NEWLEVEL"
 
 
        setlevel $NEWLEVEL
 
   
 
        sleep $INTERVAL
 
   
 
        # If needed, apply anti-pulsing hack after a settle-down period:
 
        if [[ ${ANTIPULSE[${NEWIDX}]} == 1 ]]; then
 
            if [[ $NEWLEVEL == $OLDLEVEL ]]; then
 
                if [[ $SETTLE -ge 0 ]]; then
 
                    (( SETTLE -= INTERVAL ))
 
                else
 
                    setlevel 0x40 # disengaged
 
                    sleep 0.5
 
                fi
 
            else
 
                SETTLE=6
 
            fi
 
        fi
 
   
 
        IDX=$NEWIDX
 
FIRST=false
 
    done
 
}
 
 
if $KILL_DAEMON ; then
 
    if [ -f $PID_FILE ]; then
 
set -e
 
DPID="`cat $PID_FILE`"
 
        kill "$DPID"
 
rm "$PID_FILE"
 
$VERBOSE && echo "Killed process $DPID"
 
    else
 
        $VERBOSE && echo "Daemon not running."
 
        exit 1
 
    fi
 
elif $DAEMONIZE ; then
 
    if [ -e "$PID_FILE" ]; then
 
        echo "$0: File $PID_FILE already exists, refusing to run."
 
        exit 1
 
    else
 
AM_DAEMON=true VERBOSE=false control_fan 0<&- 1>&- 2>&- &
 
        echo $! > "$PID_FILE"
 
        exit 0
 
    fi
 
else
 
    control_fan
 
fi
 
</pre>
 
 
--[[User:Thinker|Thinker]] 20:28, 8 Nov 2005 (CET)
 
----
 
 
Great work, thanks. Just added startup and shutdown messages and made some echos depending on $VERBOSE so you can for instance call <tt>script -qk</tt> from an init-script without problems. Also set the exit value to non-zero when trying to kill a not running daemon.
 
 
Oh, and I incorporated you last minor edit, because that ended up in the version that I put into my last comment. ;)
 
 
--[[User:Spiney|spiney]] 22:18, 8 Nov 2005 (CET)
 
----
 
 
Yes, good ideas. The current version works perfectly for me. If you're happy with it, let's move it to the article page (and let's make it the first one in that section, in order not to frighten people away with the need for the patch).
 
 
BTW, someone should add "Wait for winter" to the list of fan noise partial solutions. Those few degrees in ambient temperature make a big difference here, with the current thresholds...
 
 
--[[User:Thinker|Thinker]] 01:03, 9 Nov 2005 (CET)
 
 
----
 
----

Revision as of 04:48, 11 November 2005

Wyrfel, are you sure the recent (19:54, 27 Oct 2005) cosmetic change was a good idea? The extensive chunks of code make it hard to grok the structure of the article in the absense of separator lines (which "===" doesn't have). --Thinker 22:10, 27 Oct 2005 (CEST)


We can discuss this. From my point of view, the chunks of code distinguish themselves from each other quite well, because they are each in one code block.

I do not like the = section level - and so far we avoided them on all pages - because

  • it generates H1 headings, which is the same as the page heading,
  • having more than one level with the hbars is confusing/less readable, because they are not very well distinguishable. This way i.e. the "Other" section looked like a separate empty secion.

I think the way it's now, the separator lines make it possible to easily distinguish the different main sections, while when you have both levels with separator lines, an additional task of distinguishing H1 and H2 separators is necessary.

However, i see your point as well and would like to hear more opinions/arguments.

Wyrfel 22:31, 27 Oct 2005 (CEST)


bash script with fine control over fan speed (for unpatched kernels)

Here's an alternative variable speed control script that doesn't need the patch for controlling fan speed. It requires only ibm-acpi 0.11 or higher (e.g., as found in kernel 2.6.14 and higher) with the experimental=1 module parameter.

Alas, at the moment I don't have a suitable to test it on. Can someone give it a try and report?

(fixed and moved to article)

--Thinker 15:07, 3 Nov 2005 (CET)


IBM_ACPI was defined twice by accident I guess (changed above), but otherwise it seems to work fine, went from level 0 (cold machine) to 7 while compiling the kernel and now back to level 2 now at about 53 degrees Celsius.

--spiney 10:31, 5 Nov 2005 (CET)


Oops, yes. The IBM_ACPI=/tmp was just for testing (in the temporary absense of a ThinkPad, I wrote stuff to /tmp/thermal manually to test the script...). Fixed and moved to the article. Thanks!

--Thinker 12:58, 5 Nov 2005 (CET)


updated bash script for unpatched kernels

Moved to the article page, after joint development by Spiney and Thinker.