Controlling a CPU/case fan with GPIO PWM based on CPU temperature

Nope :frowning:
I don’t know how to proceed or if this setup it is just deprecated

Thank you everyone that chimed in on this topic. I did get my CPU fan to work, but the information is spread out in a way that it is hard to follow. Here is my attempt to make it easier for others to set this up. I am running HA 2023.1.2, OS 9.3

  1. Install the Home Assistant Raspberry Pi GPIO PWM custom integration
    a. You must have HACS installed already. If you do not, please follow the instructions in the HACS website.
    b. From your HA menu, go to HACS->Integrations, click the ellipses (…) on the top right, select Custom Repositories
    c. Paste the following under Repository: https://github.com/RedMeKool/HA-Raspberry-pi-GPIO-PWM
    d. Under Category select integration then click Add.
    e. On the integration page, click the Download button on the bottom right.

  2. Install the PIGPIO daemon
    a. From your HA menu, go to Settings->Add-ons, then click on the Add-on Store button on the bottom right.
    b. Click the elepsis (…) on the top right, select Repositories
    c. Paste the following in field and click add: https://github.com/Poeschl/Hassio-Addons
    d. You should be back at the Add-ons screen now. Scroll ALL THE WAY DOWN and under the Poeschl Home Assistant Add-ons click the pigpio add-on and click install.
    e. Click on Start on the PIGPIO add-on screen.

– This may be a good point to restart your HA and make sure everything is working so far. –

  1. Add the CPU sensor
    a. Edit the config/configuration.yaml (you will need to SSH or have some editor for the files. I use the File Editor add-on)
    b. Add the following sensor under the sensor property. Note the spacing in YAML is really important.
sensor:
  - platform: command_line
    name: CPU Temp
    command: "cat /sys/class/thermal/thermal_zone0/temp"
    unit_of_measurement: "°C"
    value_template: "{{ value | multiply(0.001) | round(1)  }}"
    scan_interval: 10

c. Save the file and restart. If you have the developer tools enabled it is better to check the configuration before restarting.
d. Verify you now have a CPU temp sensor by going to Settings->Devices & Services->Entities, search for CPU Temp, click on it and view the CPU temp under the info tab.

  1. Add the fan control. For this example, I have my PI 4B PWM controllable fan’s TXD cable connected to GPIO-14.
    a. Edit the config/configuration.yaml
    b. Add the following lines to add the CPU fan control
light:
  - platform: rpi_gpio_pwm
    leds:
      - name: RPI Cooling Fan
        pin: 14

c. Save the file and restart. If you have the developer tools enabled it is better to check the configuration before restarting.
d. Verify you can now control the CPU fan by going to Settings->Devices & Services->Entities, search for RPI Cooling Fan, click on it, view the info tab, then use the slider to change the CPU fan speed.

  1. Tie it all together - I wanted a very simplistic way to use these entities, so I created 3 automations to control the fan (off, mid, max). Obviously, something that controls it dynamically is the way to go, but I wanted to keep this super simple for others to follow.
    a. Create a new automation that runs every minute as follows. With this example you can just create any other fan controls. Note that checking every minute is not a great way to control a CPU fan.
    Trigger: Time pattern *, *, 0 (runs every minute) - Why this? You can use a trigger on just CPU temp, but it was hard to force CPU temp for testing.
    Condition 1: Numeric state, entity = CPU fan, above 55
    Condition 2: Numeric state, entity = CPU fan, below 70
    Action: Service = light.turn_on, target = light.rpi_cooling_fan, brightness = 130 (this is the fan speed)

Here is the YAML for this automation:

alias: RPI CPU Fan Mid
description: ""
trigger:
  - platform: time_pattern
    hours: "*"
    minutes: "*"
    seconds: "0"
condition:
  - condition: numeric_state
    entity_id: sensor.cpu_temp
    below: 70
  - condition: numeric_state
    entity_id: sensor.cpu_temp
    above: 55
action:
  - service: light.turn_on
    data:
      brightness: 130
    target:
      entity_id: light.rpi_cooling_fan
mode: single
  1. Dynamic CPU control - You can use automations to achieve dynamic fan control based on temperature. With this example, you should have another automation to shut down the fan if you are below a temperature threshold of 55C. Here is the automation I created in YAML. Note the brightness is 0-255, and the calculation sets the fan speed to 100% once the temperature is higher than 90C.
alias: Dynamic RPI CPU Fan Control
description: ""
trigger:
  - platform: time_pattern
    hours: "*"
    minutes: "*"
    seconds: /20
condition:
  - condition: numeric_state
    entity_id: sensor.cpu_temp
    above: 55
action:
  - service: light.turn_on
    data:
      brightness: |
        {{states('sensor.cpu_temp') / 90 * 255 | int }}
    target:
      entity_id: light.rpi_cooling_fan
mode: single

– And there you have it. My aim was to have the CPU fan off as much as possible since my CPU temp is always low (<50C). I hope this helps everyone!

12 Likes

Thank you for your explantion! Unfortunatly for me is not working so, I am starting to think in my case it may be all about the fan I bougth, could you share us the fan you own? thank you!

This is the fan I purchased: GeeekPi Raspberry Pi 4 Aluminum Heatsink with PWM Controllable Fan, Raspberry Pi 4 Armor Lite Heatsink with PWM Speed Control Fan for Raspberry Pi 4 Model B

1 Like

Thanks a lot, it was my fan, with this one you share everything works like a charm!
Thank you all!

Finally, I added a dinamyc percentage value for the brightness/speed fan. Here it is my automation. I have to mention the range I decided to use for the temperature is between 50-70°C and with a speed between 10-100% so finally my linear function is: speed=4.5*temperature-215

alias: "[FAN] Cold RPI"
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.cpu_temp
condition: []
action:
  - service: light.turn_on
    data_template:
      transition: 5
      brightness_pct: >
        {% set temperature = states('sensor.cpu_temp') | int %}  {% if
        temperature > 70 %}
          100
        {% elif temperature > 50 and temperature < 70 %}
          {{ 4.5*temperature-215 }}
        {% else %}
          0
        {% endif %}
    target:
      entity_id: light.rpi_cooling_fan
mode: single
4 Likes

I used to have this setup working on my CM4 module on a PITray Mini using pin 14.

Got my HA Yellow today and set it up the same, as it also has pin 14 available on J11 but doesn’t seem to be working!

Did anyone try using PIGPIO and Pi GPIO PWM on the HA Yellow with success?

Thanks for posting this clarification. Like others, I get an error at this step.

light:
  - platform: rpi_gpio_pwm
    leds:
      - name: RPI Cooling Fan
        pin: 14

Does anyone else get this?
" Platform error light.rpi_gpio_pwm - Integration ‘rpi_gpio_pwm’ not found."

If you’ve hit a snag, make sure to follow this step…

I know that my fan start spinning at ~7 (tested on Arduino PWM pin). But here the minimum is 172 as well. If this is the nature of this add-on, then I have to find another way. 172 is definitely too much for ventilation.

It’s easier that I think. There is an optional parameter: frequency.
So, if on Arduino work well, I check data sheet to find PWM frequency and set the same on RPi:

light:
  - platform: rpi_gpio_pwm
    leds:
      - name: RPi Cooling Fan
        frequency: 490
        pin: 13

And now fan start spinning from 9 (~4%).

Bonus: automation for control speed based on temperature and processor load:

alias: RPi CPU Fan Control
description: ""
trigger:
  - platform: time_pattern
    hours: "*"
    minutes: "*"
    seconds: /45
condition: []
action:
  - service: light.turn_on
    data:
      brightness: >
        {% set res = {
           "temp": states('sensor.processor_temperature') | float(),
           "load": states('sensor.load_5m') | float() } %} 
        {{ (15, (255, (res.temp * 4 * res.load) | round()) | min) | max }}
    target:
      entity_id: light.rpi_cooling_fan
mode: single

Brightness return number between 15 and 255.

It took me now more than a year to finally get this done. I started, got close to finish, pin support got removed, lost interest, but now picked it up again.
This thread is really helpful - I would like to contribute by adding my configuration. I am pretty much doing what all of you are doing except for the automations and display. I am using the “climate” / “generic thermostat” display which I find more helpful in regards to what I like to see. Code and Screenshot below:

Screenshot 2023-04-25 131148

climate: 
  - platform: generic_thermostat 
    name: RPI Cooling Fan Controller 
    heater: light.rpi_cooling_fan 
    target_sensor: sensor.cpu_temperature 
    min_temp: 55 
    max_temp: 80 
    ac_mode: true 
    target_temp: 55 
    cold_tolerance: 0.1 
    hot_tolerance: 0.1 
    min_cycle_duration: 
      seconds: 30 
    keep_alive: 
      minutes: 5 
    initial_hvac_mode: "cool"

I just installed a noctua fan and I haven’t been able to control it, I tried pin 14 or 8 for GPIO14, I tried pin 18 or 12 for GPIO18, still nothing, runs full speed…

How to check what is actual fan speed?

Is there any way to get this working on x86 platform? I get the error ‘pigpio not supported.’ My PC fan is going at 100% all the time

Core updates happen in my installation of HA. Please let me know to install software to control the fan speed?

An alternative approach (which I find cleaner and also dynamically adjusts the fan), is to set an automation to adjust the fan speed every X seconds and calculate the relative fan speed based on a temperature range. For my use, I want the fan to spin at a minimum of 65% (which seems to be my fans minimum - NASPI), if the temperature goes over 40c (I’m in the UK and the pi stays below 40 usually), dynamically ramp the temps up to 100 at a max temp of 70 and scale down under this temp…

service: light.turn_on
data:
  brightness_pct: >
    {% set cpu_temp = states('sensor.processor_temperature')|float %} {% if
    cpu_temp <= 40 %}
      65
    {% elif cpu_temp > 50 %}
      100
    {% else %}
      {{ ((cpu_temp - 40) * (100 - 65) / (50 - 40) + 65)|round(0) }}
    {% endif %}
target:
  entity_id: light.rpi_cooling_fan

Hope this helps someone.

2 Likes

Hi every one,

I try to install and use rpi_gpio_pwm on my home assistant wich is on a raspberry Pi 4 on a container.
This what I see on logs :slight_smile:

Logger: homeassistant.components.light
Source: custom_components/rpi_gpio_pwm/light.py:71
Integration: Lumière (documentation, issues)
First occurred: 14:54:12 (1 occurrences)
Last logged: 14:54:12

Error while setting up rpi_gpio_pwm platform for light
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/gpiozero/pins/data.py", line 1194, in from_revision
    ) = PI_REVISIONS[revision]
KeyError: 1684095520

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 304, in _async_setup_platform
    await asyncio.shield(task)
  File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/rpi_gpio_pwm/light.py", line 71, in setup_platform
    led = PwmSimpleLed(PWMLED(pin, **opt_args), led_conf[CONF_NAME])
  File "/usr/local/lib/python3.10/site-packages/gpiozero/devices.py", line 108, in __call__
    self = super(GPIOMeta, cls).__call__(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/gpiozero/output_devices.py", line 403, in __init__
    super(PWMOutputDevice, self).__init__(
  File "/usr/local/lib/python3.10/site-packages/gpiozero/output_devices.py", line 83, in __init__
    super(OutputDevice, self).__init__(pin, pin_factory=pin_factory)
  File "/usr/local/lib/python3.10/site-packages/gpiozero/mixins.py", line 85, in __init__
    super(SourceMixin, self).__init__(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/gpiozero/devices.py", line 548, in __init__
    self.pin_factory.reserve_pins(self, pin)
  File "/usr/local/lib/python3.10/site-packages/gpiozero/pins/pi.py", line 91, in reserve_pins
    super(PiFactory, self).reserve_pins(
  File "/usr/local/lib/python3.10/site-packages/gpiozero/pins/pi.py", line 92, in <genexpr>
    requester, *(self.pi_info.to_gpio(pin) for pin in pins))
  File "/usr/local/lib/python3.10/site-packages/gpiozero/pins/__init__.py", line 169, in <lambda>
    lambda self: self._get_pi_info(),
  File "/usr/local/lib/python3.10/site-packages/gpiozero/pins/pi.py", line 116, in _get_pi_info
    self._info = PiBoardInfo.from_revision(self._get_revision())
  File "/usr/local/lib/python3.10/site-packages/gpiozero/pins/data.py", line 1198, in from_revision
    raise PinUnknownPi('unknown old-style revision "%x"' % revision)
gpiozero.exc.PinUnknownPi: unknown old-style revision "64614220"

Some help please ? :woozy_face:

Hello, total noob here - this page has really helped me in getting my PWM cooling fan functional on my HA RPi 4. I managed to follow the instructions including the automations and it seems to be holding my CPU temp steady at ~43C! For reference if anyone’s setting it up for the first time I have:
-Noctua NF-F12 PMW 5V 120mm fan
-3d-printed Raspberry Pi Case with FULL 120mm FAN (Raspberry Pi 4 Case with FULL 120MM FAN by jesperklang - Thingiverse)
-fan pins plugged into GPIO pins 4(5v) / 5(GND) / 10(GPIO18) (for hardware PWM)

I set the PWM frequency to 25000 based on Noctua’s documentation for the fan. It starts up at 10% and runs totally silent. I have it set to ramp up above 40C but run constantly at 15% below.

I ran into an issue with the configuration.yaml formatting. The #RPI fan control needs to go under command_line as seen here:

light:
  - platform: rpi_gpio_pwm
    leds:
      - name: RPI Cooling Fan
        frequency: 25000
        pin: 18

command_line:
# RPi fan control
  sensor:
#  - platform: command_line (original text)
    name: CPU Temperature
    command: "cat /sys/class/thermal/thermal_zone0/temp"
    unit_of_measurement: "°C"
    value_template: '{{ value | multiply(0.001) | round(1) }}'
    scan_interval: 10

Hope this is helpful for someone, y’all have helped me immensely and I look forward to future projects.

1 Like

Hi,
also for me this thread was very helpful sorting my fan control. Thanks to all who contributed.
As my setup is working now I wanted to share my settings.
RPI 4B 8GB with SSD in an Geekworm NASPI 2.0 case.
HA 2023.10.0 Home Assistant Operating System.
To get the fan running I followed these steps:

  1. Install PIGPIO and Pi GPIO PWM as described above.
  2. in configuration.yaml:
command_line:
# NASPI fan control
  sensor:
    name: CPU Temperature
    command: "cat /sys/class/thermal/thermal_zone0/temp"
    unit_of_measurement: "°C"
    value_template: '{{ value | multiply(0.001) | round(1) }}'
    scan_interval: 10

light:
  - platform: rpi_gpio_pwm
    leds:
      - name: RPI Cooling Fan
        pin: 18
        frequency: 25000
  1. in automations.yaml
- id: '1112223334445'
  alias: CPU_Fan_Control
  description: CPU_Fan_Control
  trigger:
  - platform: state
    entity_id:
    - sensor.cpu_temperature
  condition: []
  action:
  - service: light.turn_on
    data:
      brightness: "{% set cpu_temp = states('sensor.cpu_temperature')|float %}
        {% if cpu_temp < 40 %} 130 {% elif cpu_temp > 45 %} 220 {% else %} 150
        {% endif %}"
    target:
      entity_id: light.rpi_cooling_fan
  mode: single

That’s it.
My CPU fan is running now on a minimal speed to have some airflow in the case. When CPU temperature increases due to load or external conditions the fan runs faster to get back to the normal temperature window.

3 Likes