#!/usr/bin/env python # # Control the fan according to temperature and load # # Copyright (c) 2012, Matthias P. Braendli # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. import time import os import daemon import lockfile import signal # I'll count SMT as cores NUM_PROC = 4 # graph_points define the function mapping temperature to fanspeed # linear interpolation inbetween, flat outside graph_points = {39: 0, 40: 80, 50: 160, 60: 255} # load increase factor defines by what factor*norm_load we must increase # the temperature (calc_temp = real_temp + load_increase_factor * norm_load) load_increase_factor_day = 8 load_increase_factor_night = 0 # stronger ventilation during those hours day = [9, 22] PWM_ENABLE_FILE = "/sys/devices/platform/w83627ehf.656/hwmon/hwmon1/device/pwm2_enable" FAN_PWM = "/sys/devices/platform/w83627ehf.656/hwmon/hwmon1/device/pwm2" CORETEMP_FOLDER = "/sys/devices/platform/coretemp.0" def enable_pwm(): """Initial setup""" with open(PWM_ENABLE_FILE, 'w') as fd: fd.write('1') def load_increase_factor(): """According to time of day, return load increase factor""" now = time.gmtime().tm_hour if day[0] <= now < day[1]: return load_increase_factor_day else: return load_increase_factor_night def get_normalised_load(): """Return the load average in the last minute divided by the number of processors""" return os.getloadavg()[0] / NUM_PROC def get_temperature(): """Return the maximum of the two CPU temperatures""" core0_temp_file = CORETEMP_FOLDER + "/temp1_input" core1_temp_file = CORETEMP_FOLDER + "/temp2_input" with open(core0_temp_file) as fd: temp0 = fd.read() with open(core1_temp_file) as fd: temp1 = fd.read() return max([int(t) / 1000 for t in [temp0, temp1]]) def set_fan(speed): """Set the fanspeed. speed is between 0 and 255""" print("Setting fan to {0}...".format(speed)) with open(FAN_PWM, "w") as fd: fd.write(str(speed)) print("Fanspeed set.") def calculate_fan_speed(): """Calculate desired fanspeed according to system state""" temp_incr = load_increase_factor() * get_normalised_load() temp_real = get_temperature() temp = int(temp_real + temp_incr) print("real / load compensated temperature: {0} / {1}".format(temp_real, temp)) # interpolate fanspeed temp_points = sorted(graph_points.keys()) last_tp = temp_points[0] if temp < last_tp: return graph_points[last_tp] for tp in temp_points: if temp == tp: return graph_points[tp] elif temp < tp: f1 = graph_points[tp] f2 = graph_points[last_tp] fraction = 1.0 * (temp - last_tp) / (tp - last_tp) return int(fraction * f1 + (1-fraction) * f2) last_tp = tp return graph_points[tp] running = True def quit(): running = False def main(): enable_pwm() while running: try: fanspeed = calculate_fan_speed() #print("New fan speed {0}".format(fanspeed)) set_fan(fanspeed) except KeyboardInterrupt: break #except: # print("Exception occurred") time.sleep(20) context = daemon.DaemonContext(pidfile=lockfile.FileLock('/var/run/ventilator.pid')) context.signal_map = { signal.SIGTERM: quit, } print("Testing") enable_pwm() fanspeed = calculate_fan_speed() print("Fan speed {0}".format(fanspeed)) set_fan(fanspeed) print("starting daemon") with context: main()