What's new
VORON Design

Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members!

Might be useful Pi Fan option

MichaelOToole

Well-known member
After powering up the printer, I noticed my Pi 3 getting a little warm (46° C) even with no activity. It's not excessively warm but we can do better...

Went searching for code that would allow running a fan at a given percentage to keep thing cool on power up but switch to full (or say 50%) during a print cycle...
Now after switching printer on, the Pi stays pretty much at 29° C (with fan running real quiet at 20% speed)...

Code:
# Notes:
#
# fan_generic       = so it can be controlled manually or via delayed_gcode
# heater_fan        = active when heater active, need to define which: heater: heater_name
# controller_fan    = A "controller fan" is a fan that will be enabled whenever its associated heater
#                     or its associated stepper driver is active.
#                     The fan will stop whenever an idle_timeout is reached to ensure no overheating will occur after deactivating a watched component.
#
# Reference: https://www.klipper3d.org/Config_Reference.html?h=controller#controller_fan
#


#[controller_fan fan_name]
#pin: pin_name         # pin name eg. PD14
#max_power: x.x        # max speed 0.1 - 1.0  read as 10 to 100%
#kick_start_time: x.x  # full speed for x time to make sure fan spins up
#shutdown_speed: x.x   # if error occurs, set speed to 0.0 - 1.0 (0 - 100%) issues with this ???
#idle_timeout: 30      # keep fan active for x seconds after heater (extruder) is turned off (soak up the fumes)
#heater: ?             # extruder or heater_bed
#stepper: ?,?,?        # associated stepper eg. stepper_x, stepper_y, stepper_z, stepper_z1 etc...

#shutdown_speed        # By default, a heater_fan has a shutdown_speed equal to max_power


# An attempt to set Pi fan speed to idle at 20% on power up, and 50% when printing... I found the code here:
# https://klipper.discourse.group/t/how-to-get-the-controller-fan-to-turn-on-at-power-up/7313/11
# It works, starts at 20%, switches to 50% on trigger by either extruder or steppers

[duplicate_pin_override]                  # allow reuse of pins
pins: PD12

[fan_generic electronics]                 # must be generic
pin: PD12
max_power: 1.0
shutdown_speed: 1.0
kick_start_time: 1

[delayed_gcode start_fan_at_idle_speed]
initial_duration: 1.0
gcode:
  SET_FAN_SPEED FAN=electronics SPEED=0.2


# Controller fan - FAN0
# pin: PA8
# ...
# Controller fan - FAN1
# pin: PE5
# ...

# Controller fan - FAN2
[controller_fan Pi]
pin: PD12
max_power: 0.5        # only want 50% speed,
kick_start_time: 2.0  # full speed for x to make sure fan spins
shutdown_speed: 0.0   # if error occurs, keep fat at 100%

stepper: stepper_x, stepper_y, stepper_z  # trigger by stepper (to trigger by heater, comment this line out, and un-comment next line
#heater: extruder"                        # trigger by heater

idle_timeout: 30      # keep fan active for x seconds after heater (extruder) is turned off

# Controller fan - FAN3
#[controller_fan Nevermore_(Filter)]   #[heater_fan Nevermore_(Filter)]
#pin: PD13
#max_power: 1.0        # If shutdown_speed is 1.0, max_power must also be 1.0 to ensure fan continues in the even of a fault condition.
#shutdown_speed: 1.0   # max_power must first be set to 1.0 if shutdown_speed is set to 1.0.
#kick_start_time: 3.0  # full speed for x to make sure fan spins
#heater: extruder      # associated heater/stepper
#fan_speed: 0.2        # full speed for x to make sure fan spins
#idle_timeout: 60      # keep fan active for x seconds after heater (extruder) is turned off (soak up the fumes)

# Controller fan - FAN4
[controller_fan Driver_Fan]
pin: PD14
max_power: 0.5        # max speed 100% might be too loud for these fans unless dampened (may change for quieter fans later)...
kick_start_time: 0.5  # full speed for x to make sure fan spins
shutdown_speed: 0.0   # if error occurs, keep fat at 100%
stepper: stepper_x, stepper_y, stepper_z  # trigger on stepper or heater
idle_timeout: 30      # keep fan active for x seconds after heater (extruder) is turned off

# Controller fan - FAN4
# pin: PD15
# ...

# Controller fan - FAN3
[fan_generic Nevermore]   #[heater_fan Nevermore_(Filter)]
pin: PD13
max_power: 1.0        # If shutdown_speed is 1.0, max_power must also be 1.0 to ensure fan continues in the even of a fault condition.
shutdown_speed: 1.0   # max_power must first be set to 1.0 if shutdown_speed is set to 1.0.
kick_start_time: 3.0  # full speed for x to make sure fan spins
#heater: extruder      # associated heater/stepper
#fan_speed: 0.2        # full speed for x to make sure fan spins


# Controller fan - FAN?
#[fan_generic part_cooling]
#pin: EBBCan:gpio13
#max_power: 1.0        # If shutdown_speed is 1.0, max_power must also be 1.0 to ensure fan continues in the even of a fault condition.
#shutdown_speed: 1.0   # max_power must first be set to 1.0 if shutdown_speed is set to 1.0.
#kick_start_time: 3.0  # full speed for x to make sure fan spins


[gcode_macro Part_Cooling_Fan]
gcode:
    {% if printer['fan_generic part_cooling'].speed > 0 %}
      SET_FAN_SPEED FAN=part_cooling SPEED=0
    {% else %}
      SET_FAN_SPEED FAN=part_cooling SPEED=1
    {% endif %}

[gcode_macro Cooling_Fan_PLA]
gcode:
      SET_FAN_SPEED FAN=part_cooling SPEED=1.0

[gcode_macro Cooling_Fan_ASA]
gcode:
      SET_FAN_SPEED FAN=part_cooling SPEED=0.1


[gcode_macro NEVERMORE_ONOFF]
gcode:
    {% if printer['fan_generic Nevermore'].speed > 0 %}
      SET_FAN_SPEED FAN=Nevermore SPEED=0
    {% else %}
      SET_FAN_SPEED FAN=Nevermore SPEED=1
    {% endif %}
 
[gcode_macro NEVERMORE_PLA]
gcode:
      SET_FAN_SPEED FAN=Nevermore SPEED=0.25

[gcode_macro NEVERMORE_ASA]
gcode:
      SET_FAN_SPEED FAN=Nevermore SPEED=1.0
The link to original code is included in config.

Pi-Electronics_2024-04-13_18-32-31.png
Idle... Electronics = Pi fan (idle speed at 20%)

Pi-Electronics_2024-04-13_18-42-01.png
Active... Pi = 100% (actually 50% as max speed is set to 50%)

Pi becomes active after any xyz motor movement in my case but it could be after a heater heats up (see cfg code), also note, Pi & Electronics fan use the same pin PD12

[duplicate_pin_override] is intended for debugging/testing (I believe) but it came in handy here.
There is also mention of unpredictable affects, so testing is advisable...

Of course you could just connect another fan to 5V or 24V...
 
Last edited:
Interesting...

I ran your test on my machine, in what I considered might be a worst case condition - running a long print, then restarting Klipper (to reset the logic that keeps the electronics fans on when steppers or heaters are active), then checking the Pi's temp later when everything around it has "soaked" with no fans, pre-existing internal heat, and a big radiant heat bed above that still has some warmth. In this state, the Pi hit 52.1C at the highest point.

That actually sounds fine to me. The Pi 3 B+ will "soft limit" at 60C (dropping from 1.4Ghz to 1.2GHz), and then throttle more aggressively at 80C, so even that torture test at 52.1C isn't of major concern.


Working through your config though, I did find a couple things worth mentioning.

First, it seems like the use of [duplicate_pin_override] at all is pretty strongly discouraged? It bypasses error checks, and is noted as possibly having unexpected results. That sounds a bit risky for something as important as the electronics bay fans... (https://www.klipper3d.org/Config_Reference.html?h=display#duplicate_pin_override)

Second, is there any info as to what happens at the software/hardware level when two PWM outputs have this override and are working against each other? Is there some mathematical function (or simply a clobbering by whichever command was last) BEFORE the PWM generation? Or is there possible aliasing of two simultaneous PWM signals each trying to continuously overwrite each other? (Having the 20% "idle" speed or some other odd value override the 50% "printing" fan speed would be of much more concern than having the Pi be slightly warm at idle...)

And third, I believe to complete your intended operation, you might also need some gcode to trigger this idle condition AFTER a print is completed? Otherwise, I believe what will happen is that the fans will remain on moderate speed (the "printing" speed) until the steppers eventually release by timeout, at which point the fans would turn off completely... putting you back in your "cold start / Pi is warmer than you would like" condition.

Thanks for sharing, there's a lot to learn about Klipper, so it's always fun to have a reason to dig a little deeper. (y)
 
I've been running with it for a week now, printed several prints and no issues so far.

As I mention above, [duplicate_pin_override] was intended for debug/testing (it may well end up being allowed in normal operation in some fashion in future code I gather).
As to "This section is not needed where Klipper supports using the same pin multiple times"... currently, it doesn't appear to support this, I wonder what they mean here?

I did notice that after a print the Pi fan continued at speed until another action, in an ideal world it would drop down to idle speed after idle_timeout.
And third, I believe to complete your intended operation, you might also need some gcode to trigger this idle condition AFTER a print is completed
I do but I am not familiar with gcode yet ... perhaps later...

Might be able to call ELECTRONICS_SET_IDLE from PRINT_END ?

Looks like you can control fan_generic from gcode but not controller_fan but it's probably a better bet to use [temperature_fan] for Pi control...
 
Last edited:
Pi Fan Control:

This is probably a better option, just use the existing Pi fan temperature sensor to control the Pi Fan...

Code:
[temperature_fan Pi_Fan]       # FAN2
pin: PD12                      #
control: watermark             #
max_delta: 3.0                 #
shutdown_speed: 1              # Max speed in the event of an error
sensor_type: temperature_host  # use this sensor
min_temp: 0                    # range min anything under is an error
max_temp: 100                  # range max anything over is an error
target_temp: 30.0              # activate at this temperature (- delta)
min_speed: 0.2                 # only works with pid?
max_power: 0.6                 # restrict to x% (keep it quiet, no need for max speed)
shutdown_speed: 0.0            # in event of shutdown stop or max

PiFanControl.png
We can even modify the target temperature at which the fan powers up on the fly...
Only down side is you don't appear to be able to set min_speed, if I could, I would remove the previous [duplicate_pin_override] code.
 
Last edited:
That's probably a much more elegant solution than my kludged-together macros. I just use the stock electronics chamber fans and turn them on and off based on the Pi's reported temperature. Which is of course what that temperature_fan definition does but using Klipper to manage it. I'm going to have to try tweaking my configs and see if that works.
 
I just use the stock electronics chamber fans and turn them on and off based on the Pi's reported temperature.
You probably shouldn't do that... The electronics chamber fans cool not only the Pi, but also the control board, power supplies, the SSR for the heated bed, etc. Since the power supplies in most kits seem to be fanless, and the SSR has no dedicated fan or temperature monitoring and is run at its max current limit, those parts need guaranteed airflow when operating under load. Allowing the Pi's temp to turn off the electronics bay fans runs the risk of cutting airflow when other components need it...
 
In a perfect world you would monitor
  1. SSR Temperature...
  2. Motor Drivers Temperature...
  3. Pi Temperature...
You can combine sensors to obtain an average... the electronics bay temperature?
Code:
sensor_type: temperature_combined
#sensor_list:
#   Must be provided. List of sensors to combine to new "virtual"
#   sensor.
#   E.g. 'temperature_sensor sensor1,extruder,heater_bed'
#combination_method:
#   Must be provided. Combination method used for the sensor.
#   Available options are 'max', 'min', 'mean'.
#maximum_deviation:
#   Must be provided. Maximum permissible deviation between the sensors
#   to combine (e.g. 5 degrees). To disable it, use a large value (e.g. 999.9)

On the Octopus, we can use thermistor inputs PF5, PF6 and PF7 and fan drivers PD12, PD13, PD14
As we only have 3 inputs, we can only control 3 fans but adding temperature_combined (virtual) gives us another...

Code:
# Display the temperature for a given sensor.
[temperature_sensor MyNameIs]
sensor_type:  Generic 3950
sensor_pin:   PF5

I ended up using this code:
Control the Pi Fan based on generic sensor...
Control SSR Fan based on another generic sensor...
The 2209's Fan is triggered by any movement of x,y,z...

Code:
# FAN2
[temperature_fan Pi_Fan]
pin: PD12                      #
control: watermark             #
max_delta: 3.0                 #
shutdown_speed: 1.0            # Max speed in the event of an error
sensor_type: temperature_host  # use this sensor
min_temp: 0                    # range min anything under is an error
max_temp: 100                  # range max anything over is an error
target_temp: 30.0              # activate at this temperature (- delta)
min_speed: 0.2                 # only works with pid?
max_power: 0.6                 # restrict to x% (keep it quiet, no need for max speed)
shutdown_speed: 0.0            # in event of shutdown stop or max

# FAN4
[temperature_fan SSR_Fan]
pin: PD14                      #
sensor_pin: PF5                #
control: watermark             #
max_delta: 3.0                 #
shutdown_speed: 1.0            # Max speed in the event of an error
sensor_type: Generic 3950      # use this sensor
min_temp: 0                    # range min anything under is an error
max_temp: 100                  # range max anything over is an error
target_temp: 30.0              # activate at this temperature (- delta)
min_speed: 0.2                 # only works with pid?
max_power: 0.6                 # restrict to x% (keep it quiet, no need for max speed)
shutdown_speed: 0.0            # in event of shutdown stop or max


# Option 1: use this code if triggered on stepper or heater

# FAN5
[controller_fan Motors_Fan]
pin: PD15
max_power: 1.0 #0.5                       # max speed 100% might be too loud for these fans unless dampened (may change for quieter fans later)...
kick_start_time: 0.5                      # full speed for x to make sure fan spins
shutdown_speed: 1.0                       # if error occurs, keep fat at 100%
stepper: stepper_x, stepper_y, stepper_z  # trigger on stepper or heater
idle_timeout: 30                          # keep fan active for x seconds after heater (extruder) is turned off

# Option 2: use this code if triggered with sensor

# FAN5
[temperature_fan Motors_Fan]
pin: PD15                      #
sensor_pin: PF6                #
control: watermark             #
max_delta: 3.0                 #
shutdown_speed: 1.0            # Max speed in the event of an error
sensor_type: Generic 3950      # use this sensor
min_temp: 0                    # range min anything under is an error
max_temp: 100                  # range max anything over is an error
target_temp: 30.0              # activate at this temperature (- delta)
min_speed: 0.2                 # only works with pid?
max_power: 0.6                 # restrict to x% (keep it quiet, no need for max speed)
shutdown_speed: 0.0            # in event of shutdown stop or max

VoronAllTemps.png
Shame temperature_fan > min_speed only works with pid, otherwise it would be ideal...

Note, BTT-PI shows Pi temperature as does Pi Fan but Pi Fan is controllable from the drop-down while BTT-Pi is controlled in code...
There are advantages to both so I included both...
 
Last edited:
I've had the same issue/concern, but I'm quite averse to modifying the electronics bay fan config for the same reason @Dave32 mentioned (and the LDO-supplied fans are noisy af.)
As an alternative, I'm considering a dedicated 5v 40x10 noctua fan connected to gpio...
 
Ideal:
In a perfect world, we would run the fans at speeds proportional to temperatures, only running full speed when entirely necessary, otherwise run at about 20% to keep everything quiet. If you could map speeds to temperatures that would be ideal, but the best we can do is ramp up if temperature exceeds x ° and slow below that.

My compromise:
I had four 3950 NTC 100K Thermistor from previous project (all four cost less than €5 including shipping), so I decided to monitor the main areas and to control two additional (60mm Noctuas) fans mounted in the spare fan locations in the skirt

I left the original 60mm fans as is but limited them to 60% which keeps them relatively quiet, basically, four fans running slower (and quieter), is way better than two fans at full speed and annoying noise levels...

Just ordered four more thermistors to build a small circuit to control four fans proportional to temperatures read by their respective thermistor, a little Arduino or ESP32 should do the trick. It will be independent but I guess it could be integrated too, but that's for another day...
 
Last edited:
Top