Commit 89436aa586d4c8341b076d221911670624743737
1 parent
e6e9828d
- 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%)
Showing
6 changed files
with
286 additions
and
12 deletions
README.md
0 → 100644
1 | +Raspberry Pi Scripts | ||
2 | +==================== | ||
3 | +This repository contains my collection of scripts and other snippets for the | ||
4 | +[Raspberry Pi](https://www.raspberrypi.org). I am publishing these in case they | ||
5 | +are of any benefit to other enthusiasts. Use them freely and please let me know | ||
6 | +in case you encounter any issues or require changes. | ||
7 | + | ||
8 | +The latest versions, documentation and bugtracker available on my | ||
9 | +[GitLab instance](https://gitlab.lindenaar.net/scripts/raspberrypi) | ||
10 | + | ||
11 | +Copyright (c) 2019 Frederik Lindenaar. free for distribution under | ||
12 | +the GNU General Public License, see [below](#license) | ||
13 | + | ||
14 | +Contents | ||
15 | +======== | ||
16 | +This repository contains the following scripts: | ||
17 | + * [gpio_trigger.py](#gpio_trigger) | ||
18 | + is s script to execute a command when a GPIO input pin changes | ||
19 | + * [rpi_no_hdmi.service](#services) | ||
20 | + is a systemd service to disable the Raspberry Pi's HDMI port at boot | ||
21 | + * [rpi_no_usb.service](#services) | ||
22 | + is a systemd service to disable the Raspberry Pi's USB bus at boot | ||
23 | + * [rpi_no_wifi.service](#services) | ||
24 | + is a systemd service to disable on-board WiFi on Raspberry Pi at boot | ||
25 | + * [rpi_poweroff_button.service](#services) | ||
26 | + is a systemd service to support a power-off button using [gpio_trigger.py](#gpio_trigger) | ||
27 | + | ||
28 | + | ||
29 | +<a name=gpio_trigger>gpio_trigger.py</a> | ||
30 | +---------------------------------------- | ||
31 | +This script was initially written to add a power-off button to a Raspberry Pi, | ||
32 | +[see this blog post](https://frederik.lindenaar.nl/2019/10/23/raspberry-pi-power-off-button.html) | ||
33 | +for the rationale behind it and how to construct and connect a physical button. | ||
34 | + | ||
35 | +The script itself is a generic solution to monitor a GPIO pin and executes a | ||
36 | +command when the input signal on a pin changes. It can run as interactively as | ||
37 | +well as in the background and executes a command once or continuously upon any | ||
38 | +change or specific transition (e.g. HIGH to LOW). | ||
39 | + | ||
40 | +The script is written in Python 2 and uses the `RPi.GPIO` library as both are | ||
41 | +installed by default on most distributions so should just work. Please note that | ||
42 | +by default the script should be started as root to gain access to the GPIO port. | ||
43 | + | ||
44 | +To implement a simple power-off button, install the script in `/usr/local/sbin`, | ||
45 | +connect an NC switch (i.e. one that connects when pressed) between pin 39 (GND) | ||
46 | +and pin 40 of the Raspberry Pi and add: | ||
47 | + | ||
48 | +~~~ | ||
49 | +if [ -x /usr/local/sbin/gpio_trigger.py ]; then | ||
50 | + /usr/local/sbin/gpio_trigger.py -D -H 5000 poweroff | ||
51 | +fi | ||
52 | +~~~ | ||
53 | + | ||
54 | +to the file `/etc/rc.local`. This will start the script in the background (`-D`) | ||
55 | +to wait for pin 40 (default pin) to be connected to ground for 5000ms (`-H`) and | ||
56 | +then run the command `poweroff` to shutdown the Raspberry Pi. | ||
57 | + | ||
58 | +Please refer to the output of `gpio_trigger.py -h` for the options supported and | ||
59 | +defaults used when no option is specified. | ||
60 | + | ||
61 | + | ||
62 | +<a name=services>Systemd services</a> | ||
63 | +------------------------------------- | ||
64 | +The repository contains a number of `.service` files, which are systemd service | ||
65 | +descriptions to control specific on-board features of the Raspberry Pi (e.g. to | ||
66 | +disable unused ports). Their purpose should be pretty clear from their name (and | ||
67 | +comments they contain). The rationale of the initial scripts is covered in this | ||
68 | +[blog post](https://frederik.lindenaar.nl/2018/05/11/raspberry-pi-power-saving-disable-hdmi-port-and-others-the-systemd-way.html). | ||
69 | + | ||
70 | +In general, to install these copy them to the directory `/etc/systemd/system/` | ||
71 | + | ||
72 | +To manually disable the port, 'start' the 'service' with: | ||
73 | + | ||
74 | +~~~ | ||
75 | +service <<filename without .service>> start | ||
76 | +~~~ | ||
77 | + | ||
78 | +To manually enable the port again, 'stop' the 'service' with: | ||
79 | + | ||
80 | +~~~ | ||
81 | +service <<filename without .service>> stop | ||
82 | +~~~ | ||
83 | + | ||
84 | +To enable starting during system boot (to disable the port) run: | ||
85 | + | ||
86 | +~~~ | ||
87 | +systemctl enable service <<filename without .service>> | ||
88 | +~~~ | ||
89 | + | ||
90 | +To disable starting during system boot (to no longer disable the port) run: | ||
91 | + | ||
92 | +~~~ | ||
93 | +systemctl disable service <<filename without .service>> | ||
94 | +~~~ | ||
95 | + | ||
96 | + | ||
97 | +<a name="license">License</a> | ||
98 | +----------------------------- | ||
99 | +These scripts, documentation & configration examples are free software: you can | ||
100 | +redistribute and/or modify it under the terms of the GNU General Public License | ||
101 | +as published by the Free Software Foundation, either version 3 of the License, | ||
102 | +or (at your option) any later version. | ||
103 | + | ||
104 | +This script, documenatation and configuration examples are distributed in the | ||
105 | +hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied | ||
106 | +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
107 | +General Public License for more details. | ||
108 | + | ||
109 | +You should have received a copy of the GNU General Public License along with | ||
110 | +this program. If not, download it from <http://www.gnu.org/licenses/>. | ||
0 | \ No newline at end of file | 111 | \ No newline at end of file |
gpio_trigger.py
1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
2 | 2 | ||
3 | # | 3 | # |
4 | -# gpio_trigger.py - execute a command when a GPIO pin is triggered (up or down) | 4 | +# gpio_trigger.py - execute a command when a GPIO input pin changes |
5 | # | 5 | # |
6 | # Version 1.0, latest version, documentation and bugtracker available at: | 6 | # Version 1.0, latest version, documentation and bugtracker available at: |
7 | # https://gitlab.lindenaar.net/scripts/raspberrypi | 7 | # https://gitlab.lindenaar.net/scripts/raspberrypi |
@@ -24,8 +24,12 @@ from os import fork, system | @@ -24,8 +24,12 @@ from os import fork, system | ||
24 | from argparse import ArgumentParser | 24 | from argparse import ArgumentParser |
25 | from RPi import GPIO | 25 | from RPi import GPIO |
26 | 26 | ||
27 | -parser = ArgumentParser(description='Run command when a Raspberry Pi pin is' | ||
28 | - ' high or low (e.g. a button is pressed)') | 27 | +parser = ArgumentParser( |
28 | + description='Run command when a Raspberry Pi pin changes to high or low.', | ||
29 | + epilog='(*) use \'--\' to stop argument parsing, arguments can contain the ' | ||
30 | + 'following variables: %PIN%=pin number, %STATE%=value (HIGH or LOW)') | ||
31 | +parser.add_argument('-D', '--daemon', action='store_true', | ||
32 | + help='run in background (as daemon)') | ||
29 | pgroup = parser.add_mutually_exclusive_group(required=False) | 33 | pgroup = parser.add_mutually_exclusive_group(required=False) |
30 | pgroup.add_argument('-P', '--pcb', action='store_const', dest='mode', | 34 | pgroup.add_argument('-P', '--pcb', action='store_const', dest='mode', |
31 | const=GPIO.BOARD, default=GPIO.BOARD, | 35 | const=GPIO.BOARD, default=GPIO.BOARD, |
@@ -50,22 +54,22 @@ pgroup.add_argument('-r', '--edge-rising', action='store_const', dest='edge', | @@ -50,22 +54,22 @@ pgroup.add_argument('-r', '--edge-rising', action='store_const', dest='edge', | ||
50 | const=GPIO.RISING, help='respond to pin going up') | 54 | const=GPIO.RISING, help='respond to pin going up') |
51 | pgroup.add_argument('-a', '--edge-any', action='store_const', const=GPIO.BOTH, | 55 | pgroup.add_argument('-a', '--edge-any', action='store_const', const=GPIO.BOTH, |
52 | dest='edge', help='respond to any pin change') | 56 | dest='edge', help='respond to any pin change') |
53 | -parser.add_argument('-i', '--ignore-result', action='store_true', default=False, | ||
54 | - help='ignore command result code (default: exit if <> 0)') | ||
55 | parser.add_argument('-b', '--debounce', type=int, default=200, | 57 | parser.add_argument('-b', '--debounce', type=int, default=200, |
56 | help='debounce period (in milliseconds, default=200)') | 58 | help='debounce period (in milliseconds, default=200)') |
59 | +parser.add_argument('-H', '--hold', type=int, default=-1, | ||
60 | + help='optional hold time, pin must be held stable for the ' | ||
61 | + 'specified time in milliseconds to trigger cmd') | ||
57 | parser.add_argument('-t', '--timeout', type=int, default=-1, | 62 | parser.add_argument('-t', '--timeout', type=int, default=-1, |
58 | help='optional timeout (in milliseconds) to wait') | 63 | help='optional timeout (in milliseconds) to wait') |
64 | +parser.add_argument('-i', '--ignore-result', action='store_true', default=False, | ||
65 | + help='ignore command result code (default: exit if <> 0)') | ||
59 | pgroup = parser.add_mutually_exclusive_group(required=False) | 66 | pgroup = parser.add_mutually_exclusive_group(required=False) |
60 | pgroup.add_argument('-c', '--continuous', default=False, action='store_true', | 67 | pgroup.add_argument('-c', '--continuous', default=False, action='store_true', |
61 | help='continously monitor GPIO pin and run cmd upon change') | 68 | help='continously monitor GPIO pin and run cmd upon change') |
62 | pgroup.add_argument('-o', '--once', action='store_false', dest='continuous', | 69 | pgroup.add_argument('-o', '--once', action='store_false', dest='continuous', |
63 | help='monitor pin and run cmd once, then exit (default)') | 70 | help='monitor pin and run cmd once, then exit (default)') |
64 | -parser.add_argument('-D', '--daemon', action='store_true', | ||
65 | - help='run in background (as daemon)') | ||
66 | parser.add_argument('cmd', help='command to execute when pin goes low') | 71 | parser.add_argument('cmd', help='command to execute when pin goes low') |
67 | -parser.add_argument('arg', nargs='*', help='argument(s) for the command, use --' | ||
68 | - ' before first argument to stop parsing parameters') | 72 | +parser.add_argument('arg', nargs='*', help='argument(s) for the command (*)') |
69 | 73 | ||
70 | args = parser.parse_args() # parse command line | 74 | args = parser.parse_args() # parse command line |
71 | GPIO.setmode(args.mode) # set GPIO number mode | 75 | GPIO.setmode(args.mode) # set GPIO number mode |
@@ -76,12 +80,21 @@ if args.daemon and fork() != 0: # Fork for daemon mode | @@ -76,12 +80,21 @@ if args.daemon and fork() != 0: # Fork for daemon mode | ||
76 | 80 | ||
77 | ret = 0 | 81 | ret = 0 |
78 | while args.ignore_result or ret == 0: | 82 | while args.ignore_result or ret == 0: |
83 | + # wait for GPIO pin to be changed in the right direction | ||
79 | if GPIO.wait_for_edge(args.pin, args.edge, bouncetime=args.debounce, | 84 | if GPIO.wait_for_edge(args.pin, args.edge, bouncetime=args.debounce, |
80 | timeout=args.timeout): | 85 | timeout=args.timeout): |
81 | - ret = system(' '.join([ args.cmd ] + args.arg)) # run the command | ||
82 | - if not args.continuous: # exit if running once | ||
83 | - break | 86 | + state = GPIO.input(args.pin) |
87 | + # pin changed, if required check if it is stays the same long enough | ||
88 | + if (args.hold < 0) or ( | ||
89 | + GPIO.wait_for_edge(args.pin, GPIO.BOTH, bouncetime=args.debounce, | ||
90 | + timeout=args.hold - args.debounce) is None): | ||
91 | + ret = system(' '.join([ args.cmd ] + [ | ||
92 | + [ 'LOW', 'HIGH' ][state] if arg == '%STATE%' else | ||
93 | + str(args.pin) if arg == '%PIN%' else arg for arg in args.arg])) | ||
94 | + if not args.continuous: # exit if running once | ||
95 | + break | ||
84 | else: # exit if timeout | 96 | else: # exit if timeout |
97 | + ret = -1 | ||
85 | break | 98 | break |
86 | 99 | ||
87 | GPIO.cleanup(args.pin) # cleanup GPIO | 100 | GPIO.cleanup(args.pin) # cleanup GPIO |
rpi_no_hdmi.service
0 → 100644
1 | +# | ||
2 | +# rpi_no_hdmi.service – Systemd service to disable the Raspberry Pi's HDMI port | ||
3 | +# | ||
4 | +# Version 1.0, latest version, documentation and bugtracker available at: | ||
5 | +# https://gitlab.lindenaar.net/scripts/raspberrypi | ||
6 | +# | ||
7 | +# Copyright (c) 2019 Frederik Lindenaar | ||
8 | +# | ||
9 | +# This script is free software: you can redistribute and/or modify it under the | ||
10 | +# terms of version 3 of the GNU General Public License as published by the Free | ||
11 | +# Software Foundation, or (at your option) any later version of the license. | ||
12 | +# | ||
13 | +# This script is distributed in the hope that it will be useful but WITHOUT ANY | ||
14 | +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
15 | +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
16 | +# | ||
17 | +# You should have received a copy of the GNU General Public License along with | ||
18 | +# this program. If not, visit <http://www.gnu.org/licenses/> to download it. | ||
19 | +# | ||
20 | + | ||
21 | +# To install copy to /etc/systemd/system/rpi_no_hdmi.service | ||
22 | +# enable it to start during boot with: systemctl enable rpi_no_hdmi | ||
23 | +# temporarily enable with: service rpi_no_hdmi start | ||
24 | +# temporarily disable with: service rpi_no_hdmi stop | ||
25 | +# disable starting during boot with: systemctl disable rpi_no_hdmi | ||
26 | + | ||
27 | +[Unit] | ||
28 | +Description=Disable Raspberry Pi HDMI port | ||
29 | + | ||
30 | +[Service] | ||
31 | +Type=oneshot | ||
32 | +ExecStart=/opt/vc/bin/tvservice -o | ||
33 | +ExecStop=/opt/vc/bin/tvservice -p | ||
34 | +RemainAfterExit=yes | ||
35 | + | ||
36 | +[Install] | ||
37 | +WantedBy=default.target | ||
0 | \ No newline at end of file | 38 | \ No newline at end of file |
rpi_no_usb.service
0 → 100644
1 | +# | ||
2 | +# rpi_no_usb.service – Systemd service to disable the Raspberry Pi's USB bus | ||
3 | +# | ||
4 | +# Version 1.0, latest version, documentation and bugtracker available at: | ||
5 | +# https://gitlab.lindenaar.net/scripts/raspberrypi | ||
6 | +# | ||
7 | +# Copyright (c) 2019 Frederik Lindenaar | ||
8 | +# | ||
9 | +# This script is free software: you can redistribute and/or modify it under the | ||
10 | +# terms of version 3 of the GNU General Public License as published by the Free | ||
11 | +# Software Foundation, or (at your option) any later version of the license. | ||
12 | +# | ||
13 | +# This script is distributed in the hope that it will be useful but WITHOUT ANY | ||
14 | +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
15 | +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
16 | +# | ||
17 | +# You should have received a copy of the GNU General Public License along with | ||
18 | +# this program. If not, visit <http://www.gnu.org/licenses/> to download it. | ||
19 | +# | ||
20 | + | ||
21 | +# To install copy to /etc/systemd/system/rpi_no_usb.service | ||
22 | +# enable it to start during boot with: systemctl enable rpi_no_usb | ||
23 | +# temporarily enable with: service rpi_no_usb start | ||
24 | +# temporarily disable with: service rpi_no_usb stop | ||
25 | +# disable starting during boot with: systemctl disable rpi_no_usb | ||
26 | + | ||
27 | +[Unit] | ||
28 | +Description=Disable Raspberry Pi USB bus | ||
29 | + | ||
30 | +[Service] | ||
31 | +Type=oneshot | ||
32 | +ExecStart=/bin/sh -c "echo 0x0 > /sys/devices/platform/soc/3f980000.usb/buspower" | ||
33 | +ExecStop=/bin/sh -c "echo 0x1 > /sys/devices/platform/soc/3f980000.usb/buspower" | ||
34 | +RemainAfterExit=yes | ||
35 | + | ||
36 | +[Install] | ||
37 | +WantedBy=default.target | ||
0 | \ No newline at end of file | 38 | \ No newline at end of file |
rpi_no_wifi.service
0 → 100644
1 | +# | ||
2 | +# rpi_no_wifi.service – Systemd service to disable WiFi on Raspberry Pi 3B/ZeroW | ||
3 | +# | ||
4 | +# Version 1.0, latest version, documentation and bugtracker available at: | ||
5 | +# https://gitlab.lindenaar.net/scripts/raspberrypi | ||
6 | +# | ||
7 | +# Copyright (c) 2019 Frederik Lindenaar | ||
8 | +# | ||
9 | +# This script is free software: you can redistribute and/or modify it under the | ||
10 | +# terms of version 3 of the GNU General Public License as published by the Free | ||
11 | +# Software Foundation, or (at your option) any later version of the license. | ||
12 | +# | ||
13 | +# This script is distributed in the hope that it will be useful but WITHOUT ANY | ||
14 | +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
15 | +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
16 | +# | ||
17 | +# You should have received a copy of the GNU General Public License along with | ||
18 | +# this program. If not, visit <http://www.gnu.org/licenses/> to download it. | ||
19 | +# | ||
20 | + | ||
21 | +# To install copy to /etc/systemd/system/rpi_no_wifi.service | ||
22 | +# enable it to start during boot with: systemctl enable rpi_no_wifi | ||
23 | +# temporarily enable with: service rpi_no_wifi start | ||
24 | +# temporarily disable with: service rpi_no_wifi stop | ||
25 | +# disable starting during boot with: systemctl disable rpi_no_wifi | ||
26 | + | ||
27 | +[Unit] | ||
28 | +Description=Disable Raspberry Pi 3B/ZeroW WiFi interface | ||
29 | + | ||
30 | +[Service] | ||
31 | +Type=oneshot | ||
32 | +ExecStart=/sbin/ifdown wlan0 | ||
33 | +ExecStop=/sbin/ifup wlan0 | ||
34 | +RemainAfterExit=yes | ||
35 | + | ||
36 | +[Install] | ||
37 | +WantedBy=default.target | ||
0 | \ No newline at end of file | 38 | \ No newline at end of file |
rpi_poweroff_button.service
0 → 100644
1 | +# | ||
2 | +# rpi_poweroff_button.service – Systemd service to implement a poweroff button | ||
3 | +# | ||
4 | +# Version 1.0, latest version, documentation and bugtracker available at: | ||
5 | +# https://gitlab.lindenaar.net/scripts/raspberrypi | ||
6 | +# | ||
7 | +# Copyright (c) 2019 Frederik Lindenaar | ||
8 | +# | ||
9 | +# This script is free software: you can redistribute and/or modify it under the | ||
10 | +# terms of version 3 of the GNU General Public License as published by the Free | ||
11 | +# Software Foundation, or (at your option) any later version of the license. | ||
12 | +# | ||
13 | +# This script is distributed in the hope that it will be useful but WITHOUT ANY | ||
14 | +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
15 | +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
16 | +# | ||
17 | +# You should have received a copy of the GNU General Public License along with | ||
18 | +# this program. If not, visit <http://www.gnu.org/licenses/> to download it. | ||
19 | +# | ||
20 | + | ||
21 | +# Please note this requires gpio_trigger.py installed in /usr/local/sbin, see: | ||
22 | +# https://frederik.lindenaar.nl/2019/10/23/raspberry-pi-power-off-button.html | ||
23 | + | ||
24 | +# To install copy to /etc/systemd/system/rpi_poweroff_button.service | ||
25 | +# enable it to start during boot with: systemctl enable rpi_poweroff_button | ||
26 | +# temporarily enable with: service rpi_poweroff_button start | ||
27 | +# temporarily disable with: service rpi_poweroff_button stop | ||
28 | +# disable starting during boot with: systemctl disable rpi_poweroff_button | ||
29 | + | ||
30 | +[Unit] | ||
31 | +Description=Power-off button on GPIO | ||
32 | + | ||
33 | +[Service] | ||
34 | +Type=idle | ||
35 | +ExecStart=/usr/local/sbin/gpio_trigger.py --hold 5000 poweroff | ||
36 | +Restart=always | ||
37 | +RestartSec=30 | ||
38 | + | ||
39 | +[Install] | ||
40 | +WantedBy=default.target | ||
0 | \ No newline at end of file | 41 | \ No newline at end of file |