├── .gitignore ├── README.md ├── install.sh ├── install_libreelec.sh ├── scripts ├── libreelec │ ├── autostart.sh │ └── powerblock.sh ├── powerblock └── powerblockservice ├── supplementary ├── powerblockconfig.cfg └── powerblockswitchoff.sh └── uninstall.sh /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/macos,sublimetext,jetbrains,cmake,eclipse 3 | 4 | doc/htmlDoc/ 5 | 6 | ### macOS ### 7 | *.DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | # Thumbnails 14 | ._* 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | 31 | ### SublimeText ### 32 | # cache files for sublime text 33 | *.tmlanguage.cache 34 | *.tmPreferences.cache 35 | *.stTheme.cache 36 | 37 | # workspace files are user-specific 38 | *.sublime-workspace 39 | 40 | # project files should be checked into the repository, unless a significant 41 | # proportion of contributors will probably not be using SublimeText 42 | # *.sublime-project 43 | 44 | # sftp configuration file 45 | sftp-config.json 46 | 47 | # Package control specific files 48 | Package Control.last-run 49 | Package Control.ca-list 50 | Package Control.ca-bundle 51 | Package Control.system-ca-bundle 52 | Package Control.cache/ 53 | Package Control.ca-certs/ 54 | Package Control.merged-ca-bundle 55 | Package Control.user-ca-bundle 56 | oscrypto-ca-bundle.crt 57 | bh_unicode_properties.cache 58 | 59 | # Sublime-github package stores a github token in this file 60 | # https://packagecontrol.io/packages/sublime-github 61 | GitHub.sublime-settings 62 | 63 | 64 | ### JetBrains ### 65 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 66 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 67 | 68 | # User-specific stuff: 69 | .idea/workspace.xml 70 | .idea/tasks.xml 71 | 72 | # Sensitive or high-churn files: 73 | .idea/dataSources/ 74 | .idea/dataSources.ids 75 | .idea/dataSources.xml 76 | .idea/dataSources.local.xml 77 | .idea/sqlDataSources.xml 78 | .idea/dynamic.xml 79 | .idea/uiDesigner.xml 80 | 81 | .idea/* 82 | 83 | # Gradle: 84 | .idea/gradle.xml 85 | .idea/libraries 86 | 87 | # Mongo Explorer plugin: 88 | .idea/mongoSettings.xml 89 | 90 | ## File-based project format: 91 | *.iws 92 | 93 | ## Plugin-specific files: 94 | 95 | # IntelliJ 96 | /out/ 97 | 98 | # mpeltonen/sbt-idea plugin 99 | .idea_modules/ 100 | 101 | # JIRA plugin 102 | atlassian-ide-plugin.xml 103 | 104 | # Crashlytics plugin (for Android Studio and IntelliJ) 105 | com_crashlytics_export_strings.xml 106 | crashlytics.properties 107 | crashlytics-build.properties 108 | fabric.properties 109 | 110 | ### JetBrains Patch ### 111 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 112 | 113 | # *.iml 114 | # modules.xml 115 | # .idea/misc.xml 116 | # *.ipr 117 | 118 | 119 | ### CMake ### 120 | CMakeCache.txt 121 | CMakeFiles 122 | CMakeScripts 123 | Testing 124 | Makefile 125 | cmake_install.cmake 126 | install_manifest.txt 127 | compile_commands.json 128 | CTestTestfile.cmake 129 | cmake-build-debug/ 130 | 131 | 132 | ### Eclipse ### 133 | 134 | .metadata 135 | bin/ 136 | tmp/ 137 | *.tmp 138 | *.bak 139 | *.swp 140 | *~.nib 141 | local.properties 142 | .settings/ 143 | .loadpath 144 | .recommenders 145 | 146 | # Eclipse Core 147 | .project 148 | 149 | # External tool builders 150 | .externalToolBuilders/ 151 | 152 | # Locally stored "Eclipse launch configurations" 153 | *.launch 154 | 155 | # PyDev specific (Python IDE for Eclipse) 156 | *.pydevproject 157 | 158 | # CDT-specific (C/C++ Development Tooling) 159 | .cproject 160 | 161 | # JDT-specific (Eclipse Java Development Tools) 162 | .classpath 163 | 164 | # Java annotation processor (APT) 165 | .factorypath 166 | 167 | # PDT-specific (PHP Development Tools) 168 | .buildpath 169 | 170 | # sbteclipse plugin 171 | .target 172 | 173 | # Tern plugin 174 | .tern-project 175 | 176 | # TeXlipse plugin 177 | .texlipse 178 | 179 | # STS (Spring Tool Suite) 180 | .springBeans 181 | 182 | # Code Recommenders 183 | .recommenders/ 184 | 185 | # End of https://www.gitignore.io/api/macos,sublimetext,jetbrains,cmake,eclipse 186 | 187 | build/ 188 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PowerBlock Driver 2 | ================= 3 | 4 | This is the driver for the petrockblock.com [PowerBlock](https://blog.petrockblock.com/powerblock-raspberry-pi-power-switch/), 5 | which is an extension board for the Raspberry Pi (TM). The driver itself is denoted as _powerblock_ in the following. The 6 | driver provides a service for interacting with the power button signal as well as driving the optionally attached LED. 7 | 8 | :exclamation: The following description is intended to be used in combination with a Raspberry Pi and a 9 | **Linux-based operating system** running on the Raspberry Pi. There is a **driver for Windows 10 IoT Core** that can be 10 | found at https://github.com/petrockblog/PowerBlockWinIoT/releases :exclamation: 11 | 12 | 13 | ## Installation 14 | 15 | There comes an installation script with this reposity that does all the steps described below: `install.sh` This script 16 | compiles the driver, installs the binary and configuration files, and installs the PowerBlock service. 17 | 18 | To run the quick installation, you just need to call this one line from the Raspbian console 19 | 20 | ```bash 21 | wget -O - https://raw.githubusercontent.com/petrockblog/PowerBlock/master/install.sh | sudo bash 22 | ``` 23 | 24 | To uninstall the service you can simply call `sudo ./uninstall.sh` from within the `PowerBlock` directory. 25 | 26 | 27 | ## Configuration 28 | 29 | The configuration file of _powerblock_ is located at ```/etc/powerblockconfig.cfg```. It uses simple .ini file syntax = for setting the the values of its configuration parameters. 30 | 31 | The parameters are explained in detail in the following: 32 | 33 | - ```activated```: Can be set to 34 | + ```1```: Activates the handling of the power switch signals of the PowerBlock. 35 | + ```0```: Deactivates the handling of the power switch signals of the PowerBlock. 36 | - ```statuspin```: Raspberry BCM pin used for status signaling (default: 17) connects to S2 on PowerBlock 37 | - ```shutdownpin```: Raspberry BCM pin used for shutdown signaling (default: 18) connects to S1 on Powerblock 38 | - ```logging```: Enables or disables logging via syslog to /var/log/syslog. The logging is enabled if set to 1. 39 | 40 | ## Logging 41 | 42 | The PowerBlock driver logs certain events in the file `/var/log/syslog`. 43 | 44 | ## Shutdown Script 45 | 46 | When the driver observes a shutdown signal from the PowerBlock, a shutdown Bash script is called. You can find and edit 47 | it at `/etc/powerblockswitchoff.sh`. 48 | 49 | 50 | ## LibreELEC Installation 51 | 52 | This section is about installing the PowerBlock service on LibreELEC. In the following, it is assumed that we install the driver on a fresh, unmodified LibreELEC installation. 53 | 54 | First, use SSH to log into the running LibreELEC instance. This can be done, e.g., with the command `ssh root@IP_OF_LIBREELEC`. The default password is `libreelec`. 55 | 56 | Being logged in, the actual driver installation is done with this command: 57 | 58 | ```bash 59 | wget -O - https://raw.githubusercontent.com/petrockblog/PowerBlock/master/install_libreelec.sh | bash 60 | ``` 61 | 62 | And that's it! 63 | 64 | 65 |

66 | __Have fun!__ 67 | 68 | -Florian [petrockblock.com](http://blog.petrockblock.com) 69 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function prepare() { 4 | # check if sudo is used 5 | if [[ "$(id -u)" -ne 0 ]]; then 6 | echo "Script must be run under sudo from the user you want to install for. Try 'sudo \$0'" 7 | exit 1 8 | fi 9 | 10 | # ensure that all needed OS packages are installed 11 | apt-get install -y gpiod || ( 12 | c=$? 13 | echo "Error during installation of APT packages" 14 | (exit $c) 15 | ) 16 | 17 | # ensure that we are within the PowerBlock directory 18 | currentDirectory=${PWD##*/} 19 | if [[ $currentDirectory != "PowerBlock" ]]; then 20 | if [[ -d PowerBlock ]]; then 21 | rm -rf PowerBlock 22 | fi 23 | 24 | # Download and extract the repository 25 | curl -L https://github.com/petrockblog/PowerBlock/archive/master.tar.gz | tar xz 26 | mv PowerBlock-master PowerBlock 27 | cd PowerBlock || exit 28 | fi 29 | 30 | # ensure that no old instance of the driver is running 31 | isOldServiceRunning=$(pgrep -f powerblock) 32 | if [[ -n $isOldServiceRunning ]]; then 33 | source uninstall.sh 34 | fi 35 | } 36 | 37 | function installFiles() { 38 | install --mode 0755 scripts/powerblockservice /usr/bin/powerblockservice 39 | install --mode 0755 supplementary/powerblockconfig.cfg /etc/powerblockconfig.cfg 40 | install --mode 0755 supplementary/powerblockswitchoff.sh /etc/powerblockswitchoff.sh 41 | } 42 | 43 | function installService() { 44 | install -m 0755 scripts/powerblock /etc/init.d 45 | update-rc.d powerblock defaults 46 | /etc/init.d/powerblock start 47 | echo "Installation of PowerBlock service done." 48 | } 49 | 50 | function updateBootConfig() { 51 | FILE="$1" # Use the first argument as the file path 52 | LINE_TO_ENSURE="usb_max_current_enable=1" 53 | LINE_TO_REPLACE="usb_max_current_enable=0" 54 | 55 | # Check if the line exists and do nothing if it does 56 | if grep -Fxq "$LINE_TO_ENSURE" "$FILE"; then 57 | echo "$FILE is up-to-date, no action taken." 58 | elif grep -Fxq "$LINE_TO_REPLACE" "$FILE"; then 59 | # If the line with usb_max_current_enable=0 exists, replace it 60 | sed -i "s/^$LINE_TO_REPLACE/$LINE_TO_ENSURE/" "$FILE" 61 | echo "$FILE updated." 62 | else 63 | # If the line does not exist, append it 64 | echo "$LINE_TO_ENSURE" | sudo tee -a "$FILE" 65 | echo "$FILE updated." 66 | fi 67 | } 68 | 69 | 70 | # ---------------------------------- 71 | 72 | prepare 73 | installFiles 74 | installService 75 | 76 | if [[ -f "/boot/config.txt" ]]; then 77 | updateBootConfig "/boot/config.txt" 78 | elif [[ -f "/boot/firmware/config.txt" ]]; then 79 | updateBootConfig "/boot/firmware/config.txt" 80 | fi 81 | 82 | sleep 3 83 | 84 | # sanity checks 85 | # check that the binary is installed 86 | if [[ ! -f /usr/bin/powerblockservice ]]; then 87 | echo "[ERROR] The PowerBlock driver executable is not installed" 88 | else 89 | echo "[SUCCESS] The PowerBlock driver executable is installed" 90 | fi 91 | 92 | # check that the service is running 93 | isServiceRunning=$(pgrep -f powerblock) 94 | if [[ -n $isServiceRunning ]]; then 95 | echo "[SUCCESS] The PowerBlock service is running" 96 | else 97 | echo "[ERROR] The PowerBlock service is not running" 98 | fi 99 | 100 | echo "You can find the configuration file at /etc/powerblockconfig.cfg". 101 | -------------------------------------------------------------------------------- /install_libreelec.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Starting the installation of the PowerBlock service for LibreELEC" 4 | 5 | wget https://raw.githubusercontent.com/petrockblog/PowerBlock/master/scripts/libreelec/powerblock.sh 6 | mv powerblock.sh /storage/ 7 | chmod +x /storage/powerblock.sh 8 | 9 | wget https://raw.githubusercontent.com/petrockblog/PowerBlock/master/scripts/libreelec/autostart.sh 10 | mv autostart.sh /storage/.config/ 11 | chmod +x /storage/.config/autostart.sh 12 | 13 | /storage/powerblock.sh & 14 | 15 | echo "Finished installing the PowerBlock service." 16 | -------------------------------------------------------------------------------- /scripts/libreelec/autostart.sh: -------------------------------------------------------------------------------- 1 | ( 2 | bash /storage/powerblock.sh 3 | ) & 4 | -------------------------------------------------------------------------------- /scripts/libreelec/powerblock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ------------ USER CONFIGURATION HERE -------------- 4 | 5 | IS_ACTIVATED=1 6 | STATUS_PIN=17 7 | SHUTDOWN_PIN=18 8 | DO_LOGGING=1 9 | 10 | # ------------ NO EDITING BELOW THIS LINE ----------- 11 | 12 | 13 | VERSION_MAJOR=2 14 | VERSION_MINOR=0 15 | VERSION_PATCH=0 16 | 17 | # Common path for all GPIO access 18 | BASE_GPIO_PATH=/sys/class/gpio 19 | 20 | # Assign names to states 21 | STATE_ON="1" 22 | STATE_OFF="0" 23 | 24 | # ------------------- FUNCTION DEFINITIONS --------------- 25 | 26 | # Exports a pin if not already exported 27 | # Parameter $1: pin number 28 | exportPin() { 29 | if [ ! -e $BASE_GPIO_PATH/gpio$1 ]; then 30 | echo "$1" > $BASE_GPIO_PATH/export 31 | fi 32 | } 33 | 34 | # Set a pin as an output 35 | # Parameter $1: pin number to be set as output pin 36 | setDirectionOutput() { 37 | echo "out" > $BASE_GPIO_PATH/gpio$1/direction 38 | } 39 | 40 | # Set a pin as an output 41 | # Parameter $1: pin number to be set as output pin 42 | setDirectionInput() { 43 | echo "in" > $BASE_GPIO_PATH/gpio$1/direction 44 | } 45 | 46 | # Set value of a pin 47 | # Parameter $1: pin number 48 | # Parameter $2: output value. Can be 0 or 1 49 | setPinValue() { 50 | echo $2 > $BASE_GPIO_PATH/gpio$1/value 51 | } 52 | 53 | # Returns the current pin value 54 | # Parameter $1: pin number to be read from 55 | getPinValue() { 56 | echo $(cat $BASE_GPIO_PATH/gpio$1/value) 57 | } 58 | 59 | # ---------------- LOGGING FUNCTIONALITY --------------- 60 | 61 | # If the logger is enabled ($DO_LOGGING != 0), log events are written 62 | # into syslog at /var/log/syslog. NOt working per default in Libreelec 63 | write_log() 64 | { 65 | if [[ $DO_LOGGING != 0 ]]; then 66 | while read text 67 | do 68 | logger -t powerblockservice $text 69 | done 70 | fi 71 | } 72 | 73 | 74 | # ---------------- BEGIN OF MAIN PART ------------------ 75 | 76 | echo "Starting PowerBlock driver, version $VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" 77 | 78 | if [[ $IS_ACTIVATED == 0 ]]; then 79 | echo "Shutdown is DEACTIVATED" 80 | else 81 | echo "Shutdown is ACTIVATED" 82 | fi 83 | echo "Shutdown Pin is $SHUTDOWN_PIN" 84 | echo "Status Pin is $STATUS_PIN" 85 | 86 | exportPin $STATUS_PIN 87 | exportPin $SHUTDOWN_PIN 88 | 89 | setDirectionOutput $STATUS_PIN 90 | setDirectionInput $SHUTDOWN_PIN 91 | 92 | echo "Setting RPi status signal to HIGH" 93 | setPinValue $STATUS_PIN $STATE_ON 94 | 95 | stopRunning() { 96 | echo "Exiting PowerBlock driver." 97 | exit 98 | } 99 | 100 | # Ctrl-C handler for clean shutdown 101 | trap stopRunning SIGINT 102 | trap stopRunning SIGQUIT 103 | trap stopRunning SIGABRT 104 | trap stopRunning SIGTERM 105 | 106 | isShutdownRunning=0 107 | while [ 1 ] 108 | do 109 | sleep 1 110 | if [[ $IS_ACTIVATED != 0 ]]; then 111 | currentValue=$(getPinValue $SHUTDOWN_PIN) 112 | if [ $currentValue -eq $STATE_ON ]; then 113 | if [ $isShutdownRunning -eq 0 ]; then 114 | echo "Shutdown signal observed. Executing shutdownscript $SHUTDOWNSCRIPT and initiating shutdown." 115 | shutdown -h now & 116 | isShutdownRunning=1 117 | fi 118 | fi 119 | fi 120 | done 121 | -------------------------------------------------------------------------------- /scripts/powerblock: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: powerblock 4 | # Required-Start: 5 | # Required-Stop: 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: powerblock init script 9 | # Description: Script to start ControlBlockService daemon 10 | ### END INIT INFO 11 | 12 | # Author: Florian Mueller 13 | 14 | # Do NOT "set -e" 15 | 16 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 17 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 18 | DESC="PowerBlockService daemon" 19 | NAME=powerblockservice 20 | DAEMON=/usr/bin/$NAME 21 | DAEMON_ARGS="" 22 | PIDFILE=/var/run/$NAME.pid 23 | SCRIPTNAME=/etc/init.d/$NAME 24 | 25 | # Exit if the package is not installed 26 | [ -x "$DAEMON" ] || exit 0 27 | 28 | # Read configuration variable file if it is present 29 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME 30 | 31 | # Load the VERBOSE setting and other rcS variables 32 | . /lib/init/vars.sh 33 | 34 | # Define LSB log_* functions. 35 | # Depend on lsb-base (>= 3.2-14) to ensure that this file is present 36 | # and status_of_proc is working. 37 | . /lib/lsb/init-functions 38 | 39 | # 40 | # Function that starts the daemon/service 41 | # 42 | do_start() 43 | { 44 | # Return 45 | # 0 if daemon has been started 46 | # 1 if daemon was already running 47 | # 2 if daemon could not be started 48 | start-stop-daemon -b --start --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON --test > /dev/null \ 49 | || return 1 50 | start-stop-daemon -b --start --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON -- \ 51 | $DAEMON_ARGS \ 52 | || return 2 53 | # Add code here, if necessary, that waits for the process to be ready 54 | # to handle requests from services started subsequently which depend 55 | # on this one. As a last resort, sleep for some time. 56 | } 57 | 58 | # 59 | # Function that stops the daemon/service 60 | # 61 | do_stop() 62 | { 63 | # Return 64 | # 0 if daemon has been stopped 65 | # 1 if daemon was already stopped 66 | # 2 if daemon could not be stopped 67 | # other if a failure occurred 68 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME 69 | RETVAL="$?" 70 | [ "$RETVAL" = 2 ] && return 2 71 | # Wait for children to finish too if this is a daemon that forks 72 | # and if the daemon is only ever run from this initscript. 73 | # If the above conditions are not satisfied then add some other code 74 | # that waits for the process to drop all resources that could be 75 | # needed by services started subsequently. A last resort is to 76 | # sleep for some time. 77 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON 78 | [ "$?" = 2 ] && return 2 79 | # Many daemons don't delete their pidfiles when they exit. 80 | rm -f $PIDFILE 81 | return "$RETVAL" 82 | } 83 | 84 | # 85 | # Function that sends a SIGHUP to the daemon/service 86 | # 87 | do_reload() { 88 | # 89 | # If the daemon can reload its configuration without 90 | # restarting (for example, when it is sent a SIGHUP), 91 | # then implement that here. 92 | # 93 | start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME 94 | return 0 95 | } 96 | 97 | case "$1" in 98 | start) 99 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" 100 | do_start 101 | case "$?" in 102 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 103 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 104 | esac 105 | ;; 106 | stop) 107 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 108 | do_stop 109 | case "$?" in 110 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 111 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 112 | esac 113 | ;; 114 | status) 115 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? 116 | ;; 117 | #reload|force-reload) 118 | # 119 | # If do_reload() is not implemented then leave this commented out 120 | # and leave 'force-reload' as an alias for 'restart'. 121 | # 122 | #log_daemon_msg "Reloading $DESC" "$NAME" 123 | #do_reload 124 | #log_end_msg $? 125 | #;; 126 | restart|force-reload) 127 | # 128 | # If the "reload" option is implemented then remove the 129 | # 'force-reload' alias 130 | # 131 | log_daemon_msg "Restarting $DESC" "$NAME" 132 | do_stop 133 | case "$?" in 134 | 0|1) 135 | do_start 136 | case "$?" in 137 | 0) log_end_msg 0 ;; 138 | 1) log_end_msg 1 ;; # Old process is still running 139 | *) log_end_msg 1 ;; # Failed to start 140 | esac 141 | ;; 142 | *) 143 | # Failed to stop 144 | log_end_msg 1 145 | ;; 146 | esac 147 | ;; 148 | *) 149 | #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 150 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 151 | exit 3 152 | ;; 153 | esac 154 | 155 | : 156 | -------------------------------------------------------------------------------- /scripts/powerblockservice: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | VERSION_MAJOR=2 4 | VERSION_MINOR=1 5 | VERSION_PATCH=0 6 | 7 | # Common path for all GPIO access 8 | BASE_GPIO_PATH=/sys/class/gpio 9 | 10 | # Assign names to states 11 | STATE_ON="1" 12 | 13 | CONFIGFILE=/etc/powerblockconfig.cfg 14 | SHUTDOWNSCRIPT=/etc/powerblockswitchoff.sh 15 | 16 | # ------------------- FUNCTION DEFINITIONS --------------- 17 | 18 | # thanks to https://stackoverflow.com/a/54597545 19 | function iniget() { 20 | if [[ $# -lt 2 || ! -f $1 ]]; then 21 | echo "usage: iniget [--list|
[key]]" 22 | return 1 23 | fi 24 | local inifile=$1 25 | 26 | if [ "$2" == "--list" ]; then 27 | for section in $(cat "$inifile" | grep "\[" | sed -e "s#\[##g" | sed -e "s#\]##g"); do 28 | echo "$section" 29 | done 30 | return 0 31 | fi 32 | 33 | local section=$2 34 | local key 35 | [ $# -eq 3 ] && key=$3 36 | 37 | # https://stackoverflow.com/questions/49399984/parsing-ini-file-in-bash 38 | # This awk line turns ini sections => [section-name]key=value 39 | local lines 40 | lines=$(awk '/\[/{prefix=$0; next} $1{print prefix $0}' "$inifile") 41 | for line in $lines; do 42 | if [[ "$line" = \[$section\]* ]]; then 43 | local keyval 44 | keyval=$(echo "$line" | sed -e "s/^\[$section\]//") 45 | if [[ -z "$key" ]]; then 46 | echo "$keyval" 47 | else 48 | if [[ "$keyval" = $key=* ]]; then 49 | echo "$keyval" | sed -e "s/^$key=//" 50 | fi 51 | fi 52 | fi 53 | done 54 | } 55 | 56 | # Exports a pin if not already exported 57 | # Parameter $1: pin number 58 | exportPin() { 59 | if [ ! -e $BASE_GPIO_PATH/gpio"$1" ]; then 60 | echo "$1" > $BASE_GPIO_PATH/export 61 | fi 62 | } 63 | 64 | # Set a pin as an output 65 | # Parameter $1: pin number to be set as output pin 66 | setDirectionOutput() { 67 | echo "out" > $BASE_GPIO_PATH/gpio"$1"/direction 68 | } 69 | 70 | # Set a pin as an output 71 | # Parameter $1: pin number to be set as output pin 72 | setDirectionInput() { 73 | echo "in" > $BASE_GPIO_PATH/gpio"$1"/direction 74 | } 75 | 76 | # Set value of a pin 77 | # Parameter $1: pin number 78 | # Parameter $2: output value. Can be 0 or 1 79 | setPinValue() { 80 | if $GPIO_TOOLS_AVAILABLE; then 81 | gpioset $chip "$1"="$2" 82 | else 83 | echo "$2" > $BASE_GPIO_PATH/gpio"$1"/value 84 | fi 85 | } 86 | 87 | # Returns the current pin value 88 | # Parameter $1: pin number to be read from 89 | getPinValue() { 90 | if $GPIO_TOOLS_AVAILABLE; then 91 | gpioget $chip "$1" 92 | else 93 | cat $BASE_GPIO_PATH/gpio"$1"/value 94 | fi 95 | } 96 | 97 | doShutdown() { 98 | exec $SHUTDOWNSCRIPT & 99 | } 100 | 101 | checkForRoot() { 102 | # check if run as root 103 | if [ "$EUID" -ne 0 ] 104 | then echo "ERROR: powerblockservice needs to be run as root." 105 | exit 106 | fi 107 | } 108 | 109 | Checkkernel(){ 110 | # check the kernel version if GT 6.6 Use Gpiotool event if not runing on a RP5 111 | CURRENT_KERNEL_VERSION=$(uname --kernel-release | cut --delimiter="." --fields=1-2) 112 | CURRENT_KERNEL_MAJOR_VERSION=$(echo "$CURRENT_KERNEL_VERSION" | cut --delimiter="." --fields=1) 113 | CURRENT_KERNEL_MINOR_VERSION=$(echo "$CURRENT_KERNEL_VERSION" | cut --delimiter="." --fields=2) 114 | ALLOWED_KERNEL_VERSION="6.6" 115 | ALLOWED_KERNEL_MAJOR_VERSION=$(echo $ALLOWED_KERNEL_VERSION | cut --delimiter="." --fields=1) 116 | ALLOWED_KERNEL_MINOR_VERSION=$(echo $ALLOWED_KERNEL_VERSION | cut --delimiter="." --fields=2) 117 | if [ "$CURRENT_KERNEL_MAJOR_VERSION" -ge "$ALLOWED_KERNEL_MAJOR_VERSION" ]; then 118 | if [ "$CURRENT_KERNEL_MINOR_VERSION" -lt "$ALLOWED_KERNEL_MINOR_VERSION" ]; then 119 | echo " Kernel $CURRENT_KERNEL_VERSION gpiotools not supported, please use sysfs." | write_log 120 | GPIO_TOOLS_AVAILABLE=false # gpioset is not working as expected. Therefore, use sysfs approach for RPi models < 5. 121 | else 122 | echo " Kernel $CURRENT_KERNEL_VERSION please use gpiotools." | write_log 123 | GPIO_TOOLS_AVAILABLE=true # gpioset is working as expected. 124 | fi 125 | else 126 | echo "Kernel $CURRENT_KERNEL_VERSION Gpiotools not supported please use sysfs." | write_log 127 | GPIO_TOOLS_AVAILABLE=false # gpioset is not working as expected. Therefore, use sysfs approach for RPi models < 5. 128 | fi 129 | } 130 | 131 | # ---------------- LOGGING FUNCTIONALITY --------------- 132 | 133 | # If the logger is enabled ($DO_LOGGING != 0), log events are written 134 | # into syslog at /var/log/syslog. 135 | write_log() 136 | { 137 | if [[ $DO_LOGGING != 0 ]]; then 138 | while read -r text 139 | do 140 | logger -t powerblockservice "$text" 141 | done 142 | fi 143 | } 144 | 145 | 146 | # ---------------- BEGIN OF MAIN PART ------------------ 147 | 148 | checkForRoot 149 | 150 | echo "Starting PowerBlock driver, version $VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" | write_log 151 | 152 | # Check for gpiodetect availability 153 | if command -v gpiodetect >/dev/null 2>&1; then 154 | GPIO_TOOLS_AVAILABLE=true 155 | echo "GPIO tools available." | write_log 156 | else 157 | GPIO_TOOLS_AVAILABLE=false 158 | echo "GPIO tools not available. Trying sysfs method." | write_log 159 | fi 160 | 161 | # Check for RPi model 162 | model=$(cat /proc/cpuinfo | grep 'Model' | awk '{print $5}') 163 | echo "Detected RPi model $model". 164 | 165 | # Determine Raspberry Pi GPIO access chip 166 | # chip=4 for RPi5, chip=0 for older models 167 | if [ $GPIO_TOOLS_AVAILABLE ] && [ "$model" -gt 4 ]; then 168 | chip=4 169 | else 170 | chip=0 171 | Checkkernel 172 | fi 173 | 174 | # read configuration 175 | IS_ACTIVATED=$(iniget $CONFIGFILE powerblock activated) 176 | STATUS_PIN=$(iniget $CONFIGFILE powerblock statuspin) 177 | SHUTDOWN_PIN=$(iniget $CONFIGFILE powerblock shutdownpin) 178 | DO_LOGGING=$(iniget $CONFIGFILE powerblock logging) 179 | 180 | if [[ $IS_ACTIVATED == 0 ]]; then 181 | echo "Shutdown is DEACTIVATED" | write_log 182 | else 183 | echo "Shutdown is ACTIVATED" | write_log 184 | fi 185 | echo "Shutdown Pin is $SHUTDOWN_PIN" | write_log 186 | echo "Status Pin is $STATUS_PIN" | write_log 187 | 188 | if ! $GPIO_TOOLS_AVAILABLE; then 189 | exportPin "$STATUS_PIN" 190 | exportPin "$SHUTDOWN_PIN" 191 | 192 | setDirectionOutput "$STATUS_PIN" 193 | setDirectionInput "$SHUTDOWN_PIN" 194 | fi 195 | 196 | echo "Setting RPi status signal to HIGH" | write_log 197 | setPinValue "$STATUS_PIN" $STATE_ON 198 | 199 | stopRunning() { 200 | echo "Exiting PowerBlock driver." | write_log 201 | exit 202 | } 203 | 204 | # Ctrl-C handler for clean shutdown 205 | trap stopRunning SIGINT 206 | trap stopRunning SIGQUIT 207 | trap stopRunning SIGABRT 208 | trap stopRunning SIGTERM 209 | 210 | isShutdownRunning=0 211 | while true 212 | do 213 | sleep 1 214 | if [[ $IS_ACTIVATED != 0 ]]; then 215 | currentValue=$(getPinValue "$SHUTDOWN_PIN") 216 | if [ "$currentValue" -eq $STATE_ON ]; then 217 | if [ $isShutdownRunning -eq 0 ]; then 218 | echo "Shutdown signal observed. Executing shutdownscript $SHUTDOWNSCRIPT and initiating shutdown." | write_log 219 | doShutdown 220 | isShutdownRunning=1 221 | fi 222 | fi 223 | fi 224 | done 225 | -------------------------------------------------------------------------------- /supplementary/powerblockconfig.cfg: -------------------------------------------------------------------------------- 1 | [powerblock] 2 | activated=1 3 | statuspin=17 4 | shutdownpin=18 5 | logging=1 6 | -------------------------------------------------------------------------------- /supplementary/powerblockswitchoff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | shutdown -h now 3 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # check, if sudo is used 4 | if [[ "$(id -u)" -ne 0 ]]; then 5 | echo "Script must be run under sudo from the user you want to install for. Try 'sudo $0'" 6 | exit 1 7 | fi 8 | 9 | function uninstallFiles() { 10 | rm /usr/bin/powerblockservice 11 | rm /etc/powerblockconfig.cfg 12 | rm /etc/powerblockswitchoff.sh 13 | } 14 | 15 | function uninstallService() { 16 | update-rc.d powerblock remove 17 | rm /etc/init.d/powerblock 18 | echo "PowerBlock service uninstalled." 19 | } 20 | 21 | uninstallService 22 | uninstallFiles 23 | 24 | # check that the service is not running anymore 25 | echo "[SUCCESS] The PowerBlock service was successfully removed. Please reboot to finish the uninstallation." 26 | --------------------------------------------------------------------------------