-

CommentStreams:98b4f6ee1f2db4c26065155d713b9f0e

From Geekworm Wiki
< Smart Fan and Power Expansion Board
Revision as of 19:02, 26 October 2018 by 209.58.142.155 (talk) (Migrated comment #26)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
  1. !/usr/bin/python
  2. MyRPi.ca script - ocs11@hotmail.com
  1. 2018-10-01
  1. Python script that uses multiple sources to determine the CPU and enclosure temperature
  2. and adjust the fan speed automatically
  1. 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

  1. 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.

  1. I2C Temperature LM75 sensor onboard Fan/power hat
  2. i2cdetect -y 1
  3. 0 1 2 3 4 5 6 7 8 9 a b c d e f
  4. 00: -- -- -- -- -- -- -- -- -- -- -- -- --
  5. 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  6. 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  7. 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  8. 40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- <- 0x48 LM75 Temperature Sensor
  9. 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  10. 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- <- 0x68 DS3231 RTC Module
  11. 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

  1. End of typical user configuration. Changing the code below may result in unexpected behaviour.
  1. 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
  1. 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)
  1. 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);

  1. 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)
  1. 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()
  1. End of Script