0

TL;DR: Since this is a long question I have the following problems:

  1. I'm looking for a good way to use gpiozero Button to raise SystemExit upon button press
  2. I have an imported module to drive an E-Ink display (Waveshare 2.7in). To shut down the display, the shutdown function of this module does besides other things also call GPIO.cleanup(), which clashes with gpiozeros automated cleanup
  3. Depending on whether I place GPIO.cleanup() and raise SystemExit within the button press callback function or the main loop of my dummy program below, I either get errors or I don't and I don't understand why

LONG VERSION

I have a Waveshare E-Ink Display (https://github.com/waveshare/e-Paper) and a button connected to my Raspberry Pi. My main program should run some code, check whether the display needs to be updated and then sleep for a set amount of time before checking again. When the button is pressed, the E-Ink Display should be cleared and turned off and then, the python program should quit. I'm using gpiozero to control the Button. The Waveshare E-Ink module to drive the display calls GPIO.cleanup() among other things when it is turned off. So I realize there is already some conflict between GPIO.cleanup() and gpiozero. My first code looked like this

from gpiozero import Button
from time import sleep
import RPi.GPIO as GPIO

btn1 = Button(5)

def handleBtnPress():

    GPIO.cleanup() #simulates the E-Ink Display shut down which also calls GPIO.cleanup()
    raise SystemExit

btn1.when_pressed = handleBtnPress

while True:

    #run main program stuff

    sleep(1)

Now when I run this code in Thonny on the Raspberry Pi, I get the following Error:

Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 302, in _set_when_changed
    self._disable_event_detect()
  File "/usr/lib/python3/dist-packages/gpiozero/pins/rpigpio.py", line 253, in _disable_event_detect
    GPIO.remove_event_detect(self.number)
RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)
Exception ignored in: <function GPIOBase.__del__ at 0xb5e39300>
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/gpiozero/devices.py", line 151, in __del__
  File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 394, in close
  File "/usr/lib/python3/dist-packages/gpiozero/devices.py", line 540, in close
  File "/usr/lib/python3/dist-packages/gpiozero/pins/rpigpio.py", line 136, in close
  File "/usr/lib/python3/dist-packages/gpiozero/pins/__init__.py", line 450, in <lambda>
  File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 302, in _set_when_changed
  File "/usr/lib/python3/dist-packages/gpiozero/pins/rpigpio.py", line 253, in _disable_event_detect
RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)

I realize this is because of GPIO.cleanup() being called in this case directly or later by the E-Ink module shutdown function. The second weird thing is that Thonny displays

Backend terminated or disconnected. Use 'Stop/Restart' to restart.

I then have to restart the backend manually.

I designed another way to shutdown the progam using the button. In this case, pressing the button does not call GPIO.cleanup() and raises SystemExit but rather sets an exitflag and the main program listens for that flag and shuts down when it sees it.

from gpiozero import Button
from time import sleep
import RPi.GPIO as GPIO
exitflag=False

btn1 = Button(5)

def handleBtnPress():

    global exitflag
    print("set exitflag")
    exitflag=True

btn1.when_pressed = handleBtnPress

while True:
    if exitflag==True:
        print("exitflag set")

        GPIO.cleanup() #simulates the E-Ink Display shut down which also calls GPIO.cleanup()

        print("E-Ink display off. Raising SystemExit")
        raise SystemExit

    else:
        print("exitflag not set")

    #run main program stuff

    sleep(1)

Now when I run this program, neither do I get errors from gpiozero because of the called GPIO.cleanup() nor do I get an error from Thonny about the backend being terminated. I realize the reason must be because I'm calling these functions not inside the function that is called upon button press.

I don't understand why this callback function behaves so differently from the main while loop, could somebody explain that?

While I could just use the exitflag strategy, depending on the sleep time in the loop, the program might continue some operations or take a long time before it checks the flag again and I don't want that.

Are there alternative ways to design the program beside my two suggestions. Any help is welcome

4
  • What are you ACTUALLY trying to achieve? – Milliways Feb 21 at 23:04
  • Exactly what I tried to explain: My program displays stuff on the E-Ink display and checks from time to time whether the info has to be updated. I'm just looking for a good way to stop my program upon pressing the button that doesn't lead to errors. To shut down the display, the Waveshare E-Ink display drivers call GPIO.cleanup() and other things which are not causing issues. The E-Ink display needs to properly shut down. Otherwise, if voltages are still set to high when the python progam exits, the display might take damage over time. – J.P. Feb 21 at 23:10
  • The reason I asked is because your questions is unclear (at least to me) - try writing in plain English - not "raise SystemExit". You won't get anywhere is you ignore RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM) – Milliways Feb 21 at 23:15
  • See raspberrypi.stackexchange.com/a/117897/8697 where the gpiozero author advises against mixing libraries. – Milliways Feb 22 at 3:40
0

The problem you have is that you're trying to use RPi.GPIO without setting the numbering system as required - as the error message states. However, as mentioned in the comments, it's not recommended that you mix the two libraries, and gpiozero has implicit cleanup, so you don't need to use GPIO.cleanup() - which is mentioned in the docs: https://gpiozero.readthedocs.io/en/stable/faq.html#gpio-cleanup

In your case, rather than raising SystemExit, you should call sys.exit() (see the Python docs for SystemExit)

Since sys.exit is callable without arguments, you can simply assign it as the button callback:

btn.when_pressed = sys.exit

If you want to do something else, you can wrap it in a function as before:

def exit():
    print("exiting")
    sys.exit()

btn.when_pressed = exit

It's worth reading the Migrating from RPi.GPIO page of the docs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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