hotch-potch, Note to self

いろいろ作業記録

外部信号でシャットダウン

はじめに

RaspberryPiの端子に信号が入力されると、シャットダウンする機能です。
いろいろなサンプルが公開されていますが、ここでは、systemdからスクリプトを起動して、定期的に監視する方法を説明します。

環境

  • Raspbian(2018-11-13-raspbian-stretch-lite)

スクリプト

GPIOの監視

「定数」の所は、環境に合わせて修正してください。

定数 意味
SET_WATCH_PORT GPIOピンにシャットダウン信号を入れる番号を指定
SET_TRIGGER 信号の立ち上がり・立ち下がりどちらで反応するかを設定
SET_WATCH_INTERVAL 指定した秒数ごとに、シャットダウン信号の状態を監視

ソース中の所々の[INSTALL DIR]は、お使いの環境に合わせて書き換えてください。

#!/bin/bash
#--------------------------------------------------------------------
#UPSの信号監視とシャットダウン
#--------------------------------------------------------------------
#
#install:
# $ sudo ln -s /home/pi/[INSTALL DIR]/setPowerWatcher.service /etc/systemd/system
# $ sudo systemctl enable setPowerWatcher.service
# $ sudo systemctl start setPowerWatcher.service
#--------------------------------------------------------------------
#ref:
#
# ※必ずbashを使うこと。shは関数の宣言でエラーが出ます
#   https://yatta47.hateblo.jp/entry/2014/04/20/222707
#

#----------------------------------
#定数
#----------------------------------

#監視するポート
SET_WATCH_PORT=2

#監視モード
# 1:ダウンパルス、0:アップパルス
SET_TRIGGER=1

#監視ループの間隔(sec)
SET_WATCH_INTERVAL=1


#----------------------------------
#関数
#----------------------------------

#シャットダウンの開始処理
function ShutdownGoProc ( ) {
    #シャットダウン信号を検出したメッセージを表示
    echo "  ! Shutdown signal detected. ${MESSAGE}"

    #シャットダウン処理を開始
    if [ ! "$MODE_TEST" = 1 ]; then
        #瞬停防止のため1秒だけshutdownを待つ
        /sbin/shutdown -P now
    fi
}

#シャットダウンの停止処理
function ShutdownStopProc ( ) {
    #シャットダウン信号を検出したメッセージを表示
    echo "  + Shutdown signal disappeared. ${MESSAGE}"

    #シャットダウン処理を中止
    if [ ! "$MODE_TEST" = 1 ]; then
        /sbin/shutdown -c
    fi

    #すぐにループ処理に戻らないよう、ウェイト
    sleep 1
}


#----------------------------------
#メイン処理
#----------------------------------

#実行中ユーザをチェック
if [ `/usr/bin/id -u` != "0" ]; then
    #rootでなければ異常終了
    echo " ! Please run as root."
    exit 1
fi

#主要コマンドの存在をチェック
if [ ! -x /usr/bin/gpio ]; then
    #見つからなければ異常終了
    echo " ! No 'gpio' Command."
    exit 1
fi

#自身のコマンド名を変数に入れる。usage用
CMDNAME=`basename $0`

#引数のチェックと動作フラグの生成
#https://shellscript.sunone.me/parameter.html
while getopts vt OPT
do
    case $OPT in
        "v" )
            #verboseモード
            MODE_VERBOSE=1 ;;
        "t" )
            #テストモード(シャットダウンはしない)
            MODE_TEST=1 ;;
        * )
            #それ以外の指定は、usageを表示
            echo "Usage: $CMDNAME [-v] [-t]" 1>&2
            exit 1 ;;
    esac
done

#動作モードのメッセージ
if [ ${SET_TRIGGER} -eq 1 ]; then
MESSAGE="(MODE: Down trigger)"
else
MESSAGE="(MODE: Up trigger)"
fi

#起動メッセージ
if [ "$MODE_VERBOSE" = 1 ]; then

  #メッセージ
  echo " * Monitor the GPIO signal and shutdown * ${MESSAGE}"

  #テストモードのメッセージ表示
  if [ "$MODE_TEST" = 1 ]; then
    echo '  - test mode (no shutdown)'
  fi
fi

#ポートの初期化
`/usr/bin/gpio -g mode ${SET_WATCH_PORT} in`

#監視ポートの状態変数の初期化と問い合わせ
STAT_PORT_PREV=`/usr/bin/gpio -g read ${SET_WATCH_PORT}`

#監視ループ
while :
do
    #監視ポートの状態を問い合わせ
    STAT_PORT_NOW=`/usr/bin/gpio -g read ${SET_WATCH_PORT}`

    if [ ${SET_TRIGGER} -eq 1 -a ${STAT_PORT_PREV} = "1"  -a  ${STAT_PORT_NOW} = "0" ]; then
        #ダウントリガで検知
        ShutdownGoProc
    elif [ ${SET_TRIGGER} -eq 0 -a ${STAT_PORT_PREV} = "0"  -a  ${STAT_PORT_NOW} = "1" ]; then
        #アップトリガで検知
        ShutdownGoProc 

    elif [ ${SET_TRIGGER} -eq 1 -a ${STAT_PORT_PREV} = "0"  -a  ${STAT_PORT_NOW} = "1" ]; then
        #アップトリガで検知
        ShutdownStopProc
    elif [ ${SET_TRIGGER} -eq 0 -a ${STAT_PORT_PREV} = "1"  -a  ${STAT_PORT_NOW} = "0" ]; then
        #ダウントリガで検知
        ShutdownStopProc 
    fi

    #監視ポートの状態変数に、現在の状態を代入
    STAT_PORT_PREV=${STAT_PORT_NOW}

    #ウェイト
    sleep ${SET_WATCH_INTERVAL}
done

exit 0

systemdに登録するserviceファイル

[Unit]
Description=Monitor the GPIO signal and shutdown
After=local-fs.target
ConditionPathExists=/home/pi/[INSTALL DIR]

[Service]
ExecStart=/home/pi/[INSTALL DIR]/setPowerWatcher.sh
ExecStop=/bin/kill ${MAINPID}
Restart=on-failure
StartLimitInterval=60
StartLimitBurst=3
KillMode=mixed
Type=simple
User=root
Group=root

[Install]
WantedBy=multi-user.target

インストール手順

systemdへ登録

systemdに登録して、サービスを起動します。

$ sudo ln -s /home/pi/[INSTALL DIR]/setPowerWatcher.service /etc/systemd/system
$ sudo systemctl enable /home/pi/[INSTALL DIR]/setPowerWatcher.service

serviceファイルを修正した時

下記のように都度、リロードします。

$ sudo systemctl daemon-reload

状態を確認

systemctl statusコマンドで、状態を確認します。
まだ起動していません

$ systemctl status setPowerWatcher.service
● setPowerWatcher.service - Monitor the GPIO signal and shutdown
   Loaded: loaded (/home/pi/[INSTALL DIR]/setPowerWatcher.service; linked; vendor preset: enabled)
   Active: inactive (dead)

起動

$ sudo systemctl start setPowerWatcher.service

状態を確認

下記のようになっていて、左上の●が緑色になっていれば、正常に起動しています。

$ systemctl status setPowerWatcher.service
● setPowerWatcher.service - Monitor the GPIO signal and shutdown
   Loaded: loaded (/home/pi/[INSTALL DIR]/setPowerWatcher.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2019-03-09 15:47:22 JST; 9s ago
 Main PID: 537 (setPowerWatcher)
   CGroup: /system.slice/setPowerWatcher.service
           tq537 /bin/bash /home/pi/[INSTALL DIR]/setPowerWatcher.sh
           mq564 sleep 1

Mar 09 15:47:22 raspizwh systemd[1]: Started Monitor the GPIO signal and shutdown.

動作確認

GPIOに信号を入れると、間もなくシャットダウンします
その際syslogには、下記のメッセージが残ります。

Mar  9 15:58:13 raspizwh setPowerWatcher.sh[537]:   ! Shutdown signal detected. (MODE: Down trigger)