0

Is it possible to have button push listener in Python for the Raspberry Pi. I have a non-latching button going to a GPIO. I want to run some python code the first time the button is pushed. Then I want the code to stop on the second button push no matter where it is in the first line of code.

I've used a toggle bit variable called "flag" to register button pushes but obviously there is no listener to determine when the second push is made.

#!/usr/bin/env python
import RPi.GPIO as GPIO
import time

Button = 16    # pin16

def setup():

    GPIO.setmode(GPIO.BOARD)          # Numbers GPIOs by physical location
    GPIO.setup(Button, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)   # Button Input

def motorcontrol():
    flag=0
    while True:
        j=GPIO.input(16)
        if j==1: #Robot is activated when button is pressed
            flag=1
            print "Robot Activated",j
        while flag==1:    
            time.sleep(5)
            print "Robot Activated for 5 seconds"
            time.sleep(5)
            print "Robot Activated for 10 seconds"
            time.sleep(5)
            print "Robot Activated for 15 seconds"

            j=GPIO.input(16)
            if j==1: #De activate robot on pushing the button
                flag=0
                print "Robot DeActivated",j
                destroy()

def destroy():
    GPIO.cleanup()                     # Release resource               

if __name__ == '__main__':     # Program start from here
    setup()
    try:
        motorcontrol()
    except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child program destroy() will be  executed.
        destroy()
2
  • razzpisampler.oreilly.com/ch07.html Commented Jan 2, 2017 at 8:04
  • you can't use sleep() because it blocks your code. You shoulf do somethink like: at start set first_text = current_time + 5 seconds and later in loop check if current_time >= first_text: print "Robot Activated for 5 seconds" Commented Jan 2, 2017 at 8:07

1 Answer 1

1

You can't use sleep() this way because your while loop can't check your botton. You have to loop all time and check if it is time to display text.

You can use small sleep() to use lees CPU.

import time

current_time = time.time()

text_1 = current_time + 5
text_2 = current_time + 10
text_3 = current_time + 15

flag = True

while flag:

    # TODO: check your button and change `flag`

    current_time = time.time()

    if text_1 and current_time >= text_1:
        print("5 seconds")
        text_1 = None #  to stop displaying
        # or show again after 5 seconds
        #text_1 = current_time + 5

    if text_2 and current_time >= text_2:
        print("10 seconds")
        text_2 = None #  to stop displaying

    if text_3 and current_time >= text_3:
        print("15 seconds")
        text_3 = None #  to stop displaying
        flag = False

    #time.sleep(0.1)

Or more like in most GUI

import time

# --- functions ---

def callback_1():
    print("5 seconds")

    # add new task to list
    tasks.append( (current_time + 5, callback_1) )

def callback_2():
    print("10 seconds")

def callback_3():
    print("15 seconds")

def callback_4():
    global flag
    flag = False

# --- main ---

current_time = time.time()

tasks = []

tasks.append( (current_time + 5, callback_1) )
tasks.append( (current_time + 10, callback_2) )
tasks.append( (current_time + 15, callback_3) )
tasks.append( (current_time + 17, callback_4) )

flag = True

while flag:

    # TODO: check your button

    current_time = time.time()

    # this way I execute task and remove from list
    new_tasks = []

    for t, c in tasks:
        if current_time >= t:
            c()
        else:
            new_tasks.append( (t,c) )

    tasks = new_tasks

    #time.sleep(0.1)

EDIT: I don't have RPi so I try to simulate it using own class GPIO - but maybe it will work on your computer. It shows where you should put code.

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

'''
#
# for test only - instead of `import RPi.GPIO as GPIO`
#

# simulate button press
current_time = time.time()
button_1 = current_time + 2
button_2 = current_time + 10

class GPIO:
    BOARD = None
    IN = None
    PUD_DOWN = None

    @staticmethod
    def setmode(a):
        pass

    @staticmethod
    def setup(a, b, pull_up_down=None):
        pass

    @staticmethod
    def input(a):
        global button_1, button_2

        current_time = time.time()

        if button_1 and current_time >= button_1:
            button_1 = None
            return 1

        if button_2 and current_time >= button_2:
            button_2 = None
            return 1

        return 0

    @staticmethod
    def cleanup():
        pass
'''

Button = 16    # pin16

def setup():
    GPIO.setmode(GPIO.BOARD)          # Numbers GPIOs by physical location
    GPIO.setup(Button, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)   # Button Input

def motorcontrol():
    flag = False

    while True:
        j = GPIO.input(16)
        if j == 1:
            flag = True

            print "Robot Activated", j
            current_time = time.time()

            text_1 = current_time + 5
            text_2 = current_time + 10
            text_3 = current_time + 15

        while flag:    
            j = GPIO.input(16)
            if j == 1:
                flag = False
                print "Robot DeActivated", j
                destroy()

            current_time = time.time()

            if text_1 and current_time >= text_1:
                print "5 seconds"
                text_1 = None #  to stop displaying
                # or show again after 5 seconds
                #text_1 = current_time + 5

            if text_2 and current_time >= text_2:
                print "10 seconds"
                text_2 = None #  to stop displaying

            if text_3 and current_time >= text_3:
                print "15 seconds"
                text_3 = None #  to stop displaying
                flag = False

            time.sleep(0.1)

def destroy():
    GPIO.cleanup()      

if __name__ == '__main__':
    setup()
    try:
        motorcontrol()
    except KeyboardInterrupt:  
        destroy()
Sign up to request clarification or add additional context in comments.

2 Comments

That's good stuff. Thanks. However the first code when run produces all lines of text at the same time without waiting for the 5 seconds because the while loop doesn't start until I click the button for the first time (initiating the code) so current_time is always greater than text_3. When I try to update the current time and the text_1 after first button press I get the following error 'text_1 = current_time + 5 TypeError: unsupported operand type(s) for +: builtin_function_or_method and int'
My plan was to start the program with the button and have my robot travel a square path repeatedly until I press the button a second time to end the program. I was planning to use time.sleep to control how long each motor was on for hence the above test code piece.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.