Loop in gobot ButtonPush event doesn't stop

Hello!

I’m playing with some Raspberry Pi stuff and using Go since I’ve been wanting to learn it. I come from a Java and Javascript(Node.js) background. There are some familiar concepts but I’m stumped on the following piece of code.

package main

import (
	"fmt"
	"time"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/drivers/gpio"
	"gobot.io/x/gobot/platforms/raspi"
)

var (
	r         = raspi.NewAdaptor()
	button    = gpio.NewButtonDriver(r, "33")
	redLed    = gpio.NewLedDriver(r, "18")
	blueLed   = gpio.NewLedDriver(r, "16")
	greenLed  = gpio.NewLedDriver(r, "22")
	yellowLed = gpio.NewLedDriver(r, "32")
	leds      = [4]*gpio.LedDriver{redLed, blueLed, greenLed, yellowLed}
)

func toggleLed(led *gpio.LedDriver) {
	led.On()
	time.Sleep(250 * time.Millisecond)
	led.Off()
}

func main() {
	button.DefaultState = 1

	isActive := false
	work := func() {
		button.On(gpio.ButtonPush, func(data interface{}) {
			fmt.Println("Button pressed")
			isActive = !isActive
			fmt.Println("is active", isActive)

			for isActive {
				fmt.Println("is active")
				for _, led := range leds {
					toggleLed(led)
				}
			}

			for !isActive {
				fmt.Println("is inactive")
				for i := len(leds) - 1; i >= 0; i-- {
					led := leds[i]

					toggleLed(led)
				}
			}
		})

		button.On(gpio.ButtonRelease, func(data interface{}) {
			fmt.Println("Button released")
		})

	}

	robot := gobot.NewRobot("buttonBot",
		[]gobot.Connection{r},
		[]gobot.Device{button, redLed},
		work)

	robot.Start()
}

The for loop inside the ButtonPush event function gets triggered and never stops. The ButtonRelease event gets triggered but the ButtonPush event does not trigger anymore after the first press. I’m fairly confident this has something to do with concurrency but I’m not sure how to approach this. Basically I want to iterate one way when I press the button and the other once I press it again.

I am not a gobot user, but it seems that the button.On(gpio.ButtonPush…) code is hung in the for isActive loop and therefore cannot receive any further events.

button.On goes into a loop that reads events from a channel and calls the passed-in func (https://github.com/hybridgroup/gobot/blob/59aaba08eba8b081a9541f77aed19aba919437ac/eventer.go#L126). If the func never returns, the select is blocked. A separate goroutine for the workload might help.

Thanks for the reply!

And after tossing and turning last night in my sleep(I couldn’t stop thinking about it) I realized that this wouldn’t even work in Node.js because the loop never allows the “callback”(that is what it looks like here, I know it isn’t) never gets out the loop.

I tried a go routine but I didn’t return. I think thats the problem. I’m going to try that out. Thanks for your input!

I sugest you a better aproach. Asynchronous events always must be intercepted and serialised in a queue not handled at the moment of event happen. Use a library that can get and debounce the input then use a queue to store the events. Next in the main function you can use a loop to extract from queue and handle each event.

1 Like

After I wrapped the loop in a go routine and returned, it worked!

However, I feel this is probably not the best way to approach this. I imagine that every time the “ButtonPush” event triggers, I create an entirely separate go routine with a new infinite loop?

What kind of go construct can I use to use a singular go-routine that encloses a while loop but update the while loop’s condition?

A goroutine with an input channel and a select statement within a loop could be a suitable option here. Have button.On send the isActive value to the goroutine. The select statement then can set a goroutine-local isActive flag accordingly.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.