From e25905c20f36e04afc6eeb338f281874f8c9fe81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Rasmussen?= Date: Mon, 1 Aug 2022 19:49:52 +0200 Subject: [PATCH] Reset 1-wire bus when temperature readings fail 60 consecutive times --- cmd/fermentord/loop.go | 42 ++++++++++++++++++++++++++------------ internal/dwingest/nats.go | 8 ++++---- internal/dwingest/types.go | 5 +++++ internal/hw/gpio.go | 34 ++++++++++++++++++++++++------ pkg/temperature/ds18b20.go | 16 ++++++++++++--- 5 files changed, 79 insertions(+), 26 deletions(-) diff --git a/cmd/fermentord/loop.go b/cmd/fermentord/loop.go index 96c2d25..6847bad 100644 --- a/cmd/fermentord/loop.go +++ b/cmd/fermentord/loop.go @@ -2,7 +2,6 @@ package main import ( "context" - "encoding/json" "log" "sync" "time" @@ -68,6 +67,11 @@ func mainLoop(ctx context.Context, wg *sync.WaitGroup, js nats.JetStream, config go temperature.PollSensors(ctx, wg, 1*time.Second, config.Sensors.Weight) go tilt.PollSensors(ctx, wg, 1*time.Minute, 20*time.Second) + oneWirePowerUpChannel := make(chan bool) + oneWirePowerUpTimer := time.AfterFunc(1*time.Millisecond, func() { + oneWirePowerUpChannel <- true + }) + for { select { case reading := <-temperature.Reading: @@ -75,6 +79,25 @@ func mainLoop(ctx context.Context, wg *sync.WaitGroup, js nats.JetStream, config ingest.AddReading(reading) //display.SetTemperature(reading) + case <-temperature.RequestReset: + log.Print("Powering down one wire bus for 20 seconds") + gpio.StopOneWirePower() + oneWirePowerUpTimer.Reset(20 * time.Second) + + // Publish to NATS + e := dwingest.Event{ + Time: time.Now().UTC(), + Event: "ONEWIRE_RESET", + } + if err := ingest.Publish(config.NATS.Subject.Event, js, e); err != nil { + hub.CaptureException(err) + log.Print(err) + } + + case <-oneWirePowerUpChannel: + log.Print("Powering up one wire bus") + gpio.StartOneWirePower() + case ev := <-gpio.C: var evs string @@ -96,19 +119,12 @@ func mainLoop(ctx context.Context, wg *sync.WaitGroup, js nats.JetStream, config evs = "DOOR_OPENED" } - b, err := json.Marshal(map[string]interface{}{ - "time": time.Now().UTC(), - "event": evs, - }) - if err != nil { - hub.CaptureException(err) - log.Printf("Error marshaling JSON: %v", err) - break - } - // Publish to NATS - _, err = js.Publish(config.NATS.Subject.Event, b, nats.AckWait(1*time.Second)) - if err != nil { + e := dwingest.Event{ + Time: time.Now().UTC(), + Event: evs, + } + if err := ingest.Publish(config.NATS.Subject.Event, js, e); err != nil { hub.CaptureException(err) log.Print(err) } diff --git a/internal/dwingest/nats.go b/internal/dwingest/nats.go index 532714c..2805f8d 100644 --- a/internal/dwingest/nats.go +++ b/internal/dwingest/nats.go @@ -64,16 +64,16 @@ func (p *DWIngest) Run(ctx context.Context, wg *sync.WaitGroup, js nats.JetStrea for { select { case reading := <-p.chTemperatureReading: - p.publish(config.NATS.Subject.Temp, js, reading) + p.Publish(config.NATS.Subject.Temp, js, reading) case state := <-p.chState: - p.publish(config.NATS.Subject.State, js, State{ + p.Publish(config.NATS.Subject.State, js, State{ Time: time.Now().UTC(), State: controllers.ChamberStateMap[state], }) case t := <-tilt.C: - p.publish(config.NATS.Subject.Tilt, js, Tilt{ + p.Publish(config.NATS.Subject.Tilt, js, Tilt{ Time: time.Now().UTC(), Color: string(t.Color()), Gravity: t.Gravity(), @@ -86,7 +86,7 @@ func (p *DWIngest) Run(ctx context.Context, wg *sync.WaitGroup, js nats.JetStrea } } -func (p *DWIngest) publish(subject string, js nats.JetStream, reading any) error { +func (p *DWIngest) Publish(subject string, js nats.JetStream, reading any) error { b, err := json.Marshal(reading) if err != nil { p.hub.CaptureException(err) diff --git a/internal/dwingest/types.go b/internal/dwingest/types.go index 04e3f7a..65fdd77 100644 --- a/internal/dwingest/types.go +++ b/internal/dwingest/types.go @@ -13,3 +13,8 @@ type Tilt struct { Gravity float64 `json:"gravity"` Temperature float64 `json:"temperature"` } + +type Event struct { + Time time.Time `json:"time"` + Event string `json:"event"` +} diff --git a/internal/hw/gpio.go b/internal/hw/gpio.go index 362bfe6..0478956 100644 --- a/internal/hw/gpio.go +++ b/internal/hw/gpio.go @@ -13,6 +13,10 @@ const ( pinHeaterPower = 24 pinLightsPower = 25 + // GPIO21 is located at pin 13 on RPi 1B rev 1. Rev 2 and never boards + // has GPIO27 here. + pinOneWirePower = 21 + off = 0 on = 1 ) @@ -26,12 +30,13 @@ const ( ) type Gpio struct { - chip *gpiod.Chip - doorOpen *gpiod.Line - lightsPower *gpiod.Line - fanPower *gpiod.Line - coolerPower *gpiod.Line - heaterPower *gpiod.Line + chip *gpiod.Chip + doorOpen *gpiod.Line + oneWirePower *gpiod.Line + lightsPower *gpiod.Line + fanPower *gpiod.Line + coolerPower *gpiod.Line + heaterPower *gpiod.Line C chan HWEvent } @@ -71,6 +76,11 @@ func NewGpio() (*Gpio, error) { p.C <- DoorClosed } + p.oneWirePower, err = p.chip.RequestLine(pinOneWirePower, gpiod.AsOutput(0)) + if err != nil { + return nil, err + } + p.lightsPower, err = p.chip.RequestLine(pinLightsPower, gpiod.AsOutput(0)) if err != nil { return nil, err @@ -99,7 +109,9 @@ func (p *Gpio) Close() { p.heaterPower.SetValue(off) p.fanPower.SetValue(off) p.lightsPower.SetValue(off) + p.oneWirePower.SetValue(off) + p.oneWirePower.Close() p.heaterPower.Close() p.coolerPower.Close() p.fanPower.Close() @@ -142,6 +154,16 @@ func (p *Gpio) LightsOff() { p.lightsPower.SetValue(off) } +func (p *Gpio) StartOneWirePower() { + // Inverted logic due to PNP transistor switch + p.oneWirePower.SetValue(off) +} + +func (p *Gpio) StopOneWirePower() { + // Inverted logic due to PNP transistor switch + p.oneWirePower.SetValue(on) +} + func (p *Gpio) onDoorStateChanged(le gpiod.LineEvent) { switch le.Type { case gpiod.LineEventFallingEdge: diff --git a/pkg/temperature/ds18b20.go b/pkg/temperature/ds18b20.go index 2daddfb..9d02b51 100644 --- a/pkg/temperature/ds18b20.go +++ b/pkg/temperature/ds18b20.go @@ -42,6 +42,7 @@ var ( // Reading receives the temperature readings Reading chan TemperatureReading ConfigUpdate chan configuration.Configuration + RequestReset chan bool accErrTrigger int sensors []Sensor @@ -50,6 +51,7 @@ var ( func init() { Reading = make(chan TemperatureReading, 30) ConfigUpdate = make(chan configuration.Configuration, 1) + RequestReset = make(chan bool) sensors = make([]Sensor, 0) } @@ -75,7 +77,6 @@ func PollSensors(ctx context.Context, wg *sync.WaitGroup, readingInterval time.D defer ticker.Stop() for { - work_loop: select { case <-ticker.C: poll_bus: @@ -100,7 +101,7 @@ func PollSensors(ctx context.Context, wg *sync.WaitGroup, readingInterval time.D accErrTrigger++ hub.CaptureException(err) log.Print(err) - break work_loop + break } accErrTrigger = 0 @@ -119,7 +120,16 @@ func PollSensors(ctx context.Context, wg *sync.WaitGroup, readingInterval time.D } if accErrTrigger > 60 { - log.Fatal("Thermal bulk read failed 60 times in a row -- terminating") + select { + case RequestReset <- true: + log.Print("Thermal bulk read failed 60 times in a row -- bus reset") + + // Reset counter to allow 60 more reads + accErrTrigger = 0 + + default: + log.Fatal("Thermal bulk read failed 60 times in a row -- terminating") + } } case c := <-ConfigUpdate: