Skip to content

Instantly share code, notes, and snippets.

@lbussy
Last active March 17, 2024 21:00
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save lbussy/9e81cbcc617952f1250e353bd42e7775 to your computer and use it in GitHub Desktop.
Save lbussy/9e81cbcc617952f1250e353bd42e7775 to your computer and use it in GitHub Desktop.
External Button Shutdown for Raspberry Pi

Shutdown Raspberry Pi with a Single Button

There are two methods:

  1. Scripted Method
  2. Boot Overlay Method (also below as "tl;dr")

tl;dr Version

Add the following to your /boot/config.txt and reboot:

dtoverlay=gpio-shutdown,gpio_pin=3
gpio=4=op,dh

This example uses the following:

  • Boot Button to Ground:
    • Physical/Board pin 5
    • GPIO/BCM pin 3 (this is I2C1/SCL so be aware of this if you are using I2C)
    • Wiring Pi pin 9
  • LED Pin to Ground with 150 Ohm Resistor:
    • Physical/Board pin 7
    • GPIO/BCM pin 4
    • Wiring Pi pin 7

Scripted Version

This script will poll the designated GPIO pin and initiate a system shutdown if it is pulled low (shorted) for > 0.5 seconds.

Dependencies

This python script uses gpiozero which simplifies interfacing GPIO devices with the Raspberry Pi.

Instructions

  • Copy rebooter.py to /usr/bin/rebooter.py
  • Copy rebooter.service to /lib/systemd/system/rebooter.service
  • Install rebooter.service:
    • sudo systemctl daemon-reload
    • sudo systemctl enable rebooter
    • sudo systemctl start rebooter

Boot Overlay (see tl;dr above)

In system version 225 and above, power-down and power-up is possible via shorting GPIO3 (pin 5). This functionality is enabled by adding the following line to /boot/config.txt and restarting:

dtoverlay=gpio-shutdown,gpio_pin=3

Note: the qualifier gpio_pin=3 is redundant since this pin is the default.

GPIO3 has an external pull-up, so shorting to ground briefly will initiate a shutdown. A subsequent short to ground (provided power is still applied) will power-on the system.

The boot overlay documentation for this function is as follows:

Name:   gpio-shutdown
Info:   Initiates a shutdown when GPIO pin changes. The given GPIO pin
        is configured as an input key that generates KEY_POWER events.

        This event is handled by systemd-logind by initiating a
        shutdown. Systemd versions older than 225 need an udev rule
        enable listening to the input device:

                ACTION!="REMOVE", SUBSYSTEM=="input", KERNEL=="event*", \
                        SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", \
                        ATTRS{keys}=="116", TAG+="power-switch"

        Alternatively this event can be handled also on systems without
        systemd, just by traditional SysV init daemon. KEY_POWER event
        (keycode 116) needs to be mapped to KeyboardSignal on console
        and then kb::kbrequest inittab action which is triggered by
        KeyboardSignal from console can be configured to issue system
        shutdown. Steps for this configuration are:

            Add following lines to the /etc/console-setup/remap.inc file:

                # Key Power as special keypress
                keycode 116 = KeyboardSignal

            Then add following lines to /etc/inittab file:

                # Action on special keypress (Key Power)
                kb::kbrequest:/sbin/shutdown -t1 -a -h -P now

            And finally reload configuration by calling following commands:

                # dpkg-reconfigure console-setup
                # service console-setup reload
                # init q

        This overlay only handles shutdown. After shutdown, the system
        can be powered up again by driving GPIO3 low. The default
        configuration uses GPIO3 with a pullup, so if you connect a
        button between GPIO3 and GND (pin 5 and 6 on the 40-pin header),
        you get a shutdown and power-up button. Please note that
        Raspberry Pi 1 Model B rev 1 uses GPIO1 instead of GPIO3.
Load:   dtoverlay=gpio-shutdown,<param>=<val>
Params: gpio_pin                GPIO pin to trigger on (default 3)
                                For Raspberry Pi 1 Model B rev 1 set this
                                explicitly to value 1, e.g.:

                                    dtoverlay=gpio-shutdown,gpio_pin=1

        active_low              When this is 1 (active low), a falling
                                edge generates a key down event and a
                                rising edge generates a key up event.
                                When this is 0 (active high), this is
                                reversed. The default is 1 (active low).

        gpio_pull               Desired pull-up/down state (off, down, up)
                                Default is "up".

                                Note that the default pin (GPIO3) has an
                                external pullup. Same applies for GPIO1
                                on Raspberry Pi 1 Model B rev 1.

        debounce                Specify the debounce interval in milliseconds
                                (default 100)

Power-On Indicator

There are a couple of ways to go with this.

  1. Raspbian has a boot overlay functionality, which will drive a GPIO high or low on power off (including halt). This configuration makes such a GPIO functional as a power indicator (or signals when the system may be safely switched off.) A drawback to this is when enabled, it disables the ability to start the system with the gpio-shutdown functionality described above.
  2. One may use a GPIO indicator to control an LED with a couple of methods. The drawback to this is that the LED lags system startup and precedes complete system hald by a few seconds
    1. cron Entry
    2. config.txt Entry

Boot Overlay Indicator

To use this indicator functionality, assuming GPIO4, add the following line to /boot/config.txt and reboot:

dtoverlay=gpio-poweroff,gpiopin=4,active_low=1

Using active_low=1 will make the pin high when the system is running. That is, when using it to drive an LED, the LED will shut off when it is safe to remove power.

The boot overlay documentation for this function is as follows:

Name:   gpio-poweroff
Info:   Drives a GPIO high or low on poweroff (including halt). Enabling this
        overlay will prevent the ability to boot by driving GPIO3 low.
Load:   dtoverlay=gpio-poweroff,<param>=<val>
Params: gpiopin                 GPIO for signalling (default 26)

        active_low              Set if the power control device requires a
                                high->low transition to trigger a power-down.
                                Note that this will require the support of a
                                custom dt-blob.bin to prevent a power-down
                                during the boot process, and that a reboot
                                will also cause the pin to go low.
        input                   Set if the gpio pin should be configured as
                                an input.
        export                  Set to export the configured pin to sysfs
        timeout_ms              Specify (in ms) how long the kernel waits for
                                power-down before issuing a WARN (default 3000).

GPIO Indicator

As an alternative to the overlay functionality above, a GPIO may be driven high or low without the overlay to serve as an indicator without canceling the functionality of the gpio-shutdown overlay.

There is a small delay on startup, and it potentially turns off shortly before the system is entirely dead, but it provides an overall indication of the device's power status.

Two such methods are described here:

CRON Entry

An LED may be added to a GPIO, then turned it on at startup by adding this line to crontab (GPIO4 used as an example):

@reboot raspi-gpio set 4 op dh

config.txt Entry

You may add a line to /boot/config.txt to toggle the GPIO on and off to approximate the system state (GPIO4 used as an example):

gpio=4=op,dh
# Enable startup/shutdown with GPIO3
dtoverlay=gpio-shutdown,gpio_pin=3
# Enable overlay-based system state indicator
# dtoverlay=gpio-poweroff,gpiopin=4,active_low=1
# Enable GPIO-based system state indicator
gpio=4=op,dh
#!/usr/bin/python3
from gpiozero import Button
from time import sleep
from os import system, getuid
from sys import stdout, exit
stopPin = 26
stopButton = Button(stopPin) # defines the button as an object and chooses GPIO pin
def isRoot():
if getuid() != 0:
return False
else:
return True
def main():
print("\nMonitoring pin {} for reboot signal.".format(stopPin))
print("Ctrl-C to quit.\n")
try:
while (True):
if stopButton.is_pressed:
sleep(0.5)
if stopButton.is_pressed:
system("shutdown now -h")
sleep(0.1)
except KeyboardInterrupt:
print('\n\nKeyboard interrupt.')
finally:
pass
return
if __name__ == "__main__":
if not (isRoot()):
print("\nScript must be run as root.")
exit(1)
else:
main()
exit(0)
[Unit]
Description=Rebooter Service
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/bin/rebooter.py
[Install]
WantedBy=multi-user.target
@AgentRev
Copy link

gpio=4=op,dh does not work, pin stays high after shutdown.

@lbussy
Copy link
Author

lbussy commented Nov 13, 2023

gpio=4=op,dh does not work, pin stays high after shutdown.

Were you trying to use both the shutdown pin and the system state indicator? For some reason, I'm sure is a good one, the above is incompatible with using a pin to shutdown the system:

dtoverlay=gpio-shutdown,gpio_pin=3

It's amazing that it took until now for a real power switch to be available on the Pi 5.

@AgentRev
Copy link

Yes, I used the config from your tl;dr, which causes GPIO4 to stay high at all times:

dtoverlay=gpio-shutdown,gpio_pin=3
gpio=4=op,dh

I ended up using the following instead, which however limits the shutdown pin to a one-time use, requiring power-cycling to boot the Pi back.

dtoverlay=gpio-shutdown,gpio_pin=3
dtoverlay=gpio-poweroff,gpiopin=4,active_low=1

I'm not exactly sure why it can't simultaneously keep GPIO3 active and GPIO4 low after shutdown, but oh well.

@lbussy
Copy link
Author

lbussy commented Nov 14, 2023

I was never able to get both to work. I recall documentation saying the same thing. Seems odd, but these are not MacBooks.

@Drexel2k
Copy link

Hey!

tl;dr Version works fine on my Raspberry 4. But to power off, the shutdown dialog pops up where I can chose to shutdown, reboot or logout and I have to press the button another time. Is there a way to shutdown directly without this dialog?

@lbussy
Copy link
Author

lbussy commented Jan 24, 2024

tl;dr Version works fine on my Raspberry 4. But to power off, the shutdown dialog pops up where I can chose to shutdown, reboot or logout and I have to press the button another time. Is there a way to shutdown directly without this dialog?

https://forums.raspberrypi.com/viewtopic.php?t=363725

Looks like you need to click twice. Odd, but hey.

@AgentRev
Copy link

AgentRev commented Jan 24, 2024

@Drexel2k On RPi OS, I think all the power button does is call the lxde-logout command, so you should probably be able to override it with something like alias lxde-logout='shutdown -h now' in ~/.profile or by creating a shell script at /usr/bin/lxde-logout

@davthomaspilot
Copy link

If I'm using the gpio-poweroff overlay, on a specific GPIO, can that GPIO also be driven from code?

If the GPIO is configured as an output and driven by code, will it still go to the state configured by the overlay at power off?

@lbussy
Copy link
Author

lbussy commented Feb 20, 2024

No, a pin is either an "in" (read) or an out (write.)

@davthomaspilot
Copy link

well, it would be an output--it's driven by the rpi to indicate it has powered down.

Did you think I was asking about the shutdown overlay?

So, the designated pin is always an output. My question is what happens if my code drives it? Will it change to the state that indicates the rpi is powered down when powerdown occurs?

@lbussy
Copy link
Author

lbussy commented Feb 20, 2024

Do you mean the LED? Not sure about that. I'd think the daemon would continuously drive the LED high (off) but you'd need to test.

@davthomaspilot
Copy link

No,

I am specifying a GPIO to be used to indicate the RPI has been shutdown as described in the documentation above. That GPIO isn't driving the LED

That GPIO OUTPUT is only used by hardware on a higher level card assembly. That external hardware turns off other devices in the system once the RPI has completely shutdown.

It would be useful to be able to also control the GPIO output from code running on the RPI--one use would be for manufacturing test of the higher level card assembly.

Can I do this? Where is the right place to ask?

@lbussy
Copy link
Author

lbussy commented Feb 20, 2024

Well, you can either try it or ask on the Raspberry Pi Forums. There are some knowledgeable people there (but you will have to weed through the not-so-knowledgeable people.)

@davthomaspilot
Copy link

Trying it might not be so easy. There's a note in the documentation about high power dissipation if you do something I was planning to do.

I'd like to get an answer from someone knowledgeable with the code, or, better yet, look at the code myself.

I'll try the RPI forum, but I usually don't get a good answer from there (probably 100 posts, maybe five or six answers worth the post).

@lbussy
Copy link
Author

lbussy commented Feb 21, 2024

It's all open source, of course finding what you need is a special sort of hell. Good luck, sir.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment