Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python button functions oddly not doing the same

I currently have 2 buttons hooked up to my Raspberry Pi (these are the ones with ring LED's in them) and I'm trying to perform this code

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

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(17, GPIO.OUT) #green LED
GPIO.setup(18, GPIO.OUT) #red LED
GPIO.setup(4, GPIO.IN, GPIO.PUD_UP) #green button
GPIO.setup(27, GPIO.IN, GPIO.PUD_UP) #red button

def remove_events():
        GPIO.remove_event_detect(4)
        GPIO.remove_event_detect(27)

def add_events():
        GPIO.add_event_detect(4, GPIO.FALLING, callback=green, bouncetime=800)
        GPIO.add_event_detect(27, GPIO.FALLING, callback=red, bouncetime=800)

def red(pin):
        remove_events()
        GPIO.output(17, GPIO.LOW)
        print "red pushed"
        time.sleep(2)
        GPIO.output(17, GPIO.HIGH)
        add_events()

def green(pin):
        remove_events()
        GPIO.output(18, GPIO.LOW)
        print "green pushed"
        time.sleep(2)
        GPIO.output(18, GPIO.HIGH)
        add_events()

def main():
    while True:
        print "waiting"
        time.sleep(0.5)

GPIO.output(17, GPIO.HIGH)
GPIO.output(18, GPIO.HIGH)
GPIO.add_event_detect(4, GPIO.FALLING, callback=green, bouncetime=800)
GPIO.add_event_detect(27, GPIO.FALLING, callback=red, bouncetime=800)

if __name__ == "__main__":
    main()

On the surface it looks like a fairly easy script. When a button press is detected:

  1. remove the events
  2. print the message
  3. wait 2 seconds before adding the events and turning the LED's back on

Which normally works out great when I press the green button. I tried it several times in succession and it works without fail. With the red, however, it works well the first time, and the second time, but after it has completed it second red(pin) cycle the script just stops.

Considering both events are fairly similar, I can't explain why it fails on the end of the 2nd red button.

EDIT: I have changed the pins from red and green respectively (either to different pin's completely or swap them). Either way, it's always the red button code (actually now green button) causes an error. So it seems its' not a physical red button problem, nor a pin problem, this just leaves the code to be at fault...

like image 659
user5740843 Avatar asked Sep 30 '16 07:09

user5740843


People also ask

How do you use and_() and or_() in Python?

Note: Python also has and_ () and or_ () functions. However, they reflect their corresponding bitwise operators rather than the Boolean ones. The and_ () and or_ () functions also work with Boolean arguments: In these examples, you use and_ () and or_ () with True and False as arguments.

What is the not operator in Python?

The not operator is the Boolean or logical operator that implements negation in Python. It’s unary, which means that it takes only one operand. The operand can be a Boolean expression or any Python object. Even user-defined objects work. The task of not is to reverse the truth value of its operand.

How to invoke a function or method automatically when the button clicks?

To invoke a function or a method of a class automatically when the button is clicked, you assign its command option to the function or method. This is called the command binding in Tkinter. To create a button, you use the ttk.Button constructor as follows: button = ttk.Button (container, **option) Code language: Python (python) ...

How do you use not in Python?

Python’s not is a logical operator that inverts the truth value of Boolean expressions and objects. It’s handy when you need to check for unmet conditions in conditional statements and while loops. You can use the not operator to help you decide the course of action in your program.


1 Answers

I was able to reproduce your problem on my Raspberry Pi 1, Model B by running your script and connecting a jumper cable between ground and GPIO27 to simulate red button presses. (Those are pins 25 and 13 on my particular Pi model.)

The python interpreter is crashing with a Segmentation Fault in the thread dedicated to polling GPIO events after red returns from handling a button press. After looking at the implementation of the Python GPIO module, it is clear to me that it is unsafe to call remove_event_detect from within an event handler callback, and this is causing the crash. In particular, removing an event handler while that event handler is currently running can lead to memory corruption, which will result in crashes (as you have seen) or other strange behaviors.

I suspect you are removing and re-adding the event handlers because you are concerned about getting a callback during the time when you are handing a button press. There is no need to do this. The GPIO module spins up a single polling thread to monitor GPIO events, and will wait for one callback to return before calling another, regardless of the number of GPIO events you are watching.

I suggest you simply make your calls to add_event_detect as your script starts, and never remove the callbacks. Simply removing add_events and remove_events (and their invocations) from your script will correct the problem.

If you are interested in the details of the problem in the GPIO module, you can take a look at the C source code for that module. Take a look at run_callbacks and remove_callbacks in the file RPi.GPIO-0.6.2/source/event_gpio.c. Notice that both of these functions use a global chain of struct callback nodes. run_callbacks walks the callback chain by grabbing one node, invoking the callback, and then following that node's link to the next callback in the chain. remove_callbacks will walk the same callback chain, and free the memory associated with the callbacks on a particular GPIO pin. If remove_callbacks is called in the middle of run_callbacks, the node currently held by run_callbacks can be freed (and have its memory potentially reused and overwritten) before the pointer to the next node is followed.

The reason you see this problem only for the red button is likely due to the order of calls to add_event_detect and remove_event_detect causes the memory previously used by the callback node for the red button to be reclaimed for some other purpose and overwritten earlier than the memory used from the green button callback node is similarly reclaimed. However, be assured that the problem exists for both buttons -- it is just luck that that the memory associated with the green button callback isn't changed before the pointer to the next callback node is followed.

More generally, there is a concerning lack of thread synchronization around the callback chain use in the GPIO module in general, and I suspect similar problems could occur if remove_event_detect or add_event_detect are called while an event handler is running, even if events are removed from another thread! I would suggest that the author of the RPi.GPIO module should use some synchronization to ensure that the callback chain can't be modified while callbacks are being made. (Perhaps, in addition to checking whether the chain is being modified on the polling thread itself, pthread_mutex_lock and pthread_mutex_unlock could be used to prevent other threads from modifying the callback chain while it is in use by the polling thread.)

Unfortunately, that is not currently the case, and for this reason I suggest you avoid calling remove_event_detect entirely if you can avoid it.

like image 187
mkimball Avatar answered Sep 18 '22 23:09

mkimball