The last couple of weeks I have been making progress and posts on my RetroPie build. I’m putting a Raspberry Pi inside of an actual SNES (well Super Famicom). Part 1 covered the schematic for a Soft Power Controller. In Part 2 I broke down the RPSPC state machine. This 3rd and final post of the series is a Raspberry Pi startup script tutorial. It covers how to make scripts run at startup and shutdown. When I started researching how to make Raspbian run a script at startup and shutdown, I found a ton of links and questions asking for help. None of them helpful. Why? Because they were wrong. At least, they are now.

/etc/rc.d doesn’t matter!

It turns out, Raspbian Jessie does not use SysV for init (anymore). So it does not matter what you scripts you put in /etc/rc.d. Pretty simple but missed by many! Here is a correct Raspberry Pi Startup Script Tutorial.

The Key is systemd

Once I started researching how to make systemd do what I wanted, new problems emerged. The syntax for systemd is not as straightforward as I first thought. Thanks to readers, I was pointed towards the RedHat systemd manual. After reviewing it, I was able to create a service that runs at startup and shutdown. In the end, I was unable to prevent this process from running during reboot. There seem to be some more layers to make sure systemd knows the difference. In the end, I decided it was not necessary to avoid the reboot.

systemd configuration

There are two systemd services necessary for my RetroPie controller to work. The first service for the Raspberry Pi startup script tutorial runs a script (or program) at startup and shutdown. The next service runs in the background watching a GPIO pin.

Raspberry Pi Startup Script Tutorial: powercontrol-com.service

Here is the first systemd configuration file.

# Put this file in /etc/systemd/system
# then run sudo systemctl enable powercontrol-coms.service 
# on startup, GPIO signal will go high
# on shutdown/reboot, GPIO signal will go low

[Unit]
Description=Retropie Pi Power Controller
Before=network.target
After=umount.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/c-start
ExecStop=/usr/local/bin/c-stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
I called mine powercontrol-coms.service (click for GitHub version). Place it in the /etc/systemd/system directory. Once it is there run: sudo systemctl enable powercontrol-coms.service. I found you need to reboot before it works both during a startup and shutdown. Here’s how the file works, as I understand it.

[Unit]


[Unit]
Description=Retropie Pi Power Controller
Before=network.target
After=umount.target
The “Description” is just that, a text string to explain what this service does for the system. “Before” tells what unit to activate this service before. In other words, it defines a dependency. I picked network seems pretty early in the boot process. “After” is what unit must run before this service is called. In this case, I wanted to know when the filesystem had been unmounted. I knew it was safe to power down at that point.
Fun fact: The process should not be called ‘unmount.’ It took me a little bit to understand what it does. I mean, how could anything run after the root file system was unmounted? Well, it is because it is not. Instead, systemd (and even SysV) remounts the filesystem as read-only! (Totally makes sense, right?)

[Service]


[Service]
Type=oneshot
ExecStart=/usr/local/bin/c-start
ExecStop=/usr/local/bin/c-stop
RemainAfterExit=yes
This section took me some time to fully understand. The documentation is a bit more technical than necessary. Moreover, what I found is that most posts online do not understand it either. Quite a few people would say “try something like this” but not able to explain how it works. I’m going to try to explain. The “Type” says what kind of process are you about to call. The most common is “oneshot” with “simple” being another option. The “oneshot” type is popular because it means the process runs, exits, then other units can continue. My understanding is that if you use simple, other units might start at the same time. The “ExecStart” parameter is what will run at startup, while the “ExecStop” parameter is what will run during shutdown (or reboot). “RemainAfterExit,” in this case, doesn’t seem to matter.

[Install]


[Install]
WantedBy=multi-user.target
The “WantedBy” create a weak dependency. However, multi-user.target is the most common target when booting with or without a GUI. And that’s it. Enable that service and whatever you define in ExecStart and ExecStop runs at startup and then shutdown. Easy. (If you only want one or the other, replace the opposite Exec with /bin/true.)

Other service: shutdown-gpio.service

I will not go through this service line-by-line. Its function is to launch the background program that monitors the GPIO pin. I tried for a bit to combine the two, but could never get the configuration right. I found it was better to split them apart.

# Put this file in /etc/systemd/system
# then run sudo systemctl enable shutdown-gpio.service
# on startup this will call /usr/local/sbin/c-watch and
# check once per second if the controller gave us the powerdown
# signal. Then forces a reboot.

[Unit]
Description=Watch GPIO pin for when to shutdown
Before=network.target

[Service]
Type=simple
ExecStart=/usr/local/sbin/c-watch
ExecStop=/bin/true
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
As the instructions say, place the file in the same place as the other systemd service. Then run sudo systemctl enable shutdown-gpio.service to activate it. In the case of the RetroPie, I have two simple compiled programs that toggle GPIO pin on the Raspberry Pi.

Shutdown Raspberry Pi with GPIO Pin

Over on the RPSPC GitHub repository, I have already written up instructions on how to compile c-start, c-stop, and c-watch. I’m not going go into detail on this post because I might cover wiringPi later in the future. However, I did want to show the code I’m using to detect an external signal to start the shutdown.

C-watch: Raspberry Pi C GPIO Program

</pre>
<pre>#include <wiringPi.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/reboot.h>

int ioPin = 7; // GPIO04, physical pin 7

int main (void) {
	wiringPiSetup ();
	pinMode (ioPin, INPUT);
	printf("c-watch v0 is now running.\n");
	while(1) {
		pullUpDnControl(ioPin, PUD_DOWN);
		if (!digitalRead(ioPin)) {
			printf("LOW\n");
			break;
		}
		delay(1000);
	}
	printf("Got signal, starting shutdown.\n");
	// Would like to change to dbus at some point
	system("shutdown -P now");
	delay(500);
	return 0;
}</pre>
<pre>
I won’t cover this as a Line-by-Line tutorial. The basic concept is that wiringPi is an Arduino-like library for the Raspberry Pi. This library lets you write relatively simple C programs to control the Pi’s GPIO pins. In this case, I set a particular pin to INPUT and then read it in a loop until it toggles. Calls like delay() make use of sleep functions available in Linux, so a program like this uses almost no CPU time. WiringPi is a brilliant library if you want a low-overhead way to control the Raspberry Pi GPIO pins.

Conclusionrpspc oshpark render

As of this post, OSHPark is making my boards. In a couple of weeks, I will know if the hat version of my RetroPie soft power controller works. This Raspberry Pi startup script tutorial already works on my prototype. But I’d like to get to a final version. As I tweak the board design, I will keep the RPSPC Repository updated. Now it’s on to finishing the physical part of the build.
Author

Fan of making things beep, blink and fly. Created AddOhms. Stream on Twitch. Video Host on element14 Presents and writing for Hackster.IO. Call sign KN6FGY.

6 Comments

  1. I’ve got the following in my .service file and for some reason the pi will at times freeze up on shutdown or restart with the message “a stop job is running for dhcpcd on all interfaces” Im not sure what part of my service file is causing this. Or maybe something thats part of the python file that it eventually kicks off.

    [Unit]
    Description=Turns off LED’s on shutdown.

    [Service]
    Type=oneshot
    RemainAfterExit=true
    ExecStart=/bin/true
    ExecStop=/home/pi/scripts/shutoff.sh

    [Install]
    WantedBy=multi-user.target

    • When I did my scripts, I found I had to put them in public directories. Keeping them in a user’s home directory caused me problems. That’s why I moved things to /usr/local/bin. Also, you’re missing the Before/After in Unit, so I am not sure where/when systemctl is going to run your script.

      • Thanks James!!

        I moved the shell script and added the before/after bit and it seems to have resolved the issue.

        Really appreciate your time.

        Bill

  2. Slight typo: ‘the “ExecStart” parameter is what will run during shutdown‘. Should be ExecStop

  3. This is so interesting; I find it hard to believe that there is no cut and dry way to reset a pi. I will keep this tutorial in mind as I am sure I will need to use something like this in the future.

Write A Comment

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