diff --git a/service/SubSysGpio.go b/service/SubSysGpio.go index 8b4c9ec..1f86170 100644 --- a/service/SubSysGpio.go +++ b/service/SubSysGpio.go @@ -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) -} -*/ diff --git a/service/pgpio/p4wnp1gpio.go b/service/pgpio/p4wnp1gpio.go index 1dfdc67..51bdd63 100644 --- a/service/pgpio/p4wnp1gpio.go +++ b/service/pgpio/p4wnp1gpio.go @@ -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} } - - - diff --git a/web_client/hvueComponentsTriggerActions.go b/web_client/hvueComponentsTriggerActions.go index f5d87f7..e210547 100644 --- a/web_client/hvueComponentsTriggerActions.go +++ b/web_client/hvueComponentsTriggerActions.go @@ -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 = ` + + + Debounce duration + Successive edge events in this duration are ignored + + + + + ` diff --git a/web_client/jsDataTriggerAction.go b/web_client/jsDataTriggerAction.go index 5ffb43c..63f8847 100644 --- a/web_client/jsDataTriggerAction.go +++ b/web_client/jsDataTriggerAction.go @@ -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)