RPI4 fan control

Wanted to send a little update and convert to American (Fahrenheit instead of Celsius) as well as update the code to utilize system monitor integration which is the current (as of 9/2025) integration that gives you CPU/processor temp and other system related stats. I believe the above and original code was related to common ways of adding CPU temp stats to your RPI through the config YAML file. Thats why it referenced “sensor.cpu_temperature” where mine below is “sensor.processor_temperature”. Play with the threshold values as you like as the cycling of the fan can be annoying to some. Thank you all above for the help though

switch:
  - platform: rpi_gpio
    switches:
      - port: 14
        name: "RPI Cooling Fan"
        unique_id: "rpi_cooling_fan"
        
climate:
  - platform: generic_thermostat
    name: RPI Cooling Fan Controller
    unique_id: rpi_cooling_fan_controller
    heater: switch.rpi_cooling_fan
    target_sensor: sensor.processor_temperature
    min_temp: 86 #30C
    max_temp: 176 #80C
    ac_mode: true
    target_temp: 110
    cold_tolerance: 5
    hot_tolerance: 5
    min_cycle_duration:
      seconds: 600
    keep_alive:
      minutes: 5
    initial_hvac_mode: "cool"

Edit: Additional tips, you need the HACS integration “Raspberry Pi GPIO” installed for this to work. Make sure you are looking at the above comments related to how they determine what pin to use for fan control. I did some tuning based on the lowest temp i saw my CPU get with constant active cooling and the top temp i saw it get with passive cooling. I do think that maybe it would be better to PWM my fan to have constant cooling to just keep it steady as im seeing a 20F swing with these settings every 10 minutes. Chatgps said that its fine and i dont know if its true but im okay with finding that out as time goes. There is cons to temp swinging if the range is large and closer to max ratings. I will try to reply if i figure out PWM control.

1 Like

Check the German spelling in the climate section… make it match the sensor

Hello Folks,

I am using Ubuntu 25.10 on Raspberry Pi 3 Model B Rev 1.2 with this kind of hardware and the basic fan

Image from https://community.home-assistant.io/t/rpi4-fan-control/610597/25


(N.B. my transistor is controlled from pin 18 instead of 14)

With the following python3 code:

Fan.py

#!/usr/bin/python3
#  Control a 5V PWM fan speed with the lgpio library
#  Uses lgpio library, on kernel 6.17.0-1006-raspi
#  Author: Lucien Hercaud

import lgpio
import time
import os
import sys
import daemon
import syslog

FAN = 18 # pin used to drive PWM fan transistor
FREQ = 100

def gettemp():
    with open('/sys/class/thermal/thermal_zone0/temp', 'r') as T:
        return float(T.read().strip())/1000.

def fan(x,y,z):
    syslog.syslog(syslog.LOG_INFO, f"Temp {z:.2f}°C Duty {y}\n")
    lgpio.tx_pwm(x, FAN, FREQ, y)
    time.sleep(2)

def main():
    syslog.openlog(ident="FAN", facility=syslog.LOG_LOCAL0)
    syslog.syslog(syslog.LOG_INFO, "daemon started\n")
    time.sleep(120)
    syslog.syslog(syslog.LOG_INFO, "daemon activated\n")
    h = lgpio.gpiochip_open(0)
    lgpio.gpio_claim_output(h, FAN)
    p = 0
    while True:
        CPU_temp = gettemp()
        if CPU_temp > 85.0:
            if p != 100:
                fan(h,100,CPU_temp)
                p=100
        elif CPU_temp > 80.0:
            if p != 80:
                fan(h,80,CPU_temp)
                p=80
        elif CPU_temp > 75.0:
            if p != 70:
                fan(h,70,CPU_temp)
                p=70
        elif CPU_temp > 72.0:
            if p != 60:
                fan(h,60,CPU_temp)
                p=60
        elif CPU_temp > 69.0:
            if p != 50:
                fan(h,50,CPU_temp)
                p=50
        elif CPU_temp > 66.0:
            if p != 45:
                fan(h,45,CPU_temp)
                p=45
        elif CPU_temp > 63.0:
            if p != 40:
                fan(h,40,CPU_temp)
                p=40
        else:
            if p != 0:
                fan(h,0,CPU_temp)
                p = 0
        try:
            time.sleep(60)
        except:
            fan(h,0,CPU_temp)
            lgpio.gpio_free(h, FAN)
            lgpio.gpiochip_close(h)
            syslog.syslog(syslog.LOG_INFO, "daemon stopped\n")
            sys.exit(0)

with daemon.DaemonContext():
    main()

Fan_off.py

#!/usr/bin/python3
import lgpio
FAN = 18 # pin used to drive PWM fan
FREQ = 100
h = lgpio.gpiochip_open(0)
lgpio.gpio_claim_output(h, FAN)
# Turn the fan off
lgpio.tx_pwm(h, FAN, FREQ, 0)

The code above needs the following (apt) packages:

root# apt install liblgpio1 python3-lgpio python3-lockfile python3-daemon \ 
   python3-dev python3-pip python3-wheel

The rsyslog “local0” facility was added as:

root# cat /etc/rsyslog.d/50-local.conf
local0.*        /var/log/local0.log
root# systemctl restart rsyslog

And the “fan” service as:

root# systemctl cat fan
# /etc/systemd/system/fan.service
[Unit]
Description=PI3 Fan Control by Temperature
After=multi-user.target
ConditionPathExists=!/etc/fan/fan_not_to_be_run

[Service]
ExecStart=/usr/local/bin/Fan.py
ExecStopPost=/usr/local/bin/Fan_off.py
KillMode=process
Type=simple
TimeoutStopSec=30
Restart=on-failure

[Install]
Alias=fand.service

root# systemctl enable fan
root# systemctl start fan

Messages can be seen from:

tail -f /var/log/local0.log

To rotate the log:

# cat /etc/logrotate.d/local
/var/log/local0.log
/var/log/local1.log
/var/log/local2.log
/var/log/local3.log
/var/log/local4.log
/var/log/local5.log
/var/log/local6.log
{
        rotate 4
        weekly
        missingok
        notifempty
        compress
        delaycompress
        sharedscripts
        endscript
}