- added README.md

- added systemd service descriptors
- new features in gpio_trigger.py:
  - support waiting for a hold period (-H)
  - added option to ignore command's result code (-i)
  - added variable subtitution in the arguments (%PIN% and %STATE%)
This commit is contained in:
2019-10-23 22:05:48 +02:00
parent e6e9828d19
commit 89436aa586
6 changed files with 286 additions and 12 deletions

110
README.md Normal file
View File

@@ -0,0 +1,110 @@
Raspberry Pi Scripts
====================
This repository contains my collection of scripts and other snippets for the
[Raspberry Pi](https://www.raspberrypi.org). I am publishing these in case they
are of any benefit to other enthusiasts. Use them freely and please let me know
in case you encounter any issues or require changes.
The latest versions, documentation and bugtracker available on my
[GitLab instance](https://gitlab.lindenaar.net/scripts/raspberrypi)
Copyright (c) 2019 Frederik Lindenaar. free for distribution under
the GNU General Public License, see [below](#license)
Contents
========
This repository contains the following scripts:
* [gpio_trigger.py](#gpio_trigger)
is s script to execute a command when a GPIO input pin changes
* [rpi_no_hdmi.service](#services)
is a systemd service to disable the Raspberry Pi's HDMI port at boot
* [rpi_no_usb.service](#services)
is a systemd service to disable the Raspberry Pi's USB bus at boot
* [rpi_no_wifi.service](#services)
is a systemd service to disable on-board WiFi on Raspberry Pi at boot
* [rpi_poweroff_button.service](#services)
is a systemd service to support a power-off button using [gpio_trigger.py](#gpio_trigger)
<a name=gpio_trigger>gpio_trigger.py</a>
----------------------------------------
This script was initially written to add a power-off button to a Raspberry Pi,
[see this blog post](https://frederik.lindenaar.nl/2019/10/23/raspberry-pi-power-off-button.html)
for the rationale behind it and how to construct and connect a physical button.
The script itself is a generic solution to monitor a GPIO pin and executes a
command when the input signal on a pin changes. It can run as interactively as
well as in the background and executes a command once or continuously upon any
change or specific transition (e.g. HIGH to LOW).
The script is written in Python 2 and uses the `RPi.GPIO` library as both are
installed by default on most distributions so should just work. Please note that
by default the script should be started as root to gain access to the GPIO port.
To implement a simple power-off button, install the script in `/usr/local/sbin`,
connect an NC switch (i.e. one that connects when pressed) between pin 39 (GND)
and pin 40 of the Raspberry Pi and add:
~~~
if [ -x /usr/local/sbin/gpio_trigger.py ]; then
/usr/local/sbin/gpio_trigger.py -D -H 5000 poweroff
fi
~~~
to the file `/etc/rc.local`. This will start the script in the background (`-D`)
to wait for pin 40 (default pin) to be connected to ground for 5000ms (`-H`) and
then run the command `poweroff` to shutdown the Raspberry Pi.
Please refer to the output of `gpio_trigger.py -h` for the options supported and
defaults used when no option is specified.
<a name=services>Systemd services</a>
-------------------------------------
The repository contains a number of `.service` files, which are systemd service
descriptions to control specific on-board features of the Raspberry Pi (e.g. to
disable unused ports). Their purpose should be pretty clear from their name (and
comments they contain). The rationale of the initial scripts is covered in this
[blog post](https://frederik.lindenaar.nl/2018/05/11/raspberry-pi-power-saving-disable-hdmi-port-and-others-the-systemd-way.html).
In general, to install these copy them to the directory `/etc/systemd/system/`
To manually disable the port, 'start' the 'service' with:
~~~
service <<filename without .service>> start
~~~
To manually enable the port again, 'stop' the 'service' with:
~~~
service <<filename without .service>> stop
~~~
To enable starting during system boot (to disable the port) run:
~~~
systemctl enable service <<filename without .service>>
~~~
To disable starting during system boot (to no longer disable the port) run:
~~~
systemctl disable service <<filename without .service>>
~~~
<a name="license">License</a>
-----------------------------
These scripts, documentation & configration examples are free software: you can
redistribute and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
This script, documenatation and configuration examples are distributed in the
hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, download it from <http://www.gnu.org/licenses/>.

View File

@@ -1,7 +1,7 @@
#! /usr/bin/env python #! /usr/bin/env python
# #
# gpio_trigger.py - execute a command when a GPIO pin is triggered (up or down) # gpio_trigger.py - execute a command when a GPIO input pin changes
# #
# Version 1.0, latest version, documentation and bugtracker available at: # Version 1.0, latest version, documentation and bugtracker available at:
# https://gitlab.lindenaar.net/scripts/raspberrypi # https://gitlab.lindenaar.net/scripts/raspberrypi
@@ -24,8 +24,12 @@ from os import fork, system
from argparse import ArgumentParser from argparse import ArgumentParser
from RPi import GPIO from RPi import GPIO
parser = ArgumentParser(description='Run command when a Raspberry Pi pin is' parser = ArgumentParser(
' high or low (e.g. a button is pressed)') description='Run command when a Raspberry Pi pin changes to high or low.',
epilog='(*) use \'--\' to stop argument parsing, arguments can contain the '
'following variables: %PIN%=pin number, %STATE%=value (HIGH or LOW)')
parser.add_argument('-D', '--daemon', action='store_true',
help='run in background (as daemon)')
pgroup = parser.add_mutually_exclusive_group(required=False) pgroup = parser.add_mutually_exclusive_group(required=False)
pgroup.add_argument('-P', '--pcb', action='store_const', dest='mode', pgroup.add_argument('-P', '--pcb', action='store_const', dest='mode',
const=GPIO.BOARD, default=GPIO.BOARD, const=GPIO.BOARD, default=GPIO.BOARD,
@@ -50,22 +54,22 @@ pgroup.add_argument('-r', '--edge-rising', action='store_const', dest='edge',
const=GPIO.RISING, help='respond to pin going up') const=GPIO.RISING, help='respond to pin going up')
pgroup.add_argument('-a', '--edge-any', action='store_const', const=GPIO.BOTH, pgroup.add_argument('-a', '--edge-any', action='store_const', const=GPIO.BOTH,
dest='edge', help='respond to any pin change') dest='edge', help='respond to any pin change')
parser.add_argument('-i', '--ignore-result', action='store_true', default=False,
help='ignore command result code (default: exit if <> 0)')
parser.add_argument('-b', '--debounce', type=int, default=200, parser.add_argument('-b', '--debounce', type=int, default=200,
help='debounce period (in milliseconds, default=200)') help='debounce period (in milliseconds, default=200)')
parser.add_argument('-H', '--hold', type=int, default=-1,
help='optional hold time, pin must be held stable for the '
'specified time in milliseconds to trigger cmd')
parser.add_argument('-t', '--timeout', type=int, default=-1, parser.add_argument('-t', '--timeout', type=int, default=-1,
help='optional timeout (in milliseconds) to wait') help='optional timeout (in milliseconds) to wait')
parser.add_argument('-i', '--ignore-result', action='store_true', default=False,
help='ignore command result code (default: exit if <> 0)')
pgroup = parser.add_mutually_exclusive_group(required=False) pgroup = parser.add_mutually_exclusive_group(required=False)
pgroup.add_argument('-c', '--continuous', default=False, action='store_true', pgroup.add_argument('-c', '--continuous', default=False, action='store_true',
help='continously monitor GPIO pin and run cmd upon change') help='continously monitor GPIO pin and run cmd upon change')
pgroup.add_argument('-o', '--once', action='store_false', dest='continuous', pgroup.add_argument('-o', '--once', action='store_false', dest='continuous',
help='monitor pin and run cmd once, then exit (default)') help='monitor pin and run cmd once, then exit (default)')
parser.add_argument('-D', '--daemon', action='store_true',
help='run in background (as daemon)')
parser.add_argument('cmd', help='command to execute when pin goes low') parser.add_argument('cmd', help='command to execute when pin goes low')
parser.add_argument('arg', nargs='*', help='argument(s) for the command, use --' parser.add_argument('arg', nargs='*', help='argument(s) for the command (*)')
' before first argument to stop parsing parameters')
args = parser.parse_args() # parse command line args = parser.parse_args() # parse command line
GPIO.setmode(args.mode) # set GPIO number mode GPIO.setmode(args.mode) # set GPIO number mode
@@ -76,12 +80,21 @@ if args.daemon and fork() != 0: # Fork for daemon mode
ret = 0 ret = 0
while args.ignore_result or ret == 0: while args.ignore_result or ret == 0:
# wait for GPIO pin to be changed in the right direction
if GPIO.wait_for_edge(args.pin, args.edge, bouncetime=args.debounce, if GPIO.wait_for_edge(args.pin, args.edge, bouncetime=args.debounce,
timeout=args.timeout): timeout=args.timeout):
ret = system(' '.join([ args.cmd ] + args.arg)) # run the command state = GPIO.input(args.pin)
if not args.continuous: # exit if running once # pin changed, if required check if it is stays the same long enough
break if (args.hold < 0) or (
GPIO.wait_for_edge(args.pin, GPIO.BOTH, bouncetime=args.debounce,
timeout=args.hold - args.debounce) is None):
ret = system(' '.join([ args.cmd ] + [
[ 'LOW', 'HIGH' ][state] if arg == '%STATE%' else
str(args.pin) if arg == '%PIN%' else arg for arg in args.arg]))
if not args.continuous: # exit if running once
break
else: # exit if timeout else: # exit if timeout
ret = -1
break break
GPIO.cleanup(args.pin) # cleanup GPIO GPIO.cleanup(args.pin) # cleanup GPIO

37
rpi_no_hdmi.service Normal file
View File

@@ -0,0 +1,37 @@
#
# rpi_no_hdmi.service Systemd service to disable the Raspberry Pi's HDMI port
#
# Version 1.0, latest version, documentation and bugtracker available at:
# https://gitlab.lindenaar.net/scripts/raspberrypi
#
# Copyright (c) 2019 Frederik Lindenaar
#
# This script is free software: you can redistribute and/or modify it under the
# terms of version 3 of the GNU General Public License as published by the Free
# Software Foundation, or (at your option) any later version of the license.
#
# This script is distributed in the hope that it will be useful but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, visit <http://www.gnu.org/licenses/> to download it.
#
# To install copy to /etc/systemd/system/rpi_no_hdmi.service
# enable it to start during boot with: systemctl enable rpi_no_hdmi
# temporarily enable with: service rpi_no_hdmi start
# temporarily disable with: service rpi_no_hdmi stop
# disable starting during boot with: systemctl disable rpi_no_hdmi
[Unit]
Description=Disable Raspberry Pi HDMI port
[Service]
Type=oneshot
ExecStart=/opt/vc/bin/tvservice -o
ExecStop=/opt/vc/bin/tvservice -p
RemainAfterExit=yes
[Install]
WantedBy=default.target

37
rpi_no_usb.service Normal file
View File

@@ -0,0 +1,37 @@
#
# rpi_no_usb.service Systemd service to disable the Raspberry Pi's USB bus
#
# Version 1.0, latest version, documentation and bugtracker available at:
# https://gitlab.lindenaar.net/scripts/raspberrypi
#
# Copyright (c) 2019 Frederik Lindenaar
#
# This script is free software: you can redistribute and/or modify it under the
# terms of version 3 of the GNU General Public License as published by the Free
# Software Foundation, or (at your option) any later version of the license.
#
# This script is distributed in the hope that it will be useful but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, visit <http://www.gnu.org/licenses/> to download it.
#
# To install copy to /etc/systemd/system/rpi_no_usb.service
# enable it to start during boot with: systemctl enable rpi_no_usb
# temporarily enable with: service rpi_no_usb start
# temporarily disable with: service rpi_no_usb stop
# disable starting during boot with: systemctl disable rpi_no_usb
[Unit]
Description=Disable Raspberry Pi USB bus
[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo 0x0 > /sys/devices/platform/soc/3f980000.usb/buspower"
ExecStop=/bin/sh -c "echo 0x1 > /sys/devices/platform/soc/3f980000.usb/buspower"
RemainAfterExit=yes
[Install]
WantedBy=default.target

37
rpi_no_wifi.service Normal file
View File

@@ -0,0 +1,37 @@
#
# rpi_no_wifi.service Systemd service to disable WiFi on Raspberry Pi 3B/ZeroW
#
# Version 1.0, latest version, documentation and bugtracker available at:
# https://gitlab.lindenaar.net/scripts/raspberrypi
#
# Copyright (c) 2019 Frederik Lindenaar
#
# This script is free software: you can redistribute and/or modify it under the
# terms of version 3 of the GNU General Public License as published by the Free
# Software Foundation, or (at your option) any later version of the license.
#
# This script is distributed in the hope that it will be useful but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, visit <http://www.gnu.org/licenses/> to download it.
#
# To install copy to /etc/systemd/system/rpi_no_wifi.service
# enable it to start during boot with: systemctl enable rpi_no_wifi
# temporarily enable with: service rpi_no_wifi start
# temporarily disable with: service rpi_no_wifi stop
# disable starting during boot with: systemctl disable rpi_no_wifi
[Unit]
Description=Disable Raspberry Pi 3B/ZeroW WiFi interface
[Service]
Type=oneshot
ExecStart=/sbin/ifdown wlan0
ExecStop=/sbin/ifup wlan0
RemainAfterExit=yes
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,40 @@
#
# rpi_poweroff_button.service Systemd service to implement a poweroff button
#
# Version 1.0, latest version, documentation and bugtracker available at:
# https://gitlab.lindenaar.net/scripts/raspberrypi
#
# Copyright (c) 2019 Frederik Lindenaar
#
# This script is free software: you can redistribute and/or modify it under the
# terms of version 3 of the GNU General Public License as published by the Free
# Software Foundation, or (at your option) any later version of the license.
#
# This script is distributed in the hope that it will be useful but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, visit <http://www.gnu.org/licenses/> to download it.
#
# Please note this requires gpio_trigger.py installed in /usr/local/sbin, see:
# https://frederik.lindenaar.nl/2019/10/23/raspberry-pi-power-off-button.html
# To install copy to /etc/systemd/system/rpi_poweroff_button.service
# enable it to start during boot with: systemctl enable rpi_poweroff_button
# temporarily enable with: service rpi_poweroff_button start
# temporarily disable with: service rpi_poweroff_button stop
# disable starting during boot with: systemctl disable rpi_poweroff_button
[Unit]
Description=Power-off button on GPIO
[Service]
Type=idle
ExecStart=/usr/local/sbin/gpio_trigger.py --hold 5000 poweroff
Restart=always
RestartSec=30
[Install]
WantedBy=default.target