From 2d4dfa73d63cdb23cf29f6ba9ad7f992d31bcf79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Rasmussen?= Date: Fri, 11 Mar 2022 20:46:18 +0100 Subject: [PATCH] Add sensor weight --- cmd/fermentord/db.go | 2 +- cmd/fermentord/main.go | 2 +- internal/controllers/chamber.go | 16 ++++++++-------- internal/controllers/config.go | 9 +++++---- pkg/temperature/ds18b20.go | 20 +++++++++++++++----- pkg/temperature/filter.go | 28 ++++++++++++++++++++++++++++ pkg/temperature/temperature.go | 10 +++------- 7 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 pkg/temperature/filter.go diff --git a/cmd/fermentord/db.go b/cmd/fermentord/db.go index 25752e7..11d55ce 100644 --- a/cmd/fermentord/db.go +++ b/cmd/fermentord/db.go @@ -55,7 +55,7 @@ func persistData(ctx context.Context, wg *sync.WaitGroup, chState <-chan control break } - err := datlog("%v TEMP %v %v", t.Time.Unix(), t.Sensor, t.MilliDegrees) + err := datlog("%v TEMP %v %v", t.Time.Unix(), t.Sensor, t.Degrees) if err != nil { hub.CaptureException(err) log.Printf("Error persisting temperature reading: %v", err) diff --git a/cmd/fermentord/main.go b/cmd/fermentord/main.go index 70a5d89..80592b3 100644 --- a/cmd/fermentord/main.go +++ b/cmd/fermentord/main.go @@ -56,7 +56,7 @@ func mainLoop(ctx context.Context, wg *sync.WaitGroup, js nats.JetStream, config wg.Add(3) go persistData(ctx, wg, chDbState, chDbTemp, config) go ctrl.Run(ctx, wg) - go temperature.PollSensors(ctx, wg, 1*time.Second) + go temperature.PollSensors(ctx, wg, 1*time.Second, config.Sensors.Weight) for { select { diff --git a/internal/controllers/chamber.go b/internal/controllers/chamber.go index 99f8e76..2a2f57d 100644 --- a/internal/controllers/chamber.go +++ b/internal/controllers/chamber.go @@ -89,18 +89,18 @@ func (p *ChamberController) Run(ctx context.Context, wg *sync.WaitGroup) { func (p *ChamberController) setTemperature(t temperature.TemperatureReading) { switch t.Sensor { - case p.config.Sensor.Ambient: - p.ambientTemperature = t.Degrees() + case p.config.Sensors.Ambient: + p.ambientTemperature = t.Degrees - case p.config.Sensor.Chamber: - p.chamberTemperature = t.Degrees() + case p.config.Sensors.Chamber: + p.chamberTemperature = t.Degrees - case p.config.Sensor.Wort: - p.wortTemperature = t.Degrees() + case p.config.Sensors.Wort: + p.wortTemperature = t.Degrees default: - log.Printf("Unknown sensor: sensor=%v temp=%v", t.Sensor, t.MilliDegrees) - p.hub.CaptureMessage(fmt.Sprintf("Unknown sensor: sensor=%v temp=%v", t.Sensor, t.MilliDegrees)) + log.Printf("Unknown sensor: sensor=%v temp=%v", t.Sensor, t.Degrees) + p.hub.CaptureMessage(fmt.Sprintf("Unknown sensor: sensor=%v temp=%v", t.Sensor, t.Degrees)) } } diff --git a/internal/controllers/config.go b/internal/controllers/config.go index 8676b60..ce2ed2e 100644 --- a/internal/controllers/config.go +++ b/internal/controllers/config.go @@ -16,10 +16,11 @@ type ControllerConfig struct { DataPath string `mapstructure:"data_path"` - Sensor struct { - Wort string `mapstructure:"wort"` - Chamber string `mapstructure:"chamber"` - Ambient string `mapstructure:"ambient"` + Sensors struct { + Wort string `mapstructure:"wort"` + Chamber string `mapstructure:"chamber"` + Ambient string `mapstructure:"ambient"` + Weight float64 `mapstructure:"weight"` } `mapstructure:"sensors"` FermentationTemperature float64 `mapstructure:"fermentation_temp"` diff --git a/pkg/temperature/ds18b20.go b/pkg/temperature/ds18b20.go index b0269ca..873f5a8 100644 --- a/pkg/temperature/ds18b20.go +++ b/pkg/temperature/ds18b20.go @@ -36,16 +36,18 @@ var ( sensors []string accErrTrigger int accErrSensor map[string]int + filters map[string]*EWMAFilter ) func Initialize() { sensors = make([]string, 0) + filters = make(map[string]*EWMAFilter) C = make(chan TemperatureReading, 30) ConfigUpdates = make(chan []string, 1) accErrSensor = make(map[string]int) } -func PollSensors(ctx context.Context, wg *sync.WaitGroup, readingInterval time.Duration) { +func PollSensors(ctx context.Context, wg *sync.WaitGroup, readingInterval time.Duration, filterWeight float64) { hub := sentry.CurrentHub().Clone() defer hub.Flush(10 * time.Second) defer wg.Done() @@ -95,6 +97,13 @@ func PollSensors(ctx context.Context, wg *sync.WaitGroup, readingInterval time.D case c := <-ConfigUpdates: sensors = c + for k := range filters { + delete(filters, k) + } + for _, x := range sensors { + filters[x] = NewEWMAFilter(filterWeight) + } + case <-ctx.Done(): close(C) return @@ -115,16 +124,17 @@ func readSensors(hub *sentry.Hub) { } accErrSensor[sensor] = 0 + tw := filters[sensor].Update(float64(t) / 1000) r := TemperatureReading{ - Time: time.Now(), - Sensor: sensor, - MilliDegrees: t, + Time: time.Now(), + Sensor: sensor, + Degrees: tw, } metrics.TemperatureSensorReadingDegreesCelcius. WithLabelValues(sensor). - Set(r.Degrees()) + Set(tw) C <- r diff --git a/pkg/temperature/filter.go b/pkg/temperature/filter.go new file mode 100644 index 0000000..4a48856 --- /dev/null +++ b/pkg/temperature/filter.go @@ -0,0 +1,28 @@ +package temperature + +// EWMAFilter is an exponentially weighted moving average filter +// See https://hackaday.com/2019/09/06/sensor-filters-for-coders/ +type EWMAFilter struct { + weight float64 + prevTemp float64 + isInitialized bool +} + +func NewEWMAFilter(weight float64) *EWMAFilter { + return &EWMAFilter{ + weight: weight, + } +} + +func (p *EWMAFilter) Update(input float64) float64 { + if !p.isInitialized { + p.prevTemp = input + p.isInitialized = true + return input + } + + ewmf := (1-p.weight)*p.prevTemp + p.weight*input + p.prevTemp = ewmf + + return ewmf +} diff --git a/pkg/temperature/temperature.go b/pkg/temperature/temperature.go index 0b9bdf1..13dc9d1 100644 --- a/pkg/temperature/temperature.go +++ b/pkg/temperature/temperature.go @@ -3,11 +3,7 @@ package temperature import "time" type TemperatureReading struct { - Sensor string `json:"sensor"` - Time time.Time `json:"time"` - MilliDegrees int64 `json:"milli_degrees"` -} - -func (t *TemperatureReading) Degrees() float64 { - return float64(t.MilliDegrees) / 1000.0 + Sensor string `json:"sensor"` + Time time.Time `json:"time"` + Degrees float64 `json:"degrees"` }