#!/bin/sh
# monitor temperature on external Quadro NVS420 and adjust fan speed
# logs changes to syslog
# Copyright 2015 TJ <hacker@iam.tj>
# Licenced on the terms of the GNU General Public Licence version 3
# uncomment for debugging in foreground
# set -x
# user-friendly device name for logging
TARGET=${TARGET:-NVS420}
# GPUs reported by nvidia-settings
GPU_CONTROL=${GPU_CONTROL:-1 2}
# Fans reported
FAN_CONTROL=${FAN_CONTROL:-0}
# temperature bounds
TARGET_TEMP_MIN=${TARGET_TEMP_MIN:-60}
TARGET_TEMP_RANGE=${TARGET_TEMP_RANGE:-5}
TARGET_TEMP_MAX=${TARGET_TEMP_MAX:-$((TARGET_TEMP_MIN + TARGET_TEMP_RANGE))}
# sampling delay in seconds
SLEEP=${SLEEP:-2}
# percentages
FAN_SPEED_MAX=${FAN_SPEED_MAX:-100}
FAN_SPEED_MIN=${FAN_SPEED_MIN:-30}
FAN_SPEED_STEP_BASE=${FAN_SPEED_STEP_BASE:-5}
FAN_SPEED_STEP=${FAN_SPEED_STEP_BASE}
# maximum speed change in one operation
FAN_SPEED_STEP_MAX=$(( (FAN_SPEED_MAX - FAN_SPEED_STEP_MIN) / 4 ))
TEMP_LAST=0
NO_CHANGE_COUNT=0
# adjust fan speed in proportion to temperature differential
adjust_proportional ()
{
TEMP_DIFF=$(( TEMP_AVG - TARGET_TEMP_MIN ))
TEMP_CHANGE=$(( TEMP_AVG - TEMP_LAST ))
# shell doesn't allow return values < 0 so bias to 128 and remember to remove bias in calling code
return $(( TEMP_CHANGE * TEMP_DIFF + $1 + TEMP_DIFF + 128 ))
}
# start
logger -p syslog.notice -- nvidia_fan_control[$$]: ${TARGET} Target ${TARGET_TEMP_MIN}-${TARGET_TEMP_MAX}°C controlling GPUs: ${GPU_CONTROL} Fans: ${FAN_CONTROL}
# continue looping whilst the X server is running
while pgrep X; do
TEMP_AVG=0
GPU_COUNT=0
for GPU in ${GPU_CONTROL}; do
TEMP_AVG=$(( TEMP_AVG + $(nvidia-settings --terse --query [gpu:${GPU}]/GPUCoreTemp) ))
done
TEMP_AVG=$(( TEMP_AVG/2 ))
for FAN in ${FAN_CONTROL}; do
FAN_SPEED=$(nvidia-settings --terse --query [fan:${FAN}]/GPUTargetFanSpeed)
if [ -n "${TEMP_AVG}" -a -n "${FAN_SPEED}" ]; then
FAN_SPEED_NEW=${FAN_SPEED}
# call adjustment calculation function
if [ ${TEMP_LAST} -gt 0 ]; then
adjust_proportional ${FAN_SPEED}
FAN_SPEED_NEW=$(( $? - 128))
fi
if [ ${FAN_SPEED_NEW} -gt ${FAN_SPEED_MAX} ]; then
FAN_SPEED_NEW=${FAN_SPEED_MAX}
elif [ ${FAN_SPEED_NEW} -lt ${FAN_SPEED_MIN} ]; then
FAN_SPEED_NEW=${FAN_SPEED_MIN}
fi
if [ ${FAN_SPEED} -ne ${FAN_SPEED_NEW} ]; then
for GPU in ${GPU_CONTROL}; do
nvidia-settings --verbose=none --assign=[gpu:${GPU}]/GPUFanControlState=1 >/dev/null
done
nvidia-settings --verbose=none --assign=[fan:${FAN}]/GPUTargetFanSpeed=${FAN_SPEED_NEW} >/dev/null
logger -p syslog.notice -- nvidia_fan_control[$$]: ${TARGET} Target ${TARGET_TEMP_MIN}-${TARGET_TEMP_MAX}°C Temp=${TEMP_AVG}°C Fan=${FAN_SPEED_NEW}%
fi
fi
done
TEMP_LAST=${TEMP_AVG}
sleep ${SLEEP}
done