Raspberry Pi Safe Reboot and Shutdown Button

Pages
Contributors: bboyho
Favorited Favorite 7

Making It Better

There’s always room for improvement. At the time of writing, I didn't realize that the example code used a lot of resources when using it with a Pi 3. While the example code worked, users reported that the code was using a substantial amount of resources when ran. After viewing the CPU usage with the top command on a Pi 4, the code was taking up 90%-100% of the Pi's processing power!

Raspberry Pi Processes Showing Python Process Taking up 100% of CPU

Possible Solutions: Delay or Interrupt

After checking with a co-worker, we agreed that the cause was from the while loop constantly checking the pin's state. Below are two possible solutions to reduce the load on the Pi's CPU.

  • Adding a Short Delay
  • Using an Interrupt

Adding a Short Delay

One suggestion was to add a small delay. Since a Raspberry Pi will just run the program as fast as it can, the delay prevents it from polling the pin at a high rate in the loop. Adding a simple time.sleep(0.5) in the while loop will free up the Pi's CPU significantly but still allow the code to read the button press to reboot or shutdown the Raspberry Pi.

language:python
.
.
.

while True:
    #short delay, otherwise this code will take up a lot of the Pi's processing power
    time.sleep(0.5)

.
.
.

The delay has been implemented in this tutorial's example code for your convenience. If you have already added this example code into an application, simply add the time.sleep(0.5) in the while loop, save the changes, and reboot the Pi for the changes to take effect. Of course, you could make the delay smaller so that the script reacts faster. Note that this will increase the amount of CPU used since you are checking the button state more frequently.

Using an Interrupt to Shutdown

Another suggestion was to adjust the code to use an interrupt to only execute the code when a button is pressed. On your Raspberry Pi, download the Python script by pressing the button below.

You can also copy the code and paste it in a text editor. Just make sure to name this file as safe_shutdown_interrupt_Pi.py and remember the location that the file was saved. Then follow the steps outlined above to modify the rc.local file in order to execute the code on startup.

language:python
# safe_shutdown_interrupt_Pi.py
#
# -----------------------------------------------------------------------------
#                 Raspberry Pi Safe Shutdown Python Script
# -----------------------------------------------------------------------------
# WRITTEN BY: Ho Yun "Bobby" Chan
# @ SparkFun Electronics
# MODIFIED: 3/18/2021
# DATE: 3/31/2020
#
# Based on code from the following blog and tutorials:
#
#    Kevin Godden
#    https://www.ridgesolutions.ie/index.php/2013/02/22/raspberry-pi-restart-shutdown-your-pi-from-python-code/
#
#    Pete Lewis
#    https://learn.sparkfun.com/tutorials/raspberry-pi-stand-alone-programmer#resources-and-going-further
#
#    Shawn Hymel
#    https://learn.sparkfun.com/tutorials/python-programming-tutorial-getting-started-with-the-raspberry-pi/experiment-1-digital-input-and-output
#
#    Ben Croston raspberry-gpio-python module
#    https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/
#
# ==================== DESCRIPTION ====================
#
# This python script takes advantage of the Qwiic pHat v2.0's
# built-in general purpose button to safely shutdown you Pi:
#
#    1.) If you press the button momentarily, the Pi will shutdown.
#
# This example also takes advantage of interrupts so that it uses a negligible
# amount of CPU. This is more efficient since it isn't taking up all of the Pi's
# processing power.
#
# ========== TUTORIAL ==========
#  For more information on running this script on startup,
#  check out the associated tutorial to adjust your "rc.local" file:
#
#        https://learn.sparkfun.com/tutorials/raspberry-pi-safe-reboot-and-shutdown-button
#
# ========== PRODUCTS THAT USE THIS CODE ==========
#
#   Feel like supporting our work? Buy a board from SparkFun!
#
#        Qwiic pHAT v2.0
#        https://www.sparkfun.com/products/15945
#
#   You can also use any button but you would need to wire it up
#   instead of stacking the pHAT on your Pi.
#
# LICENSE: This code is released under the MIT License (http://opensource.org/licenses/MIT)
#
# Distributed as-is; no warranty is given
#
# -----------------------------------------------------------------------------

import time
import RPi.GPIO as GPIO #Python Package Reference: https://pypi.org/project/RPi.GPIO/

# Pin definition
shutdown_pin = 17

# Suppress warnings
GPIO.setwarnings(False)

# Use "GPIO" pin numbering
GPIO.setmode(GPIO.BCM)

# Use built-in internal pullup resistor so the pin is not floating
# if using a momentary push button without a resistor.
#GPIO.setup(shutdown_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Use Qwiic pHAT's pullup resistor so that the pin is not floating
GPIO.setup(shutdown_pin, GPIO.IN)

# modular function to shutdown Pi
def shut_down():
    print("shutting down")
    command = "/usr/bin/sudo /sbin/shutdown -h now"
    import subprocess
    process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
    output = process.communicate()[0]
    print(output)




while True:
    #short delay, otherwise this code will take up a lot of the Pi's processing power
    time.sleep(0.5)

    # wait for a button press with switch debounce on the falling edge so that this script
    # is not taking up too many resources in order to shutdown the Pi safely
    channel = GPIO.wait_for_edge(shutdown_pin, GPIO.FALLING, bouncetime=200)

    if channel is None:
        print('Timeout occurred')
    else:
        print('Edge detected on channel', channel)

        # For troubleshooting, uncomment this line to output button status on command line
        #print('GPIO state is = ', GPIO.input(shutdown_pin))
        shut_down()

By using the GPIO.wait_for_edge() we can free up the CPU since the code waits for a rising or falling edge of a button press. In this case, we look for a falling edge on pin 17. If there is a button press, we will use the commands to shutdown the Pi like we did in the first example.

Using an Interrupt to Reboot and Shutdown

As explained in the previous section, the other option besides using a delay is to adjust the code to use an interrupt to only execute the code when a button is pressed. On your Raspberry Pi, download the Python script by pressing the button below.

You can also copy the code and paste it in a text editor. Just make sure to name this file as safe_restart_shutdown_interrupt_Pi.py and remember the location that the file was saved. Then follow the steps outlined above to modify the rc.local file in order to execute the code on startup.

language:python
# safe_restart_shutdown_interrupt_Pi.py
#
# -----------------------------------------------------------------------------
#                 Raspberry Pi Safe Restart and Shutdown Python Script
# -----------------------------------------------------------------------------
# WRITTEN BY: Ho Yun "Bobby" Chan
# @ SparkFun Electronics
# MODIFIED: 3/18/2021
# DATE: 3/31/2020
#
#
# Based on code from the following blog and tutorials:
#
#    Kevin Godden
#    https://www.ridgesolutions.ie/index.php/2013/02/22/raspberry-pi-restart-shutdown-your-pi-from-python-code/
#
#    Pete Lewis
#    https://learn.sparkfun.com/tutorials/raspberry-pi-stand-alone-programmer#resources-and-going-further
#
#    Shawn Hymel
#    https://learn.sparkfun.com/tutorials/python-programming-tutorial-getting-started-with-the-raspberry-pi/experiment-1-digital-input-and-output
#
#    Ben Croston raspberry-gpio-python module
#    https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/
#
# ==================== DESCRIPTION ====================
#
# This python script takes advantage of the Qwiic pHat v2.0's
# built-in general purpose button to safely reboot/shutdown you Pi:
#
#    1.) If you press the button momentarily, the Pi will reboot.
#    2.) Holding down the button for about 3 seconds the Pi will shutdown.
#
# This example also takes advantage of interrupts so that it uses a negligible
# amount of CPU. This is more efficient since it isn't taking up all of the Pi's
# processing power.
#
# ========== TUTORIAL ==========
#  For more information on running this script on startup,
#  check out the associated tutorial to adjust your "rc.local" file:
#
#        https://learn.sparkfun.com/tutorials/raspberry-pi-safe-reboot-and-shutdown-button
#
# ========== PRODUCTS THAT USE THIS CODE ==========
#
#   Feel like supporting our work? Buy a board from SparkFun!
#
#        Qwiic pHAT v2.0
#        https://www.sparkfun.com/products/15945
#
#   You can also use any button but you would need to wire it up
#   instead of stacking the pHAT on your Pi.
#
# LICENSE: This code is released under the MIT License (http://opensource.org/licenses/MIT)
#
# Distributed as-is; no warranty is given
#
# -----------------------------------------------------------------------------

import time
import RPi.GPIO as GPIO #Python Package Reference: https://pypi.org/project/RPi.GPIO/

# Pin definition
reset_shutdown_pin = 17

# Suppress warnings
GPIO.setwarnings(False)

# Use "GPIO" pin numbering
GPIO.setmode(GPIO.BCM)

# Use built-in internal pullup resistor so the pin is not floating
# if using a momentary push button without a resistor.
#GPIO.setup(reset_shutdown_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Use Qwiic pHAT's pullup resistor so that the pin is not floating
GPIO.setup(reset_shutdown_pin, GPIO.IN)

# modular function to restart Pi
def restart():
    print("restarting Pi")
    command = "/usr/bin/sudo /sbin/shutdown -r now"
    import subprocess
    process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
    output = process.communicate()[0]
    print(output)

# modular function to shutdown Pi
def shut_down():
    print("shutting down")
    command = "/usr/bin/sudo /sbin/shutdown -h now"
    import subprocess
    process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
    output = process.communicate()[0]
    print(output)




while True:
    #short delay, otherwise this code will take up a lot of the Pi's processing power
    time.sleep(0.5)

    # wait for a button press with switch debounce on the falling edge so that this script
    # is not taking up too many resources in order to shutdown/reboot the Pi safely
    channel = GPIO.wait_for_edge(reset_shutdown_pin, GPIO.FALLING, bouncetime=200)

    if channel is None:
        print('Timeout occurred')
    else:
        print('Edge detected on channel', channel)

        # For troubleshooting, uncomment this line to output button status on command line
        #print('GPIO state is = ', GPIO.input(reset_shutdown_pin))
        counter = 0

        while GPIO.input(reset_shutdown_pin) == False:
            # For troubleshooting, uncomment this line to view the counter. If it reaches a value above 4, we will restart.
            #print(counter)
            counter += 1
            time.sleep(0.5)

            # long button press
            if counter > 4:
                shut_down()

        #if short button press, restart!
        restart()

We use the GPIO.wait_for_edge() once again and look for a falling edge on pin 17. If there is a button press, we will use the commands to either reboot or shutdown the Pi like we did in the second example. Since the button will continue to be pressed down, its state does not change. We will check to see how long the button is pressed. For short button presses, the Pi will restart. For long presses after the counter reaches a value above 4, the Pi will shutdown.

Which is Method Better?

So how effective is adding a short delay vs an interrupt? Adding a short delay reduced the amount of processing used by the CPU significantly to about 0.3%.

Raspberry Pi Processes Showing Python Process Taking a Minimal Amount of CPU with Delay

Adding an interrupt also reduced the amount of processing used by the CPU significantly. The script stopped using the CPU once it reached the line with GPIO.wait_for_edge(). After using top in the command line, the process needed to be searched with the L command followed by entering word python since it was not shown near the top of the list.

Raspberry Pi Processes Showing Python Process Taking a Negligible Amount of CPU with Interrupt

While adding a short delay is a quick fix, it was slow to react and was wasted some of the Pi's processing as it kept checking the button state. Adding an interrupt was not as simple but it was faster to react and more efficient since it was not wasting any of the Pi's processing as it waited for the falling edge of the button. Overall, I'd say using interrupts is better but if you are in a rush to write code, adding a delay would be the alternative option.