#!/usr/bin/python # MyRPi.ca script - ocs11@hotmail.com # Load system libraries import RPi.GPIO as GPIO # Required to access the GPIO pin for PWM control. import time # Required for the loop delay. [time.sleep] import datetime # Required for date and time strings import sys # Required for CTRL-C clean exit. [sys.exit] import os # Required for secondary CPU temperature extraction. [os.open] # User Configuration Settings FAN_PIN = 12 # BCM pin used to drive the fan control. WAIT_TIME = 30 # Time, in seconds, to wait between each loop cycle. fanSpeedMin = 36 # Minimum fan speed. Must be greater than 0. Use the calibration utility to get this value. fanSpeedMax = 100 # Maximum fan speed. Set to limit noise otherwise leave at 100. Maximum 100. cpuTempMin = 40 # Temperature at which to start the fan. Minimum 1. cpuTempMax = 60 # Temperature at which fan should run at [fanSpeedMax] speed. Maximum 70 (for safety). diagCon = 1 # Set to [1] to enable diagnostic output to the console. [0] to diable. diagLog = 1 # Set to [1] to enable diagnostic output to a log file. [0] to diable. fanStatusLog = "/tmp/fanStatus" # Diagnostic Log file path and filename. # End of typical user configuration. Changing the code below may result in unexpected behaviour. # Initialize runtime variables PWM_FREQ = 50 # [Hz] Do not change. See documentation. cpuTemp = 0 # CPU Temperatue cpuTempOld = 0 # CPU Temperature on the last cycle fanSpeed = 0 # Fan Speed fanSpeedOld = 0 # Fan Speed on the last cycle fanRunning = 1 # Boolean to store if the fan is running or not def getCPUtemp1(): if (diagCon): print "Method 1: Trying to acquire CPU Temperature." if (diagLog): fileLog.write ( "Method 1: Trying to acquire CPU Temperature.\n" ) cpuTempFile = open("/sys/class/thermal/thermal_zone0/temp","r") # Open system file Read-Only cpuTemp1 = int(float(cpuTempFile.read())/1000) # Retrieve the system temperature value cpuTempFile.close() # Close the system file return cpuTemp1 def getCPUtemp2(): if (diagCon): print "Method 2: Trying to acquire CPU Temperature." if (diagLog): fileLog.write ( "Method 2: Trying to acquire CPU Temperature.\n" ) cpuTemp2 = os.popen('vcgencmd measure_temp').readline() return float(cpuTemp2.replace("temp=","").replace("'C\n","")) # Verify the configuration values if ((cpuTempMin >= cpuTempMax) or (cpuTempMin < 1) or (cpuTempMax > 70)): print "Invalid cpuTempMin or cpuTempMax value has been configured. Please read the documentation. Aborting." exit(0) if ((fanSpeedMin >= fanSpeedMax) or (fanSpeedMin < 0) or (fanSpeedMax > 100)): print "Invalid fanSpeedMin or fanSpeedMax value has been configured. Please read the documentation. Aborting." exit(0) # Initialize the GPIO subsystem GPIO.setmode(GPIO.BCM) GPIO.setup(FAN_PIN, GPIO.OUT, initial=GPIO.LOW) GPIO.setwarnings(False) fan = GPIO.PWM(FAN_PIN,PWM_FREQ) fan.start(0); # Main runtime loop try: while (1): # Repeat until CTRL-C is pressed. # Reset variables at beginning of loop cpuTemp = 0 now = datetime.datetime.now() timeNow = now.strftime("%Y-%m-%d %H:%M:%S") if (diagCon): print timeNow if (diagLog): fileLog = open(fanStatusLog,"w") fileLog.write ( timeNow + "\n" ) # Try method 1 to acquire CPU Temperature cpuTemp = getCPUtemp1() # If method 1 failed, try method 2 to get CPU temperature if ((cpuTemp <= 0) or (cpuTemp > 120)): cpuTemp = getCPUtemp2() # Check to make sure we have a reasonable CPU temperature to work with if ((cpuTemp <= 0) or (cpuTemp > 120)): print "Unable to read the CPU Temperature [cpuTemp]. Incompatible system architecture or Linux version. Aborting." exit(0) if (diagCon): print "CPU Temperature: ", cpuTemp if (diagLog): fileLog.write ( "CPU Temperature: " + repr(cpuTemp) + "\n" ) # Calculate the appropriate fan speed based on the current CPU temperature if (cpuTemp > cpuTempMin): cpuTempPerc = (((cpuTemp - cpuTempMin) * 100) / (cpuTempMax - cpuTempMin)) fanSpeed = (((fanSpeedMax - fanSpeedMin) * cpuTempPerc ) / 100 ) + fanSpeedMin if (diagCon): print "Calculated cpuTempPerc:", cpuTempPerc, "% fanSpeed:", fanSpeed if (diagLog): fileLog.write ( "Calculated cpuTempPerc: " + str(cpuTempPerc) + "% fanSpeed: " + str(fanSpeed) + "\n" ) # Enforce maximum fan speed if ((fanSpeed > fanSpeedMax) or (cpuTemp >= cpuTempMax)): if (diagCon): print "Setting fanSpeed [", fanSpeed, "] -> fanSpeedMax [", fanSpeedMax, "] based on fanSpeedMax or cpuTempMax" if (diagLog): fileLog.write ( "Setting fanSpeed [" + str(fanSpeed) + "] -> fanSpeedMax [" + str(fanSpeedMax) + "] based on fanSpeedMax or cpuTempMax\n" ) fanSpeed = fanSpeedMax # If fanSpeed is at or below the minimum, stop the fan if (((fanSpeed < fanSpeedMin) or (cpuTemp <= cpuTempMin)) and (fanRunning != 0)): if (diagCon): print "Changing Duty Cycle [", fanSpeedOld, "] -> [Off] Cooling not required." if (diagLog): fileLog.write ( "Changing Duty Cycle [" + str(fanSpeedOld) + "] -> [Off] Cooling not required\n" ) fanSpeed = fanSpeedMin fanSpeedOld = fanSpeedMin fanRunning = 0 fan.ChangeDutyCycle(100) # If the calculated speed is different from the last cycle, set the fan speed if ((fanSpeed != fanSpeedOld)): if (diagCon): print "Changing Duty Cycle [", fanSpeedOld, "] -> [", fanSpeed, "]" if (diagLog): fileLog.write ( "Changing Duty Cycle [" + str(fanSpeedOld) + "] -> [" + str(fanSpeed) + "]\n" ) fanRunning = 1 fan.ChangeDutyCycle(100-fanSpeed) fanSpeedOld = fanSpeed # Wait until the next loop cycle if (diagCon): print if (diagLog): fileLog.close() time.sleep(WAIT_TIME) # If a keyboard interrupt occurs (ctrl + c), set the GPIO to 0 and exit the program. except(KeyboardInterrupt): print "Fan speed control script interrupted by the user. CTRL-C was pressed." GPIO.cleanup() if not fileLog.closed: fileLog.close() sys.exit()