You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
devices/firmata/pin.go

234 lines
3.8 KiB
Go

package firmata
import (
"context"
"errors"
"sync"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/pin"
)
var (
ErrUnsupportedGPIOPull = errors.New("firmata: PullDown is not supported")
ErrNoMatchingGPIOPull = errors.New("firmata: pin was previously in a non-input mode")
)
type Pin struct {
c ClientI
pin uint8
edge gpio.Edge
ch chan gpio.Level
release func()
done chan struct{}
valueLast gpio.Level
valueNew gpio.Level
edgeChange chan gpio.Edge
mu sync.Mutex
}
func newPin(c ClientI, num uint8) *Pin {
p := &Pin{
c: c,
pin: num,
ch: make(chan gpio.Level),
}
go p.run()
return p
}
func (p *Pin) run() {
p.done = make(chan struct{})
for {
select {
case <-p.done:
close(p.ch)
return
case v := <-p.ch:
p.valueLast = p.valueNew
p.valueNew = v
func() {
p.mu.Lock()
defer p.mu.Unlock()
if p.edgeChange == nil {
return
}
if p.valueLast == true || p.valueNew == false {
p.edgeChange <- gpio.FallingEdge
}
if p.valueLast == false || p.valueNew == true {
p.edgeChange <- gpio.RisingEdge
}
}()
}
}
}
func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error {
var mode = PinFuncDigitalInput
switch pull {
case gpio.PullDown:
return ErrUnsupportedGPIOPull
case gpio.PullNoChange:
ch, err := p.c.PinStateQuery(p.pin)
if err != nil {
return err
}
s := <-ch
mode = s.Mode
switch mode {
case PinFuncInputPullUp:
case PinFuncDigitalInput:
default:
return ErrNoMatchingGPIOPull
}
case gpio.PullUp:
mode = PinFuncInputPullUp
case gpio.Float:
mode = PinFuncDigitalInput
}
if err := p.c.SetPinMode(p.pin, mode); err != nil {
return err
}
var err error
if p.release, err = p.c.SetDigitalIOMessageListener(p.pin, p.ch); err != nil {
return err
}
if err = p.c.SetDigitalPinReporting(p.pin, true); err != nil {
return err
}
p.edge = edge
return nil
}
func (p *Pin) Read() gpio.Level {
return p.valueNew
}
func (p *Pin) WaitForEdge(timeout time.Duration) bool {
defer func() {
p.mu.Lock()
defer p.mu.Unlock()
close(p.edgeChange)
p.edgeChange = nil
}()
func() {
p.mu.Lock()
defer p.mu.Unlock()
p.edgeChange = make(chan gpio.Edge)
}()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
for {
select {
case change := <-p.edgeChange:
if p.edge == gpio.BothEdges || change == p.edge {
return true
}
case <-ctx.Done():
return false
}
}
}
func (p *Pin) Pull() gpio.Pull {
ch, err := p.c.PinStateQuery(p.pin)
if err != nil {
return gpio.PullNoChange
}
s := <-ch
switch s.Mode {
case PinFuncInputPullUp:
return gpio.PullUp
case PinFuncDigitalInput:
return gpio.Float
}
return gpio.PullNoChange
}
func (p *Pin) DefaultPull() gpio.Pull {
return gpio.PullNoChange
}
func (p *Pin) Out(l gpio.Level) error {
// TODO:
panic("implement me")
}
func (p *Pin) PWM(duty gpio.Duty, f physic.Frequency) error {
// TODO:
panic("implement me")
}
func (p *Pin) Func() pin.Func {
ch, err := p.c.PinStateQuery(p.pin)
if err != nil {
return pin.FuncNone
}
s := <-ch
return s.Mode
}
func (p *Pin) SetFunc(f pin.Func) error {
return p.c.SetPinMode(p.pin, f)
}
func (p *Pin) SupportedFuncs() []pin.Func {
return p.c.GetPinFunctions(p.pin)
}
func (p *Pin) Halt() error {
close(p.done)
p.release()
if err := p.c.SetDigitalPinReporting(p.pin, false); err != nil {
return err
}
if err := p.c.SetAnalogPinReporting(p.pin, false); err != nil {
return err
}
if err := p.c.SetDigitalPinValue(p.pin, gpio.Low); err != nil {
return err
}
return nil
}
func (p *Pin) Name() string {
return p.c.GetPinName(p.pin)
}
func (p *Pin) String() string {
return p.Name()
}
func (p *Pin) Number() int {
return int(p.pin)
}
func (p *Pin) Function() string {
return string(p.Func())
}