z4gunn / OctoPrint-GpioFanController

8 stars 2 forks source link

Add an option to make the fan speed proportional to rpi cpu temp #2

Closed Fredyy90 closed 3 years ago

Fredyy90 commented 3 years ago

Hey,

I would really like to see an option to make the fan speed proportional to the cpu temp of the rpi. Like setting a min temp and min speed for the fan to start and a max temp, max speed where the fan is running.

Something like: min temp: 45° => fanspeed: 20% max temp: 60° => fanspeed: 100%

and every temp between min/max will get its fanspeed calculated proportionally.

Maybe even add a field for a hysteresis offset like 5°. So the fan only turns off, when the cpu temp reaches 5° below the min temperature to start the fan. so it doesn't get stuck in a loop of turning on/off every second.

Currently i have a similar setup via a python script that runs in the background of the pi, but i would really like to have the functionality and config inside of an octoprint plugin, so every backup contains everything and it gets setup automatically on restore.

Fredyy90 commented 3 years ago

I'm currently using this script for the temperature controlled fan. Its a modified version of this repo https://github.com/DriftKingTW/Raspberry-Pi-PWM-Fan-Control I added the hysteresis to avoid the fan toggling on/off every loop.

#!/usr/bin/python
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import time
import signal
import sys
import os

# Configuration
FAN_PIN = 18            # BCM pin used to drive PWM fan
WAIT_TIME = 1           # [s] Time to wait between each refresh
PWM_FREQ = 25000        # [Hz] 25kHz for Noctua PWM control

# Configurable temperature and fan speed
HYST_OFF_TEMP = 45
MIN_TEMP = 50
MAX_TEMP = 70
FAN_LOW = 40
FAN_HIGH = 100
FAN_OFF = 0
FAN_MAX = 100

HYSTERESIS = 3

current_fan_speed = 0;

# Get CPU's temperature
def getCpuTemperature():
    res = os.popen('vcgencmd measure_temp').readline()
    temp =(res.replace("temp=","").replace("'C\n",""))
    #print("temp is {0}".format(temp)) # Uncomment for testing
    return temp

# Set fan speed
def setFanSpeed(speed):
    current_fan_speed = speed
    fan.start(speed)
    return()

# Handle fan speed
def handleFanSpeed():
    temp = float(getCpuTemperature())
    # Turn off the fan if temperature is below MIN_TEMP
    if temp < MIN_TEMP:
        if temp < HYST_OFF_TEMP:
            setFanSpeed(FAN_OFF)
        elif current_fan_speed > 0:
            setFanSpeed(FAN_LOW)
        #print("Fan OFF") # Uncomment for testing
    # Set fan speed to MAXIMUM if the temperature is above MAX_TEMP
    elif temp > MAX_TEMP:
        setFanSpeed(FAN_MAX)
        #print("Fan MAX") # Uncomment for testing
    # Caculate dynamic fan speed
    else:
        step = float(FAN_HIGH - FAN_LOW)/float(MAX_TEMP - MIN_TEMP)  
        temp -= MIN_TEMP
        setFanSpeed(FAN_LOW + ( round(temp) * step ))
        #print(FAN_LOW + ( round(temp) * step )) # Uncomment for testing
    return ()

try:
    # Setup GPIO pin
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(FAN_PIN, GPIO.OUT, initial=GPIO.LOW)
    fan = GPIO.PWM(FAN_PIN,PWM_FREQ)
    setFanSpeed(FAN_OFF)
    # Handle fan speed every WAIT_TIME sec
    while True:
        handleFanSpeed()
        time.sleep(WAIT_TIME)

except KeyboardInterrupt: # trap a CTRL+C keyboard interrupt
    setFanSpeed(FAN_HIGH)
    #GPIO.cleanup() # resets all GPIO ports used by this function
z4gunn commented 3 years ago

I am using this plugin for my case fans, but it can also be used for fans to cool your Pi or other components. This idea seems feasible, but I will have to research a good method to call the update function on some periodic event or timer interval since this function would have to be called inside the plugin.

Fredyy90 commented 3 years ago

yes it'll need some sort of timer to call the "update" function every few seconds. there must be a way to do something like this with an octoprint plugin.

z4gunn commented 3 years ago

It looks like a RepeatedTimer function will work: image

Fredyy90 commented 3 years ago

took a look at your development branch, didn't test it yet. wow man you are fast implementing suggestions! keep up the good work.

one addition, i think either call the "Temp Hysterisis" in the GUI "Hysterisis Temp Offset" and substract it from the "Min Temp", or call it something like "Fan Off Temp" and force it to be lower than "Min Temp", otherwise "normal" users will be confused, what this field wants from them.

z4gunn commented 3 years ago

Actually i removed hysteresis all together in favor of using two temp temp measurements: current temp and temp from previous update. Both temps must meet the criteria to update fan speed or update will loop will return with out update. This is sort of a deadband approach. Can you give a try to see if it suits your needs? If it looks good then I will create PR to get it merged into master.

Fredyy90 commented 3 years ago

that sounds like a nice approach so at max the fan can toggle on/off it its above/below the limit for at least 10s so no toggle on/off every update and its simpler for most users to understand. i'll give it a try as soon as i'm home.