Adding a shutdown button to your Raspberry Pi Flask App

A quick (and dirty) method to add a shutdown button to your Raspberry Pi, allowing you to shut it down when in Kiosk Mode.

If you're running a Flask App on your Raspberry Pi in Kiosk mode you may find that you want to add a shutdown button to the ensemble so that you can switch the Pi off safely without having to SSH into it and manually issue the shutdown command. Based on Adding a Shutdown Button to the Raspberry Pi B+

The Hardware

  • a momentary button
  • a breadboard
  • a couple of jumper wires
  • a raspberry pi (I'm using a 3)
Fritzing image showing the pi and attached button
Raspberry Pi GPIO header illustration

Wire the momentary switch up to Pin 18 and Gnd of the Raspberry Pi. As it's a momentary switch it doesn't matter which way around the wires are.

The Python Script

So, to make this button do anything you need a fairly short python script

import RPi.GPIO as GPIO
import os

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def shutdown(channel):
    os.system("sudo shutdown -h now")
    GPIO.add_event_detect(18, GPIO.FALLING, callback=shutdown, bouncetime=2000)

Break the code down a little

  • GPIO.setmode(GPIO.BCM). This is where you decide whether you want to use the GPIO numbers (BCM - which change with different revisions of the Raspberry Pi or the board numbers (BOARD) which correlate to the numbers actually printed on the Pi. If you fancy going deeper into this there's a clear and consise explanation of the difference between the two settings in this Stack Exchange answer and a very useful walkthrough of using the GPIO header and pins on Raspberry Pi Spy.
  • GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP). This sets up the channel number (acording to the schema you specified in the previous line, whether it's an input or and out put and whether it's pulling up or down (for down use PUD_DOWN.
  • Shutdown function - what you want to do when the interrupt fires.
  • the event_detected() function - this is where the really cool stuff happens. According to the Rasperry Pi GPIO wiki:
The event_detected() function is designed to be used in a loop with other things, but unlike polling it is not going to miss the change in state of an input while the CPU is busy working on other things. This could be useful when using something like Pygame or PyQt where there is a main loop listening and responding to GUI events in a timely basis.

This is also why it's really useful to us. What we're setting up:

  • the channel (again, acording to the schema you specified earlier).
  • which bit of the signal we're looking for
  • the callback
  • how long we should wait to debounce.

If you transfer this to your Pi and run it (python /route/to/file/rpi_shutdown.py) when you press the button your Pi should shut itself down. You may need to pip install RPi.GPIO to make it work especially if you have an active virtualenv.

Adding the shutdown functionality to your Flask App

In the original tutorial this is called from /etc/rc.local so that it's accissible from everywhere, unfortunately Python threads really badly and, if you're using a virtualenv then the chances of it working at all are slim to none so you want to add the watcher to your flask app. This isn't actually difficult to do but where you do it will depend on whether or not your app is modular. You need the function to be called in the whereever you initialise your app. In my case this is usually in a file called something like run.py which I use to call all the rest of the functions.

run.py

# shutdown button imports

import RPi.GPIO as GPIO
import os GPIO.setmode(GPIO.BCM)

GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def shutdown(channel):
    os.system("sudo shutdown -h now")

if __name__ == '__main__':
    # initialise the button inside the main loop
    GPIO.add_event_detect(18, GPIO.FALLING, callback=shutdown, bouncetime=2000)
    app.run()

Now when you run your app you're also setting up the gpio for the shutdown button so you can arbitrarily shutdown the pi from inside your app. Very useful if you're running in kiosk mode.

Comments powered by Talkyard.