Cleanup, webclient GPIO debounce support

This commit is contained in:
MaMe82 2018-11-13 12:37:56 +01:00
parent 49267489f5
commit e3c4a9bf97
4 changed files with 41 additions and 181 deletions

View File

@ -123,8 +123,6 @@ func (gm *GpioManager) DeployGpioTrigger(in *pb.TriggerGPIOIn) (err error) {
p.In(pull, edge)
debounceDelay := time.Duration(in.DebounceMillis) * time.Millisecond
//ToDo: remove next line, as this is a fixed test delay of 100 ms
debounceDelay = 100 * time.Millisecond
go func() {
fmt.Println("Starting edge detection for pin " + p.Name())
@ -153,102 +151,6 @@ func (gm *GpioManager) DeployGpioTrigger(in *pb.TriggerGPIOIn) (err error) {
return nil
}
/*
func (gm *GpioManager) DeployGpioTriggerOld(in *pb.TriggerGPIOIn) (err error) {
if !gm.IsUsable {
return EGpioNotAvailable
}
p := gpioreg.ByName(in.GpioName)
if p == nil {
return EGpioPinInvalid
}
pull := gpio.Float
switch in.PullUpDown {
case pb.GPIOInPullUpDown_DOWN:
pull = gpio.PullDown
case pb.GPIOInPullUpDown_UP:
pull = gpio.PullUp
}
edge := gpio.BothEdges
switch in.GpioInEdge {
case pb.GPIOInEdge_FALLING:
edge = gpio.FallingEdge
case pb.GPIOInEdge_RISING:
edge = gpio.RisingEdge
}
// If edge detection is already running, stop it
if gm.isEdgeDetecting(p) {
gm.stopEdgeDetectionForPin(p)
}
p.In(pull,edge)
gm.edgeDetectingMutex.Lock()
gm.edgeDetecting[p] = true
gm.edgeDetectingMutex.Unlock()
debounceDelay := time.Duration(in.DebounceMillis) * time.Millisecond
//ToDo: remove next line, as this is a fixed test delay of 100 ms
debounceDelay = 100 *time.Millisecond
go func() {
fmt.Println("Starting edge detection for pin " + p.Name())
lastHit := time.Now()
for gm.isEdgeDetecting(p) {
fmt.Println("Wait for edge " + p.Name() + " ...")
waitSuccess := p.WaitForEdge(-1)
fmt.Printf("... done wait for edge %s waitSucces: %v\n", p.Name(), waitSuccess)
if !waitSuccess {
break
}
now := time.Now()
bounce := true
sinceLastHit := now.Sub(lastHit)
if sinceLastHit > debounceDelay {
bounce = false
// could do `lastHit = now` if timer only should be reset for "no bounce"
}
lastHit = now
//Edge detected, check if still edge detecting before consuming
if gm.isEdgeDetecting(p) {
switch p.Read() {
case gpio.High:
fmt.Println("Gpio " + p.Name() + " changed to high")
if !bounce {
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.High)))
} else {
fmt.Println("... ignored, as in debounceDelay")
}
case gpio.Low:
fmt.Println("Gpio " + p.Name() + " changed to low")
if !bounce {
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.Low)))
} else {
fmt.Println("... ignored, as in debounceDelay")
}
}
} else {
//exit for loop
break
}
}
fmt.Println("STOPPED edge detection for pin " + p.Name())
gm.edgeDetectingMutex.Lock()
delete(gm.edgeDetecting, p)
gm.edgeDetectingMutex.Unlock()
}()
return nil
}
*/
func (gm *GpioManager) FireGpioAction(out *pb.ActionGPIOOut) (err error) {
fmt.Println("FireGPIOAction for", out.GpioName)
if !gm.IsUsable {
@ -273,7 +175,7 @@ func (gm *GpioManager) FireGpioAction(out *pb.ActionGPIOOut) (err error) {
fmt.Printf("Setting %s to out level %v...\n", p.Name(), level)
p.Out(level)
fmt.Println("..setting level done")
//fmt.Println("..setting level done")
return nil
}
@ -289,51 +191,3 @@ func (gm *GpioManager) ResetPins() {
}
}
/*
func (gm *GpioManager) isEdgeDetecting(p gpio.PinIO) bool {
gm.edgeDetectingMutex.Lock()
defer gm.edgeDetectingMutex.Unlock()
if _,exists := gm.edgeDetecting[p]; exists && gm.edgeDetecting[p] {
fmt.Println("Edge detection for " + p.Name() + " running")
return true
}
fmt.Println("Edge detection for " + p.Name() + " not running")
return false
}
*/
/*
func (gm *GpioManager) stopEdgeDetectionForPin(p gpio.PinIO) {
fmt.Println("Stopping edge detection for pin " + p.Name())
gm.edgeDetectingMutex.Lock()
if _,exists := gm.edgeDetecting[p]; exists {
gm.edgeDetecting[p] = false
}
gm.edgeDetectingMutex.Unlock()
//p.Halt()
p.In(gpio.Float, gpio.BothEdges)
isNotDeleted := func() bool {
gm.edgeDetectingMutex.Lock()
defer gm.edgeDetectingMutex.Unlock()
_,exists := gm.edgeDetecting[p]
return exists
}
//write high/low till gpio is deleted from map, to assure pending edge detection ended
for isNotDeleted() {
fmt.Println("... stopping " + p.Name() + " setting PullDown to trigger final edge")
p.In(gpio.PullDown, gpio.BothEdges)
if isNotDeleted() {
fmt.Println("... stopping " + p.Name() + " setting PullUp to trigger final edge")
p.In(gpio.PullUp, gpio.BothEdges)
}
}
p.In(gpio.Float, gpio.NoEdge)
}
*/

View File

@ -6,6 +6,7 @@ import (
"context"
"errors"
"fmt"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/physic"
"time"
@ -13,12 +14,12 @@ import (
var (
EEdgeDetectNotRunning = errors.New("edge detection not running")
EEdgeDetectAborted = errors.New("edge detection aborted")
EEdgeDetectAborted = errors.New("edge detection aborted")
)
type steadyTimeLevel struct {
SteadyTime time.Duration
Level gpio.Level
Level gpio.Level
}
type P4wnp1PinIO struct {
@ -30,17 +31,16 @@ type P4wnp1PinIO struct {
edgeDetectionMutex *sync.Mutex
*/
edge gpio.Edge
pull gpio.Pull
edgeDetectionAbort bool
edge gpio.Edge
pull gpio.Pull
edgeDetectionAbort bool
edgeDetectionChannel chan steadyTimeLevel //Every time a valid edge is detected, this channel returns the duration since the last valid edge (==steadyTime. could be used for debounce) and the gpio.Level during edge event
waitForEdgeStopped bool
lastEdgeTime time.Time
waitForEdgeStopped bool
lastEdgeTime time.Time
}
func (p *P4wnp1PinIO) startEdgeDetection(pull gpio.Pull, edge gpio.Edge, preserveUnconsumed bool) (err error) {
fmt.Println("starting edge detection for", p.Name() , "...")
fmt.Println("starting edge detection for", p.Name(), "...")
p.lastEdgeTime = time.Now()
if preserveUnconsumed {
@ -102,7 +102,7 @@ func (p *P4wnp1PinIO) startEdgeDetection(pull gpio.Pull, edge gpio.Edge, preserv
// Send the steadyTimeLeve along the channel
stl := steadyTimeLevel{
Level: level,
Level: level,
SteadyTime: timeSinceLastEdge,
}
@ -114,7 +114,7 @@ func (p *P4wnp1PinIO) startEdgeDetection(pull gpio.Pull, edge gpio.Edge, preserv
} else {
//pop old stl from channel if needed
for len(p.edgeDetectionChannel) > 0 {
<- p.edgeDetectionChannel
<-p.edgeDetectionChannel
}
p.edgeDetectionChannel <- stl //put latest stl to channel
}
@ -130,29 +130,25 @@ func (p *P4wnp1PinIO) startEdgeDetection(pull gpio.Pull, edge gpio.Edge, preserv
fmt.Println("... edge detection for", p.Name(), "stopped")
}()
fmt.Println("... edge detection for", p.Name(), "started")
fmt.Printf("p: %+v\n",p)
return
}
func (p *P4wnp1PinIO) isEdgeDetectionRunning() bool {
if p.edgeDetectionChannel == nil {
fmt.Println("edge detection not running")
fmt.Printf("p: %+v\n",p)
//fmt.Println("edge detection not running")
return false
}
fmt.Println("edge detection running")
//fmt.Println("edge detection running")
return true
}
func (p *P4wnp1PinIO) stopEdgeDetection() error {
fmt.Println("stopping edge detection for", p.Name(), "...")
if !p.isEdgeDetectionRunning() {
return EEdgeDetectNotRunning
}
fmt.Println("stopping edge detection for", p.Name(), "...")
p.edgeDetectionAbort = true
// hackish approach to end WaitForEdge, by toggling Pull resistors (see comments in startEdgeDetection for details)
@ -168,11 +164,9 @@ func (p *P4wnp1PinIO) stopEdgeDetection() error {
// wait till stop success is indicated
if p.edgeDetectionChannel != nil { // if channel still exists
<-p.edgeDetectionChannel //wait for close
<-p.edgeDetectionChannel //wait for close
}
return nil
}
@ -180,34 +174,34 @@ func (p P4wnp1PinIO) Edge() gpio.Edge {
return p.edge
}
func (p *P4wnp1PinIO) ExtWaitForEdge(ctx context.Context, debounceDuration time.Duration) (level gpio.Level, err error) {
useDebounce := debounceDuration > 0
for { // the select statement is wrapped into a for loop, to account for edges not fulfilling the debounceDuration criteria
select {
case steadyTimeLevel,channelOpen := <- p.edgeDetectionChannel:
case steadyTimeLevel, channelOpen := <-p.edgeDetectionChannel:
//channel closed, so we return error
if !channelOpen {
return level,EEdgeDetectNotRunning
return level, EEdgeDetectNotRunning
}
if !useDebounce {
// no debounce needed, ultimately return the edge change
return steadyTimeLevel.Level,nil
return steadyTimeLevel.Level, nil
} else {
// we have to assure that no other edge change event occurs in debounce duration (steady level) before returning
if steadyTimeLevel.SteadyTime >= debounceDuration {
//detected edge fulfills debounce criteria
return steadyTimeLevel.Level,nil
return steadyTimeLevel.Level, nil
}
// else ignore edge
fmt.Printf("Ignored detected edge as invalidated by debounce: %v\n", steadyTimeLevel)
}
case <-ctx.Done():
return level,EEdgeDetectAborted
return level, EEdgeDetectAborted
}
}
}
/*
func (p P4wnp1PinIO) String() string {
return p.piPin.String()
@ -230,15 +224,16 @@ func (p P4wnp1PinIO) Function() string {
}
func (p *P4wnp1PinIO) In(pull gpio.Pull, edge gpio.Edge) (err error) {
fmt.Println("Called In() on ", p.Name())
p.stopEdgeDetection()
// bring up edgeDetection go routine (again) if needed
err = p.piPin.In(pull, edge)
if err != nil { return }
if err != nil {
return
}
p.pull = pull
p.edge = edge
if edge != gpio.NoEdge {
p.startEdgeDetection(pull,edge,false) //don't preserve unconsumed events
p.startEdgeDetection(pull, edge, false) //don't preserve unconsumed events
}
return err
}
@ -268,12 +263,9 @@ func (p *P4wnp1PinIO) Out(l gpio.Level) error {
func (p *P4wnp1PinIO) PWM(duty gpio.Duty, f physic.Frequency) error {
//stop edge detection, if needed
p.stopEdgeDetection()
return p.piPin.PWM(duty,f)
return p.piPin.PWM(duty, f)
}
func NewP4wnp1PinIO(p gpio.PinIO) *P4wnp1PinIO {
return &P4wnp1PinIO{piPin: p}
}

View File

@ -288,6 +288,7 @@ func InitComponentsTriggerActions() {
strTrigger += t.GpioName
strTrigger += ": " + gpioInEdgeNames[t.Edge]
strTrigger += ", resistor: " + gpioInPullUpDownNames[t.PullUpDown]
strTrigger += ", debounce: " + strconv.Itoa(int(t.DebounceMillis)) + "ms"
strTrigger += ")"
}
@ -772,6 +773,15 @@ const templateTrigger = `
</q-item-tile>
</q-item-main>
</q-item>
<q-item tag="label" v-if="isTriggerGPIOIn">
<q-item-main>
<q-item-tile label>Debounce duration</q-item-tile>
<q-item-tile sublabel>Successive edge events in this duration are ignored</q-item-tile>
<q-item-tile>
<q-input v-model="ta.TriggerData.DebounceMillis" type="number" suffix="ms" decimals="0" inverted :disable="!ta.IsActive"></q-input>
</q-item-tile>
</q-item-main>
</q-item>
</q-list>
`

View File

@ -149,6 +149,7 @@ func (dst *jsTriggerAction) fromGo(src *pb.TriggerAction) {
dstTrigger.GpioName = srcTrigger.GpioIn.GpioName
dstTrigger.Edge = GPIOInEdge(srcTrigger.GpioIn.GpioInEdge)
dstTrigger.PullUpDown = GPIOInPullUpDown(srcTrigger.GpioIn.PullUpDown)
dstTrigger.DebounceMillis = srcTrigger.GpioIn.DebounceMillis
case *pb.TriggerAction_GroupReceive:
dst.ChangeTriggerType(TriggerGroupReceive)
dstTrigger := &jsTriggerGroupReceive{Object: dst.TriggerData}
@ -264,6 +265,7 @@ func (ta *jsTriggerAction) toGo() (res *pb.TriggerAction) {
GpioName: triggerData.GpioName,
PullUpDown: pb.GPIOInPullUpDown(triggerData.PullUpDown),
GpioInEdge: pb.GPIOInEdge(triggerData.Edge),
DebounceMillis: triggerData.DebounceMillis,
},
}
case TriggerGroupReceive:
@ -383,6 +385,7 @@ func (ta *jsTriggerAction) ChangeTriggerType(newTt triggerType) {
d.Edge = GPIOInEdgeRising
d.GpioName = ""
d.PullUpDown = GPIOInPullUp
d.DebounceMillis = 0
data = d.Object
case TriggerGroupReceive:
d := &jsTriggerGroupReceive{Object:O()}
@ -508,6 +511,7 @@ type jsTriggerGPIOIn struct {
GpioName string `js:"GpioName"`
PullUpDown GPIOInPullUpDown `js:"PullUpDown"` //PullUp resistor, pull down otherwise
Edge GPIOInEdge `js:"Edge"` // 0 == GPIO.RISING, 1 == GPIO.FALLING, every value > 1 == GPIO.BOTH
DebounceMillis int64 `js:"DebounceMillis"`
}
type GPIOInPullUpDown int
const GPIOInPullUp = GPIOInPullUpDown(0)