mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
//go:build !dummy
|
|
|
|
package roverd
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"sync"
|
|
|
|
gpiocdev "github.com/warthog618/go-gpiocdev"
|
|
)
|
|
|
|
type GPIOToggle struct {
|
|
cfg GPIOToggleConfig
|
|
name string
|
|
logger *log.Logger
|
|
line *gpiocdev.Line
|
|
mu sync.Mutex
|
|
on bool
|
|
closed bool
|
|
}
|
|
|
|
func NewGPIOToggle(name string, cfg GPIOToggleConfig, logger *log.Logger) (*GPIOToggle, error) {
|
|
if !cfg.Enabled {
|
|
return nil, fmt.Errorf("%s disabled", name)
|
|
}
|
|
chip := cfg.GPIOChip
|
|
if chip == "" {
|
|
chip = "gpiochip0"
|
|
}
|
|
line, err := gpiocdev.RequestLine(
|
|
chip,
|
|
cfg.GPIOPin,
|
|
gpiocdev.AsOutput(cfg.LogicalToGPIO(cfg.InitialOn)),
|
|
gpiocdev.WithConsumer(fmt.Sprintf("roverd-%s", name)),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("gpio request: %w", err)
|
|
}
|
|
toggle := &GPIOToggle{
|
|
cfg: cfg,
|
|
name: name,
|
|
logger: logger,
|
|
line: line,
|
|
on: cfg.InitialOn,
|
|
}
|
|
logger.Printf("%s on GPIO %d (initial=%v activeLow=%v)", name, cfg.GPIOPin, cfg.InitialOn, cfg.ActiveLow)
|
|
return toggle, nil
|
|
}
|
|
|
|
func (g *GPIOToggle) Close() {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
if g.closed {
|
|
return
|
|
}
|
|
// Preserve the last logical state while closing the line. The daemon is not
|
|
// trying to force a safety state here; it is only releasing the GPIO handle.
|
|
_ = g.line.SetValue(g.cfg.LogicalToGPIO(g.on))
|
|
g.line.Close()
|
|
g.closed = true
|
|
}
|
|
|
|
func (g *GPIOToggle) HandleAction(action string) error {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
if g.closed {
|
|
return fmt.Errorf("%s controller closed", g.name)
|
|
}
|
|
act := strings.ToLower(strings.TrimSpace(action))
|
|
switch act {
|
|
case "", "toggle":
|
|
return g.setLocked(!g.on)
|
|
case "on":
|
|
return g.setLocked(true)
|
|
case "off":
|
|
return g.setLocked(false)
|
|
default:
|
|
return fmt.Errorf("unknown action %q", action)
|
|
}
|
|
}
|
|
|
|
func (g *GPIOToggle) On() bool {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
return g.on
|
|
}
|
|
|
|
func (g *GPIOToggle) setLocked(on bool) error {
|
|
// This is the only place a logical device state becomes an electrical GPIO
|
|
// value. Hardware that turns on when pulled low sets activeLow in roverd
|
|
// config; every caller above this layer still uses plain on/off semantics.
|
|
if err := g.line.SetValue(g.cfg.LogicalToGPIO(on)); err != nil {
|
|
return err
|
|
}
|
|
g.on = on
|
|
return nil
|
|
}
|