A quick and dirty implementation

This commit is contained in:
Tony Blyler 2020-12-03 23:08:46 -05:00
parent 3682810e27
commit f6e9c70eb3
352 changed files with 242881 additions and 0 deletions

68
light/rpi/basic_light.go Normal file
View file

@ -0,0 +1,68 @@
package rpi
import (
"fmt"
"sync"
"github.com/warthog618/gpiod"
)
// BasicLight is a BasicLight implementation for the Raspberry Pi
type BasicLight struct {
pin int
line *gpiod.Line
manager *Manager
isOn bool
isOnLock sync.Mutex
}
// On turns on the light
func (bl *BasicLight) On() error {
bl.isOnLock.Lock()
defer bl.isOnLock.Unlock()
if bl.isOn {
return nil
}
err := bl.line.SetValue(0)
if err != nil {
return fmt.Errorf("failed to set pin %d to low (on): %w", bl.pin, err)
}
bl.isOn = true
return nil
}
// Off turns off the light
func (bl *BasicLight) Off() error {
bl.isOnLock.Lock()
defer bl.isOnLock.Unlock()
if !bl.isOn {
return nil
}
err := bl.line.SetValue(1)
if err != nil {
return fmt.Errorf("failed to set pin %d to high (on): %w", bl.pin, err)
}
bl.isOn = false
return nil
}
// State of whether the light is on or off
func (bl *BasicLight) State() (isOn bool, err error) {
bl.isOnLock.Lock()
defer bl.isOnLock.Unlock()
return bl.isOn, nil
}
// Close underlying open connection
func (bl *BasicLight) Close() error {
return bl.manager.closeLine(bl.pin)
}

121
light/rpi/manager.go Normal file
View file

@ -0,0 +1,121 @@
package rpi
import (
"errors"
"fmt"
"sync"
"github.com/warthog618/gpiod"
)
const (
// DefaultChipName to try to use if one is not provided
DefaultChipName = "gpiochip0"
)
var (
// ErrLineAlreadyRequested denotes that the requested line is not available because it has already been requested and not Closed
ErrLineAlreadyRequested = errors.New("line has already been requested")
// ErrLineNotRequested denotes that the given line cannot be acted on because it was not previously requested
ErrLineNotRequested = errors.New("line was not previously requested")
)
// ManagerConfig configuration for a Manager
type ManagerConfig struct {
ChipName string
}
// GetChipName from the config
func (mc *ManagerConfig) GetChipName() string {
if mc.ChipName == "" {
return DefaultChipName
}
return mc.ChipName
}
// Manager for overarching raspberry pi GPIO management
type Manager struct {
config *ManagerConfig
chip *gpiod.Chip
lines map[int]*gpiod.Line
linesLock sync.Mutex
}
// NewManager creates a new Manager instance for the given config
func NewManager(config ManagerConfig) (*Manager, error) {
chip, err := gpiod.NewChip(config.GetChipName())
if err != nil {
return nil, fmt.Errorf("failed to open GPIO chip %s: %w", config.GetChipName(), err)
}
return &Manager{
config: &config,
chip: chip,
lines: make(map[int]*gpiod.Line),
}, nil
}
// GetBasicLight for the given pin if available
func (m *Manager) GetBasicLight(pin int) (*BasicLight, error) {
line, err := m.requestLine(pin)
if err != nil {
return nil, err
}
return &BasicLight{
pin: pin,
line: line,
manager: m,
}, nil
}
func (m *Manager) requestLine(pin int) (*gpiod.Line, error) {
m.linesLock.Lock()
defer m.linesLock.Unlock()
_, exists := m.lines[pin]
if exists {
return nil, fmt.Errorf("%w pin %d", ErrLineAlreadyRequested, pin)
}
line, err := m.chip.RequestLine(pin, gpiod.AsOutput(0))
if err != nil {
return nil, fmt.Errorf("failed to request line for pin %d: %w", pin, err)
}
m.lines[pin] = line
return line, nil
}
func (m *Manager) closeLine(pin int) error {
m.linesLock.Lock()
defer m.linesLock.Unlock()
return m.closeLineHelper(pin)
}
func (m *Manager) closeLineHelper(pin int) error {
line := m.lines[pin]
if line == nil {
return fmt.Errorf("%w pin %d", ErrLineNotRequested, pin)
}
delete(m.lines, pin)
return line.Close()
}
// Close this Manager and all of its associated lines
func (m *Manager) Close() error {
m.linesLock.Lock()
defer m.linesLock.Unlock()
for pin := range m.lines {
m.closeLineHelper(pin)
}
return m.chip.Close()
}