Skip to content

Commit 661543e

Browse files
authored
Merge pull request #267 from jwefers/fix/linearPwmAdjustment
WIP: Fix/linear pwm adjustment
2 parents 3555570 + de1d870 commit 661543e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+766
-339
lines changed

.github/workflows/go.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ jobs:
2222

2323
- uses: actions/setup-go@v5
2424
with:
25-
go-version: '^1.18'
25+
go-version: '^1.23'
2626

2727
- name: Run golangci-lint
2828
uses: golangci/[email protected]
2929
with:
3030
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
31-
version: v1.54.2
31+
version: v1.61.0
3232

3333
# Optional: working directory, useful for monorepos
3434
# working-directory: somedir

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ PACKAGE := github.com/markusressel/$(NAME)
77
GIT_REV ?= $(shell git rev-parse --short HEAD)
88
SOURCE_DATE_EPOCH ?= $(shell date +%s)
99
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
10-
VERSION ?= 0.8.1
10+
VERSION ?= 0.9.0
1111

1212
test: ## Run all tests
1313
@go clean --testcache && go test -v ./...

README.md

+49-10
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ curves:
373373

374374
Unlike the other curve types, this one does not use the average of the sensor data
375375
to calculate its value, which allows you to create a completely custom behaviour.
376-
Keep in mind though that the fan controller is also PID based and will also affect
376+
Keep in mind though that the fan controller may also be PID based which could also affect
377377
how the curve is applied to the fan.
378378

379379
#### Function
@@ -441,7 +441,7 @@ After successfully verifying your configuration you can launch fan2go from the C
441441
working as expected. Assuming you put your configuration file in `/etc/fan2go/fan2go.yaml` run:
442442

443443
```shell
444-
> fan2go help 2 (0.032s) < 22:43:49
444+
> fan2go help
445445
fan2go is a simple daemon that controls the fans
446446
on your computer based on temperature sensors.
447447
@@ -654,23 +654,62 @@ sensor value.
654654

655655
## Fan Controllers
656656

657-
Fan speed is controlled by a PID controller per each configured fan. The default
657+
The speed of a Fan is controlled using a combination of its curve, a control algorithm and the properties of
658+
the fan controller itself.
659+
660+
The curve is used as the target value for the control algorithm to reach. The control algorithm then calculates the
661+
next PWM value to apply to the fan to reach this target value. The fan controller then applies this PWM value to the
662+
fan, while respecting constraints like the minimum and maximum PWM values, as well as the `neverStop` flag.
663+
664+
### Control Algorithms
665+
666+
A control algorithm
667+
is a function that returns the next PWM value to apply based on the target value calculated by the curve. The simplest
668+
control algorithm is the direct control algorithm, which simply forwards the target value to the fan.
669+
670+
#### Direct Control Algorithm
671+
672+
The simplest control algorithm is the direct control algorithm. It simply forwards the curve value to the fan
673+
controller.
674+
675+
```yaml
676+
fans:
677+
- id: some_fan
678+
...
679+
controlAlgorithm: direct
680+
```
681+
682+
This control algorithm can also be used to approach the curve value more slowly:
683+
684+
```yaml
685+
fans:
686+
- id: some_fan
687+
...
688+
controlAlgorithm:
689+
direct:
690+
maxPwmChangePerCycle: 10
691+
```
692+
693+
### PID Control Algorithm
694+
695+
The PID control algorithm uses a PID loop to approach the target value. The default
658696
configuration is pretty non-aggressive using the following values:
659697

660-
| P | I | D |
661-
|--------|---------|----------|
662-
| `0.03` | `0.002` | `0.0005` |
698+
| P | I | D |
699+
|-------|--------|---------|
700+
| `0.3` | `0.02` | `0.005` |
663701

664702
If you don't like the default behaviour you can configure your own in the config:
665703

666704
```yaml
667705
fans:
668706
- id: some_fan
669707
...
670-
controlLoop:
671-
p: 0.03
672-
i: 0.002
673-
d: 0.0005
708+
controlAlgorithm:
709+
pid:
710+
p: 0.3
711+
i: 0.02
712+
d: 0.005
674713
```
675714

676715
The loop is advanced at a constant rate, specified by the `controllerAdjustmentTickRate` config option, which

cmd/curve/list.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var curveCmd = &cobra.Command{
2626

2727
err = configuration.Validate(configPath)
2828
if err != nil {
29-
ui.FatalWithoutStacktrace(err.Error())
29+
ui.FatalWithoutStacktrace("configuration validation failed: %v", err)
3030
}
3131

3232
curveConfigsToPrint := []configuration.CurveConfig{}
@@ -146,7 +146,7 @@ func drawGraph(graphValues map[int]float64, caption string) {
146146
}
147147

148148
graph := asciigraph.Plot(values, asciigraph.Height(15), asciigraph.Width(100), asciigraph.Caption(caption))
149-
ui.Printfln(graph)
149+
ui.Println(graph)
150150
}
151151

152152
func printFunctionCurveInfo(curve curves.SpeedCurve, config *configuration.FunctionCurveConfig) {
@@ -195,7 +195,7 @@ func printInfoTable(headers []string, rows [][]string) {
195195
panic(tableErr)
196196
}
197197
tableString := buf.String()
198-
ui.Printfln(tableString)
198+
ui.Println(tableString)
199199
}
200200

201201
func init() {

cmd/detect.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ var detectCmd = &cobra.Command{
122122
}
123123
tableString := buf.String()
124124
if idx < (len(tables) - 1) {
125-
ui.Printf(tableString)
125+
ui.Print(tableString)
126126
} else {
127-
ui.Printfln(tableString)
127+
ui.Println(tableString)
128128
}
129129
}
130130
}

cmd/fan/curve.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var curveCmd = &cobra.Command{
2424
configuration.LoadConfig()
2525
err := configuration.Validate(configPath)
2626
if err != nil {
27-
ui.FatalWithoutStacktrace(err.Error())
27+
ui.FatalWithoutStacktrace("%v", err)
2828
}
2929

3030
persistence := persistence.NewPersistence(configuration.CurrentConfig.DbPath)
@@ -45,7 +45,7 @@ var curveCmd = &cobra.Command{
4545

4646
pwmData, fanCurveErr := persistence.LoadFanPwmData(fan)
4747
if fanCurveErr == nil {
48-
_ = fan.AttachFanCurveData(&pwmData)
48+
_ = fan.AttachFanRpmCurveData(&pwmData)
4949
}
5050

5151
if idx > 0 {
@@ -54,7 +54,7 @@ var curveCmd = &cobra.Command{
5454
}
5555

5656
// print table
57-
ui.Printfln(fan.GetId())
57+
ui.Println(fan.GetId())
5858
tab := table.Table{
5959
Headers: []string{"", ""},
6060
Rows: [][]string{
@@ -78,11 +78,11 @@ var curveCmd = &cobra.Command{
7878
panic(tableErr)
7979
}
8080
tableString := buf.String()
81-
ui.Printfln(tableString)
81+
ui.Println(tableString)
8282

8383
// print graph
8484
if fanCurveErr != nil {
85-
ui.Printfln("No fan curve data yet...")
85+
ui.Println("No fan curve data yet...")
8686
continue
8787
}
8888

@@ -99,7 +99,7 @@ var curveCmd = &cobra.Command{
9999

100100
caption := "RPM / PWM"
101101
graph := asciigraph.Plot(values, asciigraph.Height(15), asciigraph.Width(100), asciigraph.Caption(caption))
102-
ui.Printfln(graph)
102+
ui.Println(graph)
103103
}
104104
},
105105
}

cmd/fan/fan.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func getFan(id string) (fans.Fan, error) {
3535
configuration.LoadConfig()
3636
err := configuration.Validate(configPath)
3737
if err != nil {
38-
ui.FatalWithoutStacktrace(err.Error())
38+
ui.FatalWithoutStacktrace("%v", err)
3939
}
4040

4141
controllers := hwmon.GetChips()

cmd/fan/init.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ package fan
22

33
import (
44
"github.com/markusressel/fan2go/internal/configuration"
5+
"github.com/markusressel/fan2go/internal/control_loop"
56
"github.com/markusressel/fan2go/internal/controller"
67
"github.com/markusressel/fan2go/internal/persistence"
78
"github.com/markusressel/fan2go/internal/ui"
8-
"github.com/markusressel/fan2go/internal/util"
99
"github.com/spf13/cobra"
1010
"github.com/spf13/viper"
1111
)
@@ -31,12 +31,9 @@ var initCmd = &cobra.Command{
3131
fanController := controller.NewFanController(
3232
p,
3333
fan,
34-
*util.NewPidLoop(
35-
0.03,
36-
0.002,
37-
0.0005,
38-
),
39-
configuration.CurrentConfig.ControllerAdjustmentTickRate)
34+
control_loop.NewDirectControlLoop(nil),
35+
configuration.CurrentConfig.ControllerAdjustmentTickRate,
36+
)
4037

4138
ui.Info("Deleting existing data for fan '%s'...", fan.GetId())
4239

cmd/root.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ on your computer based on temperature sensors.`,
3333
configuration.LoadConfig()
3434
err := configuration.Validate(configPath)
3535
if err != nil {
36-
ui.ErrorAndNotify("Config Validation Error", err.Error())
36+
ui.ErrorAndNotify("Config Validation Error: %v", "%v", err)
3737
return
3838
}
3939

@@ -75,6 +75,7 @@ func printHeader() {
7575
if err != nil {
7676
fmt.Println("fan2go")
7777
}
78+
ui.Info("Version: %s", global.Version)
7879
}
7980

8081
// Execute adds all child commands to the root command and sets flags appropriately.

cmd/sensor/sensor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func getSensor(id string) (sensors.Sensor, error) {
5252
configuration.LoadConfig()
5353
err := configuration.Validate(configPath)
5454
if err != nil {
55-
ui.FatalWithoutStacktrace(err.Error())
55+
ui.FatalWithoutStacktrace("%v", err)
5656
}
5757

5858
controllers := hwmon.GetChips()

fan2go.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ fans:
4646
# The curve ID (defined above) that should be used to determine the
4747
# speed of this fan
4848
curve: cpu_curve
49+
# (Optional) The algorithm how the target speed, determined by the curve is approached.
50+
# direct: the target value will be directly applied to the fan
51+
# pid: uses a PID loop with default tuning variables
52+
controlAlgorithm:
53+
direct:
54+
# together with maxPwmChangePerCycle, fan speeds will approach target value
55+
# with the given max speed.
56+
maxPwmChangePerCycle: 10
4957
# (Optional) Override for the lowest PWM value at which the
5058
# fan is able to maintain rotation if it was spinning previously.
5159
minPwm: 30
@@ -76,6 +84,7 @@ fans:
7684
hwmon:
7785
platform: it8620
7886
rpmChannel: 4
87+
controlAlgorithm: direct
7988
neverStop: true
8089
curve: case_avg_curve
8190

go.mod

+19-19
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
module github.com/markusressel/fan2go
22

3-
go 1.22
4-
5-
toolchain go1.22.5
3+
go 1.23.1
64

75
require (
86
github.com/asecurityteam/rolling v2.0.4+incompatible
@@ -13,15 +11,18 @@ require (
1311
github.com/md14454/gosensors v0.0.0-20180726083412-bded752ab001
1412
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
1513
github.com/mitchellh/go-homedir v1.1.0
14+
github.com/mitchellh/mapstructure v1.5.0
1615
github.com/oklog/run v1.1.0
16+
github.com/orcaman/concurrent-map/v2 v2.0.1
1717
github.com/prometheus/client_golang v1.20.4
1818
github.com/pterm/pterm v0.12.79
19+
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494
1920
github.com/spf13/cobra v1.8.1
2021
github.com/spf13/viper v1.19.0
2122
github.com/stretchr/testify v1.9.0
2223
github.com/tomlazar/table v0.1.2
2324
go.etcd.io/bbolt v1.3.11
24-
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
25+
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
2526
)
2627

2728
require (
@@ -30,45 +31,44 @@ require (
3031
atomicgo.dev/schedule v0.1.0 // indirect
3132
github.com/beorn7/perks v1.0.1 // indirect
3233
github.com/cespare/xxhash/v2 v2.3.0 // indirect
33-
github.com/containerd/console v1.0.3 // indirect
34+
github.com/containerd/console v1.0.4 // indirect
3435
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
3536
github.com/fsnotify/fsnotify v1.7.0 // indirect
3637
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
3738
github.com/gookit/color v1.5.4 // indirect
3839
github.com/hashicorp/hcl v1.0.0 // indirect
3940
github.com/inconshreveable/mousetrap v1.1.0 // indirect
40-
github.com/klauspost/compress v1.17.9 // indirect
41+
github.com/klauspost/compress v1.17.10 // indirect
4142
github.com/labstack/gommon v0.4.2 // indirect
4243
github.com/lithammer/fuzzysearch v1.1.8 // indirect
4344
github.com/magiconair/properties v1.8.7 // indirect
4445
github.com/mattn/go-colorable v0.1.13 // indirect
4546
github.com/mattn/go-isatty v0.0.20 // indirect
46-
github.com/mattn/go-runewidth v0.0.15 // indirect
47-
github.com/mitchellh/mapstructure v1.5.0 // indirect
47+
github.com/mattn/go-runewidth v0.0.16 // indirect
4848
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
49-
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
49+
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
5050
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
5151
github.com/prometheus/client_model v0.6.1 // indirect
52-
github.com/prometheus/common v0.55.0 // indirect
52+
github.com/prometheus/common v0.59.1 // indirect
5353
github.com/prometheus/procfs v0.15.1 // indirect
54-
github.com/rivo/uniseg v0.4.4 // indirect
55-
github.com/sagikazarmark/locafero v0.4.0 // indirect
54+
github.com/rivo/uniseg v0.4.7 // indirect
55+
github.com/sagikazarmark/locafero v0.6.0 // indirect
5656
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
5757
github.com/sourcegraph/conc v0.3.0 // indirect
5858
github.com/spf13/afero v1.11.0 // indirect
59-
github.com/spf13/cast v1.6.0 // indirect
59+
github.com/spf13/cast v1.7.0 // indirect
6060
github.com/spf13/pflag v1.0.5 // indirect
6161
github.com/subosito/gotenv v1.6.0 // indirect
6262
github.com/valyala/bytebufferpool v1.0.0 // indirect
6363
github.com/valyala/fasttemplate v1.2.2 // indirect
6464
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
6565
go.uber.org/multierr v1.11.0 // indirect
66-
golang.org/x/crypto v0.24.0 // indirect
67-
golang.org/x/net v0.26.0 // indirect
68-
golang.org/x/sys v0.22.0 // indirect
69-
golang.org/x/term v0.21.0 // indirect
70-
golang.org/x/text v0.16.0 // indirect
71-
golang.org/x/time v0.5.0 // indirect
66+
golang.org/x/crypto v0.27.0 // indirect
67+
golang.org/x/net v0.29.0 // indirect
68+
golang.org/x/sys v0.25.0 // indirect
69+
golang.org/x/term v0.24.0 // indirect
70+
golang.org/x/text v0.18.0 // indirect
71+
golang.org/x/time v0.6.0 // indirect
7272
google.golang.org/protobuf v1.34.2 // indirect
7373
gopkg.in/ini.v1 v1.67.0 // indirect
7474
gopkg.in/yaml.v3 v3.0.1 // indirect

0 commit comments

Comments
 (0)