Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Fix/linear pwm adjustment #267

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

jwefers
Copy link

@jwefers jwefers commented Nov 27, 2023

Addressing #266

This throws out the PID approach, instead linearly approaches the desired fan speed, capped to x amount of change per second. On my system, it feelds solid and not unpleasant.

Work is needed in understanding why the scaling into a window of possible (still-rotating) fan speeds is needed, as this is not really hidden from the user when they are supposed to give a step function themselves.

I understand the idea of staying within supported min and max, but i think it defies expectations when still having to give 0-255 for desired values. Maybe percent is better then. Let the user give fan speed percentage values with internally map in between min and max supported. Maybe even raise the min boundary to lowest-still-spinning when neverStop is set. But it should be made transparent to the user what happens with their values internally.

@jwefers
Copy link
Author

jwefers commented Nov 28, 2023

Once i find time, i would like to change this PR to:

  • offering the user a configurable choice between the PID algorithm and the simple linear approach, i.e. have curves have a setting controlAlgorithm: pid|linear with pid being the existing default so that its backwards compatible.

# simple: together with pwmChangePerSecond, fan speeds will approach target value
# with the given max speed. Default is 10
controlAlgorithm:
alg: simple
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's stay with how differentiation of different types is done elsewhere within the config, meaning the type should be the "key" here.

Also, I am not very fond of the name "simple", since it doesn't really explain anything about what its doing. Your initial name for it was "linear", but I don't think thats correct here either, since this only specifies how the pwm values of a fan are adjusted, not to what, so maybe "direct" would be a better fit here?

If so, changing the parameter name to "maxPwmChangePerCycle" could better indicate that this is a modification on the "direct" behavior, which applies once per control loop cycle and puts a "cap/max" on the pwm change from one to the next cycle. Since the length of a cyce is not constant, using "Second" here might not be optimal. Although for a user using a fixed amount of time for this property regardless of how fast the cycle rate is could also be of value, but would make it more difficult to implement the control algorithm, since it would have to transform the limit to multiples of its cycle time, which would not be possible in all configuration variations.... so I would like to stay away from that, at least for the initial implementation.

The "simplest" form of control algorithm should directly apply the calculated target pwm value for each cycle. No other logic involved. I am not even sure adding this "limiter" is a good idea for the simplest form, since its not really direct or linear anymore either.

Examples:

controlAlgorithm:
    direct:
        pwmChangePerSecond: 10
controlAlgorithm:
    pidLoop:
        p: 0.03
        i: 0.002
        d: 0.0005

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it this might not work for types that don't have any configuration properties 🤔 But maybe we can support setting a string ("pidLoop") when using the default values and a map when not.

@@ -228,21 +228,28 @@ func initializeObjects(pers persistence.Persistence) map[fans.Fan]controller.Fan
for config, fan := range initializeFans(controllers) {
updateRate := configuration.CurrentConfig.ControllerAdjustmentTickRate

var pidLoop util.PidLoop
if config.ControlLoop != nil {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically we cannot remove this, but I am not very fond about keeping it forever if we change the structure now.
Maybe we can keep it and add a warning notification, so users are made aware that this is now deprecated.

HwMon *HwMonFanConfig `json:"hwMon,omitempty"`
File *FileFanConfig `json:"file,omitempty"`
Cmd *CmdFanConfig `json:"cmd,omitempty"`
ControlLoop *ControlLoopConfig `json:"controlLoop,omitempty"`
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets mark this deprecated then, see: https://go.dev/wiki/Deprecated

@@ -420,10 +439,11 @@ func (f *PidFanController) calculateTargetPwm() int {

// map the target value to the possible range of this fan
maxPwm := fan.GetMaxPwm()
minPwm := fan.GetMinPwm() + f.minPwmOffset
// minPwm := fan.GetMinPwm() + f.minPwmOffset
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The f.minPwmOffset property is used to make sure a "neverStop" constraint is actually met. If a fan stops spinning when at minPwm, the minPwmOffset should be increased gradually until the fan is spinning again.

Essentially, there is

  1. the value read from the sensor
  2. the value computed by the curve based on the sensor value
  3. the target pwm value computed by the fan controller based on the curve value
  4. the actually applied pwm value, whis is computed by "sanity checks" (like pwm map, neverStop constraints) based on the target pwm of the fan controller.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't change the entire logic of the PidLoop type. If you want to implement a separate logic that is "simpler", please create a separate file for it. The changes you made here should not be part of the git history (of this file) either.

I think we will end up with supporting multiple fan control loops, so a separate package dedicated to exactly this is probably justified. The PidLoop class can then be used on one of the implementations for a fan control loop.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guess that was lost in "commit half-assed state 5 months later, will clean up

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants