Commit 89436aa586d4c8341b076d221911670624743737

Authored by Frederik Lindenaar
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%)
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(&#39;-r&#39;, &#39;--edge-rising&#39;, action=&#39;store_const&#39;, dest=&#39;edge&#39;, @@ -50,22 +54,22 @@ pgroup.add_argument(&#39;-r&#39;, &#39;--edge-rising&#39;, action=&#39;store_const&#39;, dest=&#39;edge&#39;,
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