mirror of https://github.com/periph/devices
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.
234 lines
3.8 KiB
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())
|
|
}
|