package hw import ( "time" "github.com/warthog618/gpiod" ) const ( pinDoorOpen = 17 pinFanPower = 22 pinCoolerPower = 23 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 ) type HWEvent int const ( NoOp HWEvent = iota DoorClosed DoorOpened ) type Gpio struct { chip *gpiod.Chip doorOpen *gpiod.Line oneWirePower *gpiod.Line lightsPower *gpiod.Line fanPower *gpiod.Line coolerPower *gpiod.Line heaterPower *gpiod.Line C chan HWEvent } func NewGpio() (*Gpio, error) { var err error p := &Gpio{ C: make(chan HWEvent, 1), } p.chip, err = gpiod.NewChip("gpiochip0", gpiod.WithConsumer("fermentord")) if err != nil { return nil, err } p.doorOpen, err = p.chip.RequestLine( pinDoorOpen, gpiod.AsInput, gpiod.LineBiasPullUp, gpiod.WithDebounce(200*time.Millisecond), gpiod.WithBothEdges, gpiod.WithEventHandler(p.onDoorStateChanged), ) if err != nil { return nil, err } // Add an initial door reading to the channel to ensure correct state when booting isDoorOpen, err := p.doorOpen.Value() if err != nil { return nil, err } switch isDoorOpen { case 0: // Line is pulled low when door is open (circuit is closed) p.C <- DoorOpened default: // Pull-up resistor sets line high when door is closed (open circuit) 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 } p.fanPower, err = p.chip.RequestLine(pinFanPower, gpiod.AsOutput(0)) if err != nil { return nil, err } p.coolerPower, err = p.chip.RequestLine(pinCoolerPower, gpiod.AsOutput(0)) if err != nil { return nil, err } p.heaterPower, err = p.chip.RequestLine(pinHeaterPower, gpiod.AsOutput(0)) if err != nil { return nil, err } return p, nil } func (p *Gpio) Close() { p.coolerPower.SetValue(off) 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() p.lightsPower.Close() p.doorOpen.Close() p.chip.Close() close(p.C) } func (p *Gpio) StartCooler() { p.coolerPower.SetValue(on) } func (p *Gpio) StopCooler() { p.coolerPower.SetValue(off) } func (p *Gpio) StartHeater() { p.heaterPower.SetValue(on) } func (p *Gpio) StopHeater() { p.heaterPower.SetValue(off) } func (p *Gpio) StartFan() { p.fanPower.SetValue(on) } func (p *Gpio) StopFan() { p.fanPower.SetValue(off) } func (p *Gpio) LightsOn() { p.lightsPower.SetValue(on) } 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: p.C <- DoorOpened case gpiod.LineEventRisingEdge: p.C <- DoorClosed } }