Big Picture - Klipper’s Fan Control

I agree the PID implementation is odd and counter intuitive.
To the quote above though, there is a setting for that.

#min_speed: 0.3
#   The minimum fan speed (expressed as a value from 0.0 to 1.0) that
#   the fan will be set to for PID temperature fans.
#   The default is 0.3.

If you can explain in particular what you’re trying to do/want the fans to do, it would help develop a robust solution for everyone.

In my point of view, the minimum fan speed in this case (PID) is only linked to the fan’s motor characteristics and can be defined as the minimum duty cycle of the PWM control that can successfully spin the fan’s motor: This limit has indeed 2 different values: One is the duty cycle needed to re-start the motor if it is stopped and the other is the lowest duty cycle at which the motor still spins once launched.

The kick_start_time option is here to help spinning the motor when stopped, so if a suitable value is defined for it, the minimum fan speed can be much lower than without a kick-start.

I agree that a PID isn’t the best way to control something that is non-linear by nature: Cooling power isn’t proportional to PWM duty cycle (unlike a heater). And there is no way to get anything between the minimum fan speed and no fan at all… For this range, we are forced to go back to a bang-bang that can be dampened only by accepting a temperature tolerance around the reference value, which is included in the new code.

I totally agree that a fan curve definition is probably a more solid solution… I only attempted to get something with what was already coded, but misinterpreted in my opinion.

With the new code version, I’m able to get a totally stable temperature, at ±0.5°C of the reference, out of my radiator with an equally stable fan duty cycle (at much more than minimum fan speed) as long as there is some heat produced. Once the heat production stops, the cooling fan slows down and stops with a delay as the pump still runs until the head is below 50°C.

So I got exactly what I expected: a continuously running and reasonably silent fan with a constant coolant temperature during my prints, and no noise at all when the printer has cooled down.

You’ve hit on a few key points that I’ve been working on a solution for.

I’ve been talking with some of the other members about fan functionality and been working on some “updates”.

Let me break down some of them so far:


the minimum fan speed in this case (PID) is only linked to the fan’s motor characteristics and can be defined as the minimum duty cycle of the PWM control that can successfully spin the fan’s motor

This is exactly right and I think is the source of the requests for “Normalized PWM”, because realistically below 20% Duty Cycle, most fans don’t spin. So by that very fact what users see on Mainsail/Fluidd they expect to be “% of max fan RPM” when it’s actually “% duty cycle” and duty cycle doesn’t correspond to max RPM.

The “Defacto” standard for small DC fans (2, 3 and 4 wire) are detailed in the white paper here (it says 4 wire but it really applies to all by the nature of fan characteristics):

https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/intel-4wire-pwm-fans-specs.pdf

On page 14 there is a graph shown below. Notice that below 20% duty cycle is “undefined” per the standard. Another user pointed out that in section 3.2 the “minimum RPM should be 30% of max RPM” but generally there is no way to know this per fan (unless it’s a tach fan)

To bring things more in line with a “normalized PWM” based on what users would expect and to better match that Intel standard I added a “scaling factor” in the code.

        #Scale the value so no PWM below 20% duty cycle
        #This complies with Intel standard of PWM fans (the defacto standard)
        #See page 14 of "intel-4wire-pwn-fans-specs.pdf"
        fan_speed = .2 + .8 * value
        fan_speed = 0 if fan_speed <= .2 else fan_speed

This is transparent to the end user as the “fan_speed” isn’t what is recorded and displayed on Mainsail/Fluidd, that’s the commanded speed.

In otherwords, User/Code says .5 (50%) fan speed.

.8 * .5 = .4 + .2 = .6 = 60% actual duty cycle

This needs testing (might need to bump it up to .25 or even .3) but it’s worked better than expected from my prelim tests. I’ll release a test patch for this and other tweaks for people to try out most likely this weekend.


A few other things I’ve added:

  • A min temp cut off for temp/heater/stepper fans with hysteresis. For people who want quieter fans when not needed.

    • The Min temp cutoff has a max value of 65c so people can’t burn their house down, might lower it from that depending on feedback.

    • Also, The fan by default turns off when the temp is ambient temp, but I might just make that optional or have it the default option unless the user specifies a “min speed” or some combination there of.

  • The fan curves described above, when I post the patch I’ll describe the logic in more detail

  • A prelim fix for M106/M107 that supports multiple part cooling fans by the RepRap/Slicer standard implementation.

    • Added a slight tweak to M106 to future proof it against slicers switching to 0.0 to 1.0 fan speeds instead of the 0 to 255 current implementation. RepRap standard supports both and was a “fix it while I’m already in here” thing. Kevin may say no all the fan patches including this, so we’ll see.

Works in progress (hopefully this weekend):

  • Tach stall/signal loss options - My prelim thoughts on the logic are…

    • Tach signal has failed
      • Is fan configured with a heater? If so, run fan at 100% and shut down Klipper. “Fail safe”. Non negotiable logic there.
      • Is fan configured to a temp sensor/stepper? Give periodic warning in Gcode console (user definable delay with default setting most likely)
      • Anything else I might just give a one time warning and let the user notice it in the UI
  • For 3 wire / 4 wire fans - Have a “calibration” option to map the PWM duty cycle to the measured fan RPM and save it in the “SAVE_CONFIG” so users get the full benefit of using a tach sensor.

    • There are some hurdles to this that I’ll need to figure out but I think I have an idea on how to implement it and not clujtter up code or slow down existing methods/functions.

I see that things are going on about this question.

To make things right, the fan type (2,3,4 wire) should be defined somewhere:

  • Even 2 wire fans can behave differently, depending on the onboard electronics:

    • Some will run directly with a PWM input, at least if the PWM frequency is within some limits. So the PWM frequency should be a tunable parameter? Not all fans will run at 25kHz… A much lower value (25Hz or less) might be required for brushless fans. (bang-bang on the fan’s electronic phase shifter)

    • All should run if the PWM is smoothed with a RLC filter to get a DC voltage… RLC values will depend on the PWM frequency and required power… This means that some custom additional hardware is needed between the mcu power stage (that is supposed to be switchable by PWM) and the fan. In this case, a high PWM frequency will make things easier and hardware smaller and cheaper. This is technically the best way to control a brushless DC fan, which must have some onboard electronics designed for DC supply to switch the motor’s phases. Obviously the min. PWM duty cycle in this case is corresponding to the min. DC voltage to power the electronic controller, and have a ‘corrected’ true duty cycle make sense.

    • For this type of fan, if you don’t use the supplier’s fan power curve, the cooling power you get from your PWM duty cycle is a guess, if you use it, it’s a wish… Nothing more. Safe use implies the monitoring of another parameter (temperature) and action if the deviation from the expected result is recorded…

    *Despite this 2-wire fans are extremely popular as they are much cheaper than their 3 or 4 wire counterparts.

  • 3 and 4-wire fans have a tachometer feedback, so the measured rpm can be used for regulation and/or safety. Generally, the motor itself detects a locked rotor condition and stops feeding the coils. Tach output is generally logic level for 3.3V, but you must be sure that it’s not above the mcu input level… A tach pin must be defined in this case.

  • 4 wire fan will use a PWM signal for regulation: In this case you must respect the fan’s requirements and limits: Logic level of the signal and frequency of the PWM
    Frequency is more or less standard at 25 kHz… But in this case, the PWM action on the fan speed can be regulated (50% duty is 50% rpm) or not… an additional parameter to consider with the required PWM control pin…

The goal is generally to get some cooling effect, and that’s eventually the parameter that must be tuned by changing the fan’s rpm by changing its power supply (2-3 wire) or PWM signal(4-wire). So the ‘calibration’ map should have the cooling power on one side and the fan’s PWM duty on the other. It must be computed from the fan curve and rpm-PWM curve that is specific to each installation (depending on the measured pressure vs fan RPM curve). So to get the cooling power, if the fan is ducted, we need the pressure behind the fan to have the operating point on the fan curve: Let’s install a $50 differential pressure sensor and monitor its value…
It’s going quite far…

All this might be justified to make Klipper more ‘universal’. My PR is just a fast and dirty fix to have something to work… The present implementation of the controlPID has no logic to me, with the fan at full speed when the temperature difference is null, and does not correspond to the definition of a ‘temperature-triggered cooling fan’.

You nailed it. I’ve done a “limited” release of a patch of some fan fixes I put together, limited for now for major bug checks but soon I’ll release for everyone to try out if they want.

But the relevant portion of that “patch” is below and I think covers your concerns.


“Normalized” PWM Response

Per the Intel fan spec (which applies really to most fans not just PWM fans), PWM duty cycle below 20% is “undefined” and below 30% is frowned upon.

https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/intel-4wire-pwm-fans-specs.pdf

4 wire PWM fans fix this in the fan circuitry, but normal brushless DC fans do not:

I’ve updated the “set_speed” to take this into account via a “scaling factor”.
It scales the commanded fan speed up where the new “0-100%” range is actually the old “25% - 100%” range.

This is transparent to the end user.
In other words, If the user uses the UI or GCode to command 40% fan speed the UI will show 40% fan speed.
But in the background it will actually be (.75 * .4) =.3 + .25 = 55% on the “old scale”

This does NOT apply if users are using a 4 Wire PWM fan (as defined by having “enable pin” defined). As those fans automatically do this and it skews their speed weirdly.

I found this change to work much better than I actually anticipated.
I think it better aligns with most users expectations of “If I say 40% I mean 40% of max RPM, not 40% duty cycle”.


Edit a few more:


Tachometer Failsafe/Warning

Added several new configuration options:

tach_loss_action: ('shutdown','warning','none'), default = 'shutdown'
tach_loss_interval: How many seconds before action is taken, default = 3
tach_warning_repeat_interval: Needs a better name, but is obvious. Default = loss interval

These are fairly self explanitory but if the tach signal is lost for over 3 seconds or the defined interval it either:

  • Shuts down Klipper - This is the default and the ENFORCED action for heater fans
  • Puts a warning on the GCode Console “Warning: “fan_name” has lost tach signal for longer than “tach_loss_interval” seconds!”
  • Does nothing

The time frame is based on the “eventtime” of the get_status method/frequency counter call back so should be in real world seconds.


Temperature Fan Changes


Minimum Temp Cutoff

Added a new config option

min_temp_cutoff: default = 0, max_value = 65

This one is fairly obvious, added a .5% hysteresis around the cut off point to keep the fan from blipping on and off.

Also added a new parameter to the “set_speed” method to specify if the speed command is due to the temp cut off, this way it overrides the “min_speed” but still allows for both.

If this gets picked up I’ll make sure that’s in the config reference.


I know there are more improvements needed and there are a few more in this “test patch” but it moves towards a smarter/better fan IMO

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.