1

good morning! I've been working on some python (pretty new to the language) to control some buttons and LEDs utilising gpiozero and I've run into a few issues which I've been trying to troubleshoot and look at other posts around similar problems. I'm no stranger to programming, do most of mine today in JS, but the python syntax and loop structures is giving me some grief and I'm not sure the correct term to look-up to help troubleshoot or find similar questions posted on here... which I looked at a dozen or so to make sure I wasn't wasting anyones time.

The main problem I'm encountering is:

  1. button press is getting triggered twice (and is erratic)

Erratic meaning when I first press the button, my LEDs turn on/off and then a second press _may_ turn the LEDs off, or it may repeat the on/off sequence again. Other presses, again, may turn the LEDs on (as expected) and the second press _may_ turn the LEDs off, or do the off/on sequence.

I believe the error is in my while loop, but I can't seem to figure out what I need to get the right sequence to work and would love some guidance

Here's the code I've written and a link to a video I made which shows the behaviour I'm seeing. And I do apologise for not supplying a circuit board drawing, I'm also new to this and still trying to figure how I put my current Pi and breadboard into a circuit diagram. https://youtu.be/zipFbDusaXk

from gpiozero import LEDBoard, LED, Button, PWMLED
from time import sleep
from signal import pause

## Define the buttons
static_button = Button(2)

## Model Specific LED Colour Groups
body_leds = LEDBoard(6, 5, 13, 19, 16, 26, 20, 21, pwm=True)
hull_led = LEDBoard(18, pwm=True)
left_red_thruster = LEDBoard(27, pwm=True)
left_green_thruster = LEDBoard(22, pwm=True)
right_red_thruster = LEDBoard(4, pwm=True)
right_green_thruster = LEDBoard(17, pwm=True)
boosters = LEDBoard(24, 25, pwm=True)

## Functions for button actions
def static_display():
    print("Turning Serenity on in static mode")
    body_leds.toggle()
    hull_led.toggle()
    left_red_thruster.toggle()
    left_green_thruster.toggle()
    right_red_thruster.toggle()
    right_green_thruster.toggle()
    boosters.toggle()

def leds_off():
    print("Turning LEDs off")
    body_leds.off()
    hull_led.off()
    left_red_thruster.off()
    left_green_thruster.off()
    right_red_thruster.off()
    right_green_thruster.off()
    boosters.off()

while True:
    if static_button.is_pressed:
        if body_leds.is_active:
            leds_off()
            static_button.wait_for_press(timeout=None)
        else:
            static_display()
            static_button.wait_for_press(timeout=None)

pause()
2
  • 1
    If it's a mechanical button it may just be contact bounce (google it).
    – joan
    Dec 31, 2019 at 23:27
  • wow @joan - Thank you!! That was the term I was looking for and saw what was happening. The button is indeed triggering multiple times and added a few sleeps and the wait_for_press(timeout=None) in the correct spot and my lights work as expected.
    – TimLavelle
    Dec 31, 2019 at 23:54

1 Answer 1

1

After the helpful keyword from joan, it was indeed a contact bounce and after a few attempts, here's what my loop looks like now.

while True:
    print("Started loop sequence")
    if static_button.is_pressed:
        print("Static Button Pressed")
        print(static_running)
        if static_running==False:
            static_display()
            static_running = True
            print("Static LED sequence is active")
            print(static_running)
            sleep(.1)
        else:
            leds_off()
            static_running = False
            print("Static LED sequence is not active")
            print(static_running)
            sleep(.1)
        sleep(.1)
        static_button.wait_for_press(timeout=None)

This is what works, but I'm sure there's a cleaner way and I'll continue to iterate through this to see if I can clean up. If anyone has any suggestions on how this can be cleaner, or a more efficient way, I am all ears!

Thanks again joan!

EDIT a helpful comment pointed out that the above was unnecessarily complex, which I thought it might be! But I do have multiple buttons and light sequences I'm trying to setup, so for transparency sake.. Here's my full code:

from gpiozero import LEDBoard, LED, Button, PWMLED
from time import sleep
from signal import pause

## Define the buttons
static_button = Button(2)
cruising_button = Button(8)
orbital_button = Button(15)

## Model Specific LED Colour Groups
body_leds = LEDBoard(6, 5, 13, 19, 16, 26, 20, 21, pwm=True)
hull_led = LEDBoard(18, pwm=True)
left_red_thruster = LEDBoard(27, pwm=True)
left_green_thruster = LEDBoard(22, pwm=True)
right_red_thruster = LEDBoard(4, pwm=True)
right_green_thruster = LEDBoard(17, pwm=True)
boosters = LEDBoard(24, 25, pwm=True)

static_running = False
cruising_running = False
orbital_running = False


## Functions for button actions
def static_display():
    body_leds.toggle()
    hull_led.toggle()
    left_red_thruster.toggle()
    left_green_thruster.toggle()
    right_red_thruster.toggle()
    right_green_thruster.toggle()
    boosters.toggle()

def cruising_display():
    body_leds.toggle()
    boosters.toggle()
    hull_led.pulse(1.5,1.5)
    left_red_thruster.pulse(.5,.5)
    sleep(.25)
    left_green_thruster.pulse(.5,.5)
    sleep(.25)
    right_red_thruster.pulse(.5,.5)
    sleep(.25)
    right_green_thruster.pulse(.5,.5)

def orbital_display():
    body_leds.toggle()
    hull_led.pulse(.25,.25)
    left_red_thruster.pulse(.25,.25)
    left_green_thruster.pulse(.25,.25)
    right_red_thruster.pulse(.25,.25)
    right_green_thruster.pulse(.25,.25)
    boosters.blink(0.02,0.02)

def leds_off():
    body_leds.off()
    hull_led.off()
    left_red_thruster.off()
    left_green_thruster.off()
    right_red_thruster.off()
    right_green_thruster.off()
    boosters.off()

## Button Activations
cruising_button.when_pressed = cruising_display
orbital_button.when_pressed = orbital_display


while True:
    if static_button.is_pressed:
        print("Static Button Pressed")
        if static_running==False:
            static_running = True
            static_display()
            print("Static LED sequence is active")
            sleep(.1)
        else:
            static_running = False
            leds_off()
            print("Static LED sequence is not active")
            sleep(.1)
        sleep(.1)
        static_button.wait_for_press(timeout=None)
    elif cruising_button.is_pressed:
        if cruising_running==False:
            cruising_running==True
            cruising_display()
            print("Cruising LED sequence is active")
            sleep(.1)
        else:
            cruising_running==False
            leds_off()
            print("Cruising LED sequence is not active")
            sleep(.1)
        sleep(.1)
        cruising_button.wait_for_press(timeout=None)
    elif orbital_button.is_pressed:
        if orbital_running==False:
            orbital_running = True
            orbital_display()
            print("Orbital LED sequence is active")
            sleep(.1)
        else:
            orbital_running = False
            leds_off()
            print("Orbital LED sequence is not active")
            sleep(.1)
        sleep(.1)
        orbital_button.wait_for_press(timeout=None)
pause()

Interesting enough... if I start pushing the buttons in the sequence of the static, cruising, orbital... it works fine. But if I start the script and then press say, cruising or orbital, it errors out with RuntimeError: cannot join thread before it is started

Still more to troubleshoot... but I am further than I was this morning and I'm cool with that. :)

7
  • This code is unecessarialy complex (and inefficient as the loop will run continuously). If you are going to use static_button.wait_for_press there is no point in if static_button.is_pressed: Indeed the seems to be no need for a loop at all.
    – Milliways
    Jan 1, 2020 at 0:33
  • See the code example in gpiozero.readthedocs.io/en/stable/… which does this with a couple of lines of code.
    – Milliways
    Jan 1, 2020 at 0:34
  • thanks @Milliways, I thought it was to complex.. but if I have 3 different buttons, does the above make more sense? I'll still review the docs and try a few other iterations!
    – TimLavelle
    Jan 1, 2020 at 2:15
  • You asked a question, which joan answered. You then posted an answer on which I commented that using both button.wait_for_press and button.is_pressed makes no sense. You have now posted a new question in your modified answer (which is not the way things work here)
    – Milliways
    Jan 1, 2020 at 3:00
  • Your new code uses multiple button.wait_for_press which won't work as it will Pause the script until the device is activated. No one can write your code for you, particularly as you haven't stated what it should actually do. Starting to code without a clear idea of what it is to do is unproductive. I would probably use a state machine but https://gpiozero.readthedocs.io/en/stable/recipes_advanced.html#button-controlled-robot may do what you want.
    – Milliways
    Jan 1, 2020 at 3:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.