Skip to content

Commit

Permalink
Merge pull request #267 from jwefers/fix/linearPwmAdjustment
Browse files Browse the repository at this point in the history
WIP: Fix/linear pwm adjustment
  • Loading branch information
markusressel authored Sep 28, 2024
2 parents 3555570 + de1d870 commit 661543e
Show file tree
Hide file tree
Showing 48 changed files with 766 additions and 339 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ jobs:

- uses: actions/setup-go@v5
with:
go-version: '^1.18'
go-version: '^1.23'

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

# Optional: working directory, useful for monorepos
# working-directory: somedir
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ PACKAGE := github.com/markusressel/$(NAME)
GIT_REV ?= $(shell git rev-parse --short HEAD)
SOURCE_DATE_EPOCH ?= $(shell date +%s)
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
VERSION ?= 0.8.1
VERSION ?= 0.9.0

test: ## Run all tests
@go clean --testcache && go test -v ./...
Expand Down
59 changes: 49 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ curves:

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

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

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

## Fan Controllers

Fan speed is controlled by a PID controller per each configured fan. The default
The speed of a Fan is controlled using a combination of its curve, a control algorithm and the properties of
the fan controller itself.

The curve is used as the target value for the control algorithm to reach. The control algorithm then calculates the
next PWM value to apply to the fan to reach this target value. The fan controller then applies this PWM value to the
fan, while respecting constraints like the minimum and maximum PWM values, as well as the `neverStop` flag.

### Control Algorithms

A control algorithm
is a function that returns the next PWM value to apply based on the target value calculated by the curve. The simplest
control algorithm is the direct control algorithm, which simply forwards the target value to the fan.

#### Direct Control Algorithm

The simplest control algorithm is the direct control algorithm. It simply forwards the curve value to the fan
controller.

```yaml
fans:
- id: some_fan
...
controlAlgorithm: direct
```

This control algorithm can also be used to approach the curve value more slowly:

```yaml
fans:
- id: some_fan
...
controlAlgorithm:
direct:
maxPwmChangePerCycle: 10
```

### PID Control Algorithm

The PID control algorithm uses a PID loop to approach the target value. The default
configuration is pretty non-aggressive using the following values:

| P | I | D |
|--------|---------|----------|
| `0.03` | `0.002` | `0.0005` |
| P | I | D |
|-------|--------|---------|
| `0.3` | `0.02` | `0.005` |

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

```yaml
fans:
- id: some_fan
...
controlLoop:
p: 0.03
i: 0.002
d: 0.0005
controlAlgorithm:
pid:
p: 0.3
i: 0.02
d: 0.005
```

The loop is advanced at a constant rate, specified by the `controllerAdjustmentTickRate` config option, which
Expand Down
6 changes: 3 additions & 3 deletions cmd/curve/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var curveCmd = &cobra.Command{

err = configuration.Validate(configPath)
if err != nil {
ui.FatalWithoutStacktrace(err.Error())
ui.FatalWithoutStacktrace("configuration validation failed: %v", err)
}

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

graph := asciigraph.Plot(values, asciigraph.Height(15), asciigraph.Width(100), asciigraph.Caption(caption))
ui.Printfln(graph)
ui.Println(graph)
}

func printFunctionCurveInfo(curve curves.SpeedCurve, config *configuration.FunctionCurveConfig) {
Expand Down Expand Up @@ -195,7 +195,7 @@ func printInfoTable(headers []string, rows [][]string) {
panic(tableErr)
}
tableString := buf.String()
ui.Printfln(tableString)
ui.Println(tableString)
}

func init() {
Expand Down
4 changes: 2 additions & 2 deletions cmd/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ var detectCmd = &cobra.Command{
}
tableString := buf.String()
if idx < (len(tables) - 1) {
ui.Printf(tableString)
ui.Print(tableString)
} else {
ui.Printfln(tableString)
ui.Println(tableString)
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions cmd/fan/curve.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var curveCmd = &cobra.Command{
configuration.LoadConfig()
err := configuration.Validate(configPath)
if err != nil {
ui.FatalWithoutStacktrace(err.Error())
ui.FatalWithoutStacktrace("%v", err)
}

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

pwmData, fanCurveErr := persistence.LoadFanPwmData(fan)
if fanCurveErr == nil {
_ = fan.AttachFanCurveData(&pwmData)
_ = fan.AttachFanRpmCurveData(&pwmData)
}

if idx > 0 {
Expand All @@ -54,7 +54,7 @@ var curveCmd = &cobra.Command{
}

// print table
ui.Printfln(fan.GetId())
ui.Println(fan.GetId())
tab := table.Table{
Headers: []string{"", ""},
Rows: [][]string{
Expand All @@ -78,11 +78,11 @@ var curveCmd = &cobra.Command{
panic(tableErr)
}
tableString := buf.String()
ui.Printfln(tableString)
ui.Println(tableString)

// print graph
if fanCurveErr != nil {
ui.Printfln("No fan curve data yet...")
ui.Println("No fan curve data yet...")
continue
}

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

caption := "RPM / PWM"
graph := asciigraph.Plot(values, asciigraph.Height(15), asciigraph.Width(100), asciigraph.Caption(caption))
ui.Printfln(graph)
ui.Println(graph)
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/fan/fan.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func getFan(id string) (fans.Fan, error) {
configuration.LoadConfig()
err := configuration.Validate(configPath)
if err != nil {
ui.FatalWithoutStacktrace(err.Error())
ui.FatalWithoutStacktrace("%v", err)
}

controllers := hwmon.GetChips()
Expand Down
11 changes: 4 additions & 7 deletions cmd/fan/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package fan

import (
"github.com/markusressel/fan2go/internal/configuration"
"github.com/markusressel/fan2go/internal/control_loop"
"github.com/markusressel/fan2go/internal/controller"
"github.com/markusressel/fan2go/internal/persistence"
"github.com/markusressel/fan2go/internal/ui"
"github.com/markusressel/fan2go/internal/util"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand All @@ -31,12 +31,9 @@ var initCmd = &cobra.Command{
fanController := controller.NewFanController(
p,
fan,
*util.NewPidLoop(
0.03,
0.002,
0.0005,
),
configuration.CurrentConfig.ControllerAdjustmentTickRate)
control_loop.NewDirectControlLoop(nil),
configuration.CurrentConfig.ControllerAdjustmentTickRate,
)

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

Expand Down
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ on your computer based on temperature sensors.`,
configuration.LoadConfig()
err := configuration.Validate(configPath)
if err != nil {
ui.ErrorAndNotify("Config Validation Error", err.Error())
ui.ErrorAndNotify("Config Validation Error: %v", "%v", err)
return
}

Expand Down Expand Up @@ -75,6 +75,7 @@ func printHeader() {
if err != nil {
fmt.Println("fan2go")
}
ui.Info("Version: %s", global.Version)
}

// Execute adds all child commands to the root command and sets flags appropriately.
Expand Down
2 changes: 1 addition & 1 deletion cmd/sensor/sensor.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func getSensor(id string) (sensors.Sensor, error) {
configuration.LoadConfig()
err := configuration.Validate(configPath)
if err != nil {
ui.FatalWithoutStacktrace(err.Error())
ui.FatalWithoutStacktrace("%v", err)
}

controllers := hwmon.GetChips()
Expand Down
9 changes: 9 additions & 0 deletions fan2go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ fans:
# The curve ID (defined above) that should be used to determine the
# speed of this fan
curve: cpu_curve
# (Optional) The algorithm how the target speed, determined by the curve is approached.
# direct: the target value will be directly applied to the fan
# pid: uses a PID loop with default tuning variables
controlAlgorithm:
direct:
# together with maxPwmChangePerCycle, fan speeds will approach target value
# with the given max speed.
maxPwmChangePerCycle: 10
# (Optional) Override for the lowest PWM value at which the
# fan is able to maintain rotation if it was spinning previously.
minPwm: 30
Expand Down Expand Up @@ -76,6 +84,7 @@ fans:
hwmon:
platform: it8620
rpmChannel: 4
controlAlgorithm: direct
neverStop: true
curve: case_avg_curve

Expand Down
38 changes: 19 additions & 19 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module github.com/markusressel/fan2go

go 1.22

toolchain go1.22.5
go 1.23.1

require (
github.com/asecurityteam/rolling v2.0.4+incompatible
Expand All @@ -13,15 +11,18 @@ require (
github.com/md14454/gosensors v0.0.0-20180726083412-bded752ab001
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.5.0
github.com/oklog/run v1.1.0
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/prometheus/client_golang v1.20.4
github.com/pterm/pterm v0.12.79
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/tomlazar/table v0.1.2
go.etcd.io/bbolt v1.3.11
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
)

require (
Expand All @@ -30,45 +31,44 @@ require (
atomicgo.dev/schedule v0.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.17.10 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.59.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
Loading

0 comments on commit 661543e

Please sign in to comment.