Add sensor weight

This commit is contained in:
Søren Rasmussen 2022-03-11 20:46:18 +01:00
parent e5d5b6ca2f
commit 2d4dfa73d6
7 changed files with 61 additions and 26 deletions

View file

@ -55,7 +55,7 @@ func persistData(ctx context.Context, wg *sync.WaitGroup, chState <-chan control
break 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 { if err != nil {
hub.CaptureException(err) hub.CaptureException(err)
log.Printf("Error persisting temperature reading: %v", err) log.Printf("Error persisting temperature reading: %v", err)

View file

@ -56,7 +56,7 @@ func mainLoop(ctx context.Context, wg *sync.WaitGroup, js nats.JetStream, config
wg.Add(3) wg.Add(3)
go persistData(ctx, wg, chDbState, chDbTemp, config) go persistData(ctx, wg, chDbState, chDbTemp, config)
go ctrl.Run(ctx, wg) 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 { for {
select { select {

View file

@ -89,18 +89,18 @@ func (p *ChamberController) Run(ctx context.Context, wg *sync.WaitGroup) {
func (p *ChamberController) setTemperature(t temperature.TemperatureReading) { func (p *ChamberController) setTemperature(t temperature.TemperatureReading) {
switch t.Sensor { switch t.Sensor {
case p.config.Sensor.Ambient: case p.config.Sensors.Ambient:
p.ambientTemperature = t.Degrees() p.ambientTemperature = t.Degrees
case p.config.Sensor.Chamber: case p.config.Sensors.Chamber:
p.chamberTemperature = t.Degrees() p.chamberTemperature = t.Degrees
case p.config.Sensor.Wort: case p.config.Sensors.Wort:
p.wortTemperature = t.Degrees() p.wortTemperature = t.Degrees
default: default:
log.Printf("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.MilliDegrees)) p.hub.CaptureMessage(fmt.Sprintf("Unknown sensor: sensor=%v temp=%v", t.Sensor, t.Degrees))
} }
} }

View file

@ -16,10 +16,11 @@ type ControllerConfig struct {
DataPath string `mapstructure:"data_path"` DataPath string `mapstructure:"data_path"`
Sensor struct { Sensors struct {
Wort string `mapstructure:"wort"` Wort string `mapstructure:"wort"`
Chamber string `mapstructure:"chamber"` Chamber string `mapstructure:"chamber"`
Ambient string `mapstructure:"ambient"` Ambient string `mapstructure:"ambient"`
Weight float64 `mapstructure:"weight"`
} `mapstructure:"sensors"` } `mapstructure:"sensors"`
FermentationTemperature float64 `mapstructure:"fermentation_temp"` FermentationTemperature float64 `mapstructure:"fermentation_temp"`

View file

@ -36,16 +36,18 @@ var (
sensors []string sensors []string
accErrTrigger int accErrTrigger int
accErrSensor map[string]int accErrSensor map[string]int
filters map[string]*EWMAFilter
) )
func Initialize() { func Initialize() {
sensors = make([]string, 0) sensors = make([]string, 0)
filters = make(map[string]*EWMAFilter)
C = make(chan TemperatureReading, 30) C = make(chan TemperatureReading, 30)
ConfigUpdates = make(chan []string, 1) ConfigUpdates = make(chan []string, 1)
accErrSensor = make(map[string]int) 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() hub := sentry.CurrentHub().Clone()
defer hub.Flush(10 * time.Second) defer hub.Flush(10 * time.Second)
defer wg.Done() defer wg.Done()
@ -95,6 +97,13 @@ func PollSensors(ctx context.Context, wg *sync.WaitGroup, readingInterval time.D
case c := <-ConfigUpdates: case c := <-ConfigUpdates:
sensors = c sensors = c
for k := range filters {
delete(filters, k)
}
for _, x := range sensors {
filters[x] = NewEWMAFilter(filterWeight)
}
case <-ctx.Done(): case <-ctx.Done():
close(C) close(C)
return return
@ -115,16 +124,17 @@ func readSensors(hub *sentry.Hub) {
} }
accErrSensor[sensor] = 0 accErrSensor[sensor] = 0
tw := filters[sensor].Update(float64(t) / 1000)
r := TemperatureReading{ r := TemperatureReading{
Time: time.Now(), Time: time.Now(),
Sensor: sensor, Sensor: sensor,
MilliDegrees: t, Degrees: tw,
} }
metrics.TemperatureSensorReadingDegreesCelcius. metrics.TemperatureSensorReadingDegreesCelcius.
WithLabelValues(sensor). WithLabelValues(sensor).
Set(r.Degrees()) Set(tw)
C <- r C <- r

28
pkg/temperature/filter.go Normal file
View file

@ -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
}

View file

@ -3,11 +3,7 @@ package temperature
import "time" import "time"
type TemperatureReading struct { type TemperatureReading struct {
Sensor string `json:"sensor"` Sensor string `json:"sensor"`
Time time.Time `json:"time"` Time time.Time `json:"time"`
MilliDegrees int64 `json:"milli_degrees"` Degrees float64 `json:"degrees"`
}
func (t *TemperatureReading) Degrees() float64 {
return float64(t.MilliDegrees) / 1000.0
} }