Raspberry PI shutdown button

During my adventures into over clocking my raspberry pi I had a number of SD cards become corrupt. I read a lot of forum posts asking how people had shut down their devices before they got the corrupt card and this got me a little frustrated.

I tend to run my pis headless, unless I’m getting a new one started. They are fantastic for this job as you can hide one away almost anywhere you have an Ethernet connection. Now what gets me frustrated is this shutting headless pis down safely. I know I should SSH in and issue a halt command but to put it bluntly I’m lazy and when I want that electrical socket for five minutes the last thing I want to be doing is finding a device I can ssh with. The rest of this post is my solution to this laziness, a shut down button for the pi.

Reading the input

I started with the following guide on how to access the GPIO using c.

https://sites.google.com/site/semilleroadt/raspberry-pi-tutorials/gpio

I wanted to use c as I knew I would eventually want to create a daemon to monitor for a button press. The above guide led me to the bcm2835 library which contained an example of how to read the status of an GPIO pin. I used the instructions on this guide to install the bcm2835 library.

I set pin 11 up as a input pin, and applied an internal pull up just like the example does. The pull up will make the status a 1 unless the pin has been shorted to ground when it will become a 0. I chose pin 11 as it was next to a ground pin (pin 9) on the GPIO header so I could test just by shorting pins 11 and 9 with a screwdriver.

The following code shows the setup process and my loop for reading the input pin:


if (!bcm2835_init())
  return 1;

// Set RPI pin to be an input
bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT);

// with a pullup
bcm2835_gpio_set_pud(PIN, BCM2835_GPIO_PUD_UP);

// enter checking loop
while (1)
{
  // Read pin status
  uint8_t value = bcm2835_gpio_lev(PIN);
  if (value == 0)
  {
    printf ("reset button has been pressed \n");
  }

  // wait 1s
  delay(1000);
}

Starting the shutdown

This bit is quite easy, the only catch is we have to make sure the application is running as root.

system ("halt");

This does the trick quite nicely

Starting a deamon

I can remember trying to write a daemon once before but I remember scrapping the project quite early on. The next two links where very helpful in getting a crude daemon written:

http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html
http://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux

The one thing that took me a while to get my head around where the multiple outputs from the fork() command. fork() is the key to starting a daemon and what it does make a copy of all the necessary program elements (mainly the stack) and starts a new process with this copy pre-loaded. At this point you have two copies of identical code running with one difference, the output return value from fork(). The table below explains what they mean:

fork() return value meaning
0 The fork was successful and we are executing in the new process
< 0 The fork process was unsuccessful and we are executing as the original process
> 0 The fork was successful and we are executing as the original process

This is why you normally see three if tests after a fork. One to check we have forked and to do the work of the new “forked process”. One to check the fork happened and to safely finish the current process to the fork can run in the background . The final test is to detect a failed fork. The tests probably wont appear in that order but this one


pID = fork();
if (pID == 0)
{
  // we have forked and are executing as the new process
  // what will follow will probably be an infinite loop
}
else if (pID < 0)
{
  // the fork failed and we are still the old process
}
else
{
  // the fork was successful and we are still the old process
  // clean up and finish this process leaving the
  // forked process to do the work
}

Creating a build process

I have put this towards the end of the post but in reality it was one of the first tasks I did. I used to hate Makefiles, I found them incomprehensible piles of gibberish (and sometime I still do!). Just recently I have found myself being converted, it has something to do with my whole build process being automated and consistent.

A very simple make file needs 7 lines, here are my 7:

default: all

all:
 gcc -o shutdown-btn -l rt main.c -l bcm2835

clean:
 rm shutdown-btn

The default line makes sure all is called when nothing else is specified after make. The “all:” section does the building of the application. I only had one file so this was simple.

The clean section removes any files that the other processes have created.

The following link was very helpful when it came to Makefiles:

http://stackoverflow.com/questions/1484817/how-do-i-make-a-simple-makefile-gcc-unix

Installing

Installing this application was the part that took the most of my time. I knew I wanted to get the application to run at start up but I also wanted to do this as close to the right way as I possible could.

The first step was to copy the built program to /sbin . Once there you could call it like you can any other application, by just typing the name.

The next step was to write a script that would live in /etc/init.d and could be called at boot. I used the switch-cpu-govner <<find right file name>> script as a guide as that seemed to be one of the last things the boot process started and so I decided it would be a good time for my daemon to start.

I made a copy of that script and modified it for my new daemon. I had to remove a number of lines and just put a call to /sbin/shutdown-btn in the section relating to start. I kept all the boot dependency the same and only modified the name and description headers.

The commands for installing this script was as follows:

cp shutdown-btn-start /etc/init.d/shutdown-btn
sudo update-rc.d shutdown-btn defaults
sudo update-rc.d shutdown-btn enable

After running these commands you have a start script in the right place with all the correct symbolic links that are required to make it be executed at startup.

the following page helped me out alot with figuring this part out

http://www.debian-administration.org/articles/28

The finished product

The daemon worked a treat! I can now turn off my pi safely by just holding the button down for five seconds. i can even trust other people to do it knowing my file system should be all safe and sound afterwards. I have put an archive of the project on this post.

the full source code can be downloaded here:
https://sourceforge.net/projects/raspberrypishutdownbutton/files/

I also created a small bread board to sit on top of the GPIO headder. I found I could line up a small button with one of the holes in the case I have so that you could depress it with a pen, or other small tool.

wpid-wp-1388242467275.jpg

Leave a comment