Raspberry Pi power-off button

I have a number of Raspberry Pi installed in my home for specific tasks that have no monitor or keyboard connected. To shut these down, I always need to ssh into them (fortunately they are all network connected) but this is not very practical. In this post I will describe a power-off button I have implemented to shutdown a headless Raspberry Pi cleanly.

The Raspberry Pi is a great small computer that often runs a full Linux distribution or Linux-based appliance and hence requires to be shutdown properly to not damage the filesystem on the SDHC card containing its software. To shutdown a headless Raspberry Pi (i.e. without keyboard/monitor), one either needs to connect a keyboard to hit CTRLALTDEL or log in over the network to shut it down. As this is not very practical I have been looking for a solution to simply push a button to shutdown a headless Raspberry Pi for a while.

Now I am definitely the first solving this problem, see also this MagPi article, this or this Hackster guide or this elaborate guide for the RPi4, but somehow none of these suited my needs (either too simple or too complex) and/or had dependencies on external libraries. Besides they were all quick solutions (which is fine as they were designed as such) and not really flexible and documented. Although my solution is the same hard-ware wise, I have implemented the generic script gpio_trigger.py (available on my GitLab instance).

My specific use case was a Raspberry Pi2 running Calin Crisan’s excellent motionEyeOS, a great solution to build your own camera surveillance system. As the Pi2 running it also had a camera itself, it was located in an impractical place in my house to plug in a keyboard (on top of a cupboard in my kitchen), I really needed something else to power it off when needed.

The power-off button

To create the power-off button, all that is needed are basic soldering gear, a simple NC (normally not connected) switch and short dupont cable with 2 female connectors:

parts needed

As we need to connect the switch to the Raspberry Pi GPIO connector, I simply cut this cable in the middle, stripped the two just-cut ends and soldered them to the two contacts of the switch. Next I connected the sides with the headers to pins 39 and 40 of the Raspberry Pi GPIO as depicted below (yes different cable as I made several of these):

power-off switch installed in the case

As you can see the power-button fits nicely in the case I had (the hole was already there). GPIO pins #39 and #40 are the two pins close to the USB port.

Testing the button

Before closing the case, it is good to test the setup. I did this with the script I created by running:

sudo ./gpio_trigger.py echo button pressed

This runs the script in interactive mode and waits until the button is pressed and then executes the shell command echo button pressed (which just prints “button pressed”). The script must be run as root (or with sudo as I do in this example) to access the GPIO.

Once this works, the next step is to test the script as intended, to power-off your Raspberry Pi. For this run the command:

sudo ./gpio_trigger.py -D poweroff

This will start the script in the background (option -D) waiting for the button to be pressed. The shell prompt will immediately return and you can even log out. Below movie shows what happens when the button is pressed.

Once the button is pressed, the green I/O LED flashes a few times randomly and then 10 times to indicate that the Pi has powered down.

When this works, you can implement this solution permanently on your Raspberry Pi. Please note though that by using poweroff, the Pi will end up in a mode that requires to disconnect the power and connect it again to restart the Pi (or to add a reset button to start it again, which I don’t need).

Implementing on motionEyeOS

To implement this as a permanent solution, gpio_trigger.py must be started during the boot-up process. For MotionEyeOS this means copying it to /data/gpio_trigger.py and adding to /data/etc/userinit.sh:

/data/gpio_trigger.py --daemon --hold 5000 poweroff

As you can see I am using long command options as they are more clear later and also added --hold 5000, which requires the power-off button to be kept pressed for 5 seconds so that an accidental press would not shut it down.

Since with my Pi’s camera case is closed I can barely see the internal LEDs so I added visible feedback using the Pi’s network adapter’s LEDs. Normally I have these switched off so that they don’t annoy anyone but during the power-off sequence I wanted them to switch on briefly. The contents of my /data/etc/userinit.sh for this are::

# disable the ethernet LEDs
lan951x-led-ctl --fdx=0 --lnk=0 --spd=0

# enable ethernet LEDs and power off when button is pressed 5s
/data/gpio_trigger.py --daemon --hold 5000 -- lan951x-led-ctl --fdx=1 --lnk=1 --spd=1\; poweroff

Now when I press the button after 5 seconds the ethernet lights will go on and then when the power-off is complete will go out again. Below a picture of my motionEyeOS camera with power-off button on the right side.

final result – Pi2 camera with off button

Implementing using systemd

As I also have a Raspberry Pi running a normal linux distribution, I also implemented this for Raspbian using systemd (other RPi distributions should be similar). From my GitLab instance:

  • copy the file rpi_poweroff_button.serviceand copy it to /etc/systemd/system/rpi_poweroff_button.service
  • copy the file gpio_trigger.py to /usr/local/sbin/gpio_trigger.pyand make it executable.

Next run the following two commands to enable and activate

sudo systemctl enable rpi_poweroff_button
sudo service rpi_poweroff_button start

This will activate gpio_trigger.py running as a service to be started as all other services have started, waiting for the a button connected to GPIO pins 39 and 40 to be pressed for 5 seconds before starting the poweroff command. These settings can be changed easily in the .service file.

To disable this service run the command:

sudo service rpi_poweroff_button stop

To remove it, run:

sudo systemctl disable rpi_poweroff_button

Implementing without systemd

For systems without systems, the solution to to start gpio_trigger.py during the boot-up process is often adding it to /etc/rc.local. From my GitLab instance, install gpio_trigger.py in /usr/local/sbin/ and make it executable. Next add the following to /etc/rc.local:

if [ -x /usr/local/sbin/gpio_trigger.py ]; then
  /usr/local/sbin/gpio_trigger.py --daemon --hold 5000 poweroff
  echo enabled the shutdown button
else
  echo unable to enable the shutdown button
fi

Again using long command options as they are more clear later and added --hold 5000 again to require the power-off button to be pressed for at least 5 seconds before starting the shutdown.

Monitoring Pi3 in my 19″ rack – now with power-off button

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.