Add sensor weight
This commit is contained in:
parent
e5d5b6ca2f
commit
2d4dfa73d6
7 changed files with 61 additions and 26 deletions
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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
28
pkg/temperature/filter.go
Normal 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
|
||||||
|
}
|
|
@ -5,9 +5,5 @@ 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
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue