Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # This script is intended to control the fan speeds on a Gigabyte Z77X-D3H
- # motherboard, while also using an NVIDIA GPU's temperature as input.
- #
- #
- # --------------------------------
- # Gigabyte GA-Z77X-D3H Motherboard
- # --------------------------------
- #
- # This board's sensors are at /sys/devices/platform/it87.2608/.
- #
- # pwm1 controls voltage of the CPU fan header (has to be disabled if using a PWM
- # fan).
- #
- # pwm2 controls the system fan headers (voltage on SYS_FAN1 and PWM on SYS_FAN[23]).
- #
- # pwm3 controls the PWM signal for the CPU fan header.
- #
- # temp1 is the "system" temperature.
- #
- # temp2 is the PCH's temperature.
- #
- # temp3 is the CPU temperature.
- #
- # Setting pwm2_enable to 0 disables control, will output a 100% voltage/PWM
- # signal.
- #
- # Setting pwm2_enable to 2 will cause the board's sensor chip logic to use some
- # sort of automatic to tie temperatures to fan speeds. The value in pwm2 seems
- # to be used as a multiplier for that automatic.
- #
- # Setting pwm2_enable to 1 will cause the chip to use a fixed speed as set in
- # pwm2. The value in pwm2 seems to have the value range of an unsigned 8-bit
- # number. The 0 to 255 value translates into a 0 to 100% fan speed signal.
- #
- #
- # ------------------
- # Intel i5-3570k CPU
- # ------------------
- #
- # The CPU's sensors are at /sys/devices/platform/coretemp.0/hwmon/hwmon0/.
- #
- # temp[2345] are the individual core temperatures.
- #
- # temp1 seems to be the highest core temperature.
- #
- #
- # -------------------------------
- # NVIDIA GPU, using NVIDIA's blob
- # -------------------------------
- #
- # There's no lm_sensors device.
- #
- # The nvidia-smi tool seems to be the least resource intensive method to read
- # temperatures from the card. The following outputs a single number (for GPU0)
- # in degrees Celsius (so without a factor of 1000 like lm_sensors):
- #
- # $ nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader --id=0
- #
- # [ "$(whoami)" = root ] || exec sudo "$0"
- pidfile="/var/run/fanauto.pid"
- # Temperature limits in degrees Celsius:
- gpu_temp_to_sysfan_min=35
- gpu_temp_to_sysfan_max=65
- cpu_temp_to_sysfan_min=30
- cpu_temp_to_sysfan_max=85
- cpu_temp_to_cpufan_min=25
- cpu_temp_to_cpufan_max=75
- # Fan speed limits in percent
- #
- # The script doesn't actually limit speeds at the max set here. The number is
- # only used for setting up the speed curve. It will go higher if the temperatures
- # are above max.
- sysfan_min=35
- sysfan_max=80
- cpufan_min=30
- cpufan_max=80
- # Fan speed history buffer length and update interval in seconds
- buf_length=40
- update_interval=3
- # Number of intervals to skip after each output, so only outputs every 3+3*2=9 seconds
- output_skip=2
- device_path="/sys/devices/platform/it87.2608"
- # Temperature input
- function read_temperatures {
- #read cpu_temp < /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp1_input
- read cpu_temp < $device_path/temp3_input
- #read system_temp < $device_path/temp1_input
- #read pch_temp < $device_path/temp2_input
- gpu_temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader --id=0)000
- }
- # Enable PWM control
- function enable_pwm {
- echo 1 > $device_path/pwm2_enable
- echo 1 > $device_path/pwm3_enable
- }
- function output_pwm {
- # $1: sysfan
- # $2: cpufan
- echo "$1" > $device_path/pwm2
- echo "$2" > $device_path/pwm3
- }
- # Save PWM setup
- function save_pwm {
- read save_pwm2 < $device_path/pwm2
- read save_pwm3 < $device_path/pwm3
- read save_pwm2_enable < $device_path/pwm2_enable
- read save_pwm3_enable < $device_path/pwm3_enable
- }
- # Restore PWM setup
- function restore_pwm {
- enable_pwm
- echo "$save_pwm2" > $device_path/pwm2
- echo "$save_pwm3" > $device_path/pwm3
- echo "$save_pwm2_enable" > $device_path/pwm2_enable
- echo "$save_pwm3_enable" > $device_path/pwm3_enable
- rm -f "$pidfile"
- exit "$1"
- }
- ######################################################
- (( gpu_temp_to_sysfan_min *= 1000 ))
- (( gpu_temp_to_sysfan_max *= 1000 ))
- (( cpu_temp_to_sysfan_min *= 1000 ))
- (( cpu_temp_to_sysfan_max *= 1000 ))
- (( cpu_temp_to_cpufan_min *= 1000 ))
- (( cpu_temp_to_cpufan_max *= 1000 ))
- (( sysfan_min = sysfan_min * 1000 * 255 / 100 ))
- (( sysfan_max = sysfan_max * 1000 * 255 / 100 ))
- (( cpufan_min = cpufan_min * 1000 * 255 / 100 ))
- (( cpufan_max = cpufan_max * 1000 * 255 / 100 ))
- (( gpu_temp_to_sysfan_width = gpu_temp_to_sysfan_max - gpu_temp_to_sysfan_min ))
- (( cpu_temp_to_sysfan_width = cpu_temp_to_sysfan_max - cpu_temp_to_sysfan_min ))
- (( cpu_temp_to_cpufan_width = cpu_temp_to_cpufan_max - cpu_temp_to_cpufan_min ))
- (( sysfan_width = sysfan_max - sysfan_min ))
- (( cpufan_width = cpufan_max - cpufan_min ))
- function calc_fan_speeds {
- # translate both GPU and CPU temperature to system fan speed and use max
- (( sysfan_gpu = (gpu_temp - gpu_temp_to_sysfan_min) * sysfan_width / gpu_temp_to_sysfan_width ))
- (( sysfan_cpu = (cpu_temp - cpu_temp_to_sysfan_min) * sysfan_width / cpu_temp_to_sysfan_width ))
- # translate CPU temperature to CPU fan speed
- (( cpufan_cpu = (cpu_temp - cpu_temp_to_cpufan_min) * cpufan_width / cpu_temp_to_cpufan_width ))
- # set correct minimum
- sysfan=0
- (( sysfan_gpu > sysfan )) && sysfan=$sysfan_gpu
- (( sysfan_cpu > sysfan )) && sysfan=$sysfan_cpu
- (( sysfan += sysfan_min ))
- cpufan=0
- (( cpufan_cpu > cpufan )) && cpufan=$cpufan_cpu
- (( cpufan += cpufan_min ))
- }
- # set up a history of fan speeds that is intended to be used as a ring buffer
- cpufan_buf=()
- sysfan_buf=()
- buf_ringptr=0
- read_temperatures
- calc_fan_speeds
- for (( i = 0; i < buf_length; i++ ))
- do
- (( sysfan_buf[i] = sysfan ))
- (( cpufan_buf[i] = cpufan ))
- done
- (( sysfan_sum = sysfan * buf_length ))
- (( cpufan_sum = cpufan * buf_length ))
- progname=$(basename "$0")
- if [[ -f "$pidfile" ]] ; then
- echo "$progname: $pidfile exists" >&2
- exit 1
- fi
- echo $$ > "$pidfile"
- save_pwm
- trap 'restore_pwm 0' SIGQUIT SIGTERM
- trap 'restore_pwm 1' SIGHUP SIGINT EXIT
- enable_pwm
- error_counter=0
- function error_handler {
- (( error_counter++ ))
- }
- trap 'error_handler' ERR
- # main loop
- output_limit_counter=0
- while true
- do
- read_temperatures
- calc_fan_speeds
- (( sysfan_sum += sysfan - sysfan_buf[buf_ringptr] ))
- (( cpufan_sum += cpufan - cpufan_buf[buf_ringptr] ))
- (( sysfan_buf[buf_ringptr] = sysfan ))
- (( cpufan_buf[buf_ringptr] = cpufan ))
- (( ++buf_ringptr >= buf_length )) && buf_ringptr=0
- (( sysfan_out = (sysfan_sum / buf_length + 500) / 1000 )) # +500 to make rounding work
- (( cpufan_out = (cpufan_sum / buf_length + 500) / 1000 ))
- (( sysfan_out > 255 )) && sysfan_out=255
- (( cpufan_out > 255 )) && cpufan_out=255
- # PWM output
- if (( --output_limit_counter < 0 )) ; then
- (( output_limit_counter = output_skip ))
- output_pwm $sysfan_out $cpufan_out
- fi
- if (( error_counter )) ; then
- error_counter=0
- enable_pwm # an error can happen after PC standby/resume, so trying to reenable pwm might work
- if (( error_counter )) ; then
- echo "something doesn't work!" >&2
- exit 1
- fi
- fi
- # echo cpu $((cpu_temp/1000))°C, gpu $((gpu_temp/1000))°C, \
- # cpu-fan $(((cpufan_out*1000+500)/2550))% \($(((cpufan+500)/2550))%\), \
- # sys-fan $(((sysfan_out*1000+500)/2550))% \($(((sysfan+500)/2550))%\)
- sleep $update_interval &
- wait "$!"
- done
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement