-
CommentStreams:98b4f6ee1f2db4c26065155d713b9f0e
- !/usr/bin/python
- MyRPi.ca script - ocs11@hotmail.com
- 2018-10-01
- Python script that uses multiple sources to determine the CPU and enclosure temperature
- and adjust the fan speed automatically
- 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] import smbus # Required to read the sensor temperature
- 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 = 45 # 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 = 0 # 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.
- I2C Temperature LM75 sensor onboard Fan/power hat
- i2cdetect -y 1
- 0 1 2 3 4 5 6 7 8 9 a b c d e f
- 00: -- -- -- -- -- -- -- -- -- -- -- -- --
- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
- 40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- <- 0x48 LM75 Temperature Sensor
- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
- 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- <- 0x68 DS3231 RTC Module
- 70: -- -- -- -- -- -- -- --
checkLM75 = 1 # Enable (1) or disable (0) the LM75 sensor check sensorAddress = 0x48 # I2C Address of the temperature sensor tempRegister = 0 # In progress I2Cbus = smbus.SMBus(1) # 0 or 1 depending on the RPi. 0=/dev/i2c-0 1=/dev/i2c-1
- 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 regdata2float (regdata):
return (regdata / 32.0) / 8.0
def getCPUtemp1():
if (diagCon): print "Trying to acquire CPU Temperature (Primary)."
if (diagLog): fileLog.write ( "Trying to acquire CPU Temperature (Primary).\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 "Trying to acquire CPU Temperature (Secondary)."
if (diagLog): fileLog.write ( "Trying to acquire CPU Temperature (Secondary).\n" )
cpuTemp2 = os.popen('vcgencmd measure_temp').readline()
return float(cpuTemp2.replace("temp=","").replace("'C\n",""))
def getSensorTemp(self):
# Read the temperature from the I2C LM75 sensor if (diagCon): print "Trying to acquire LM75 Sensor Temperature." if (diagLog): fileLog.write ( "Trying to acquire LM75 Sensor Temperature.\n" ) raw = I2Cbus.read_word_data(sensorAddress, tempRegister) & 0xFFFF raw = ((raw << 8) & 0xFF00) + (raw >> 8) ret = regdata2float(raw) if (diagCon): print "LM75 Sensor Temperature is ", ret, "C" if (diagLog): fileLog.write ( "LM75 Sensor Temperature is " + str(ret) + " C\n" ) return ret
- 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
if (checkLM75): getSensorTemp(I2Cbus)
# 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()
- End of Script