Re:Readme

PCトラブルや環境構築、家電量販店とかで買ったもののメモ。ご利用は自己責任で。

Raspberry Pi 3にシャットダウンボタンをつける(3)

GPIO入力でシャットダウンを実行するPythonスクリプトを前回まで作成した。今回はいよいよこのスクリプトが起動時にバックグラウンドで自動的に実行されるよう、デーモン化させてみる。

デーモンとは

こういうプロセスが一般にデーモンと呼ばれますよ、という説明が分かりやすかったのが以下の記事。
engineering.otobank.co.jp

サンプルコード

実際にPythonでデーモンを実装してみた記事はこちら。

http://momijiame.tumblr.com/post/19505634870/python-でデーモンを作る
momijiame.tumblr.com

Pythonに関して言えばスクリプトを簡単にデーモン化させるライブラリpython-daemonがあるのでこれを使ってみました。参考は下記の記事。
http://kralis.dip.jp/wiki/index.php?Python-スクリプトのDaemon化

シャットダウンスクリプトをデーモン化させてみた

デーモンの名前には末尾にdをつけるのが慣例らしいので、gpio_shutdownd.pyのようにファイル名を付け、下記の内容をファイルに書き込んだ。

#!/usr/bin/env python
# -*- encoding:utf-8 -*-

from __future__ import with_statement

import sys
import time
import subprocess

import RPi.GPIO as GPIO

from daemon import DaemonContext
from daemon.pidfile import PIDLockFile


def waitShutdownButton():
  # GPIO22 = Pin15
  GPIO_OUT = 22
  # GPIO27 = Pin13
  GPIO_IN  = 27

  LOOPS_PER_SEC = 20
  BLINK_SEC = 0.5
  MAX_PRESS_SEC = 5
  SHUTDOWN_SEC = 5
  REBOOT_SEC = 3
  count = 0

  GPIO.setmode(GPIO.BCM)
  GPIO.setup(GPIO_IN, GPIO.IN, pull_up_down = GPIO.PUD_UP)
  GPIO.setup(GPIO_OUT, GPIO.OUT, initial = GPIO.LOW)
            
  try:
    while True:
      GPIO.wait_for_edge(GPIO_IN, GPIO.FALLING)

      while not GPIO.input(GPIO_IN):
        blink = not(int(count / (LOOPS_PER_SEC * BLINK_SEC)) % 2)
        GPIO.output(GPIO_OUT, blink)
        count += 1
        if count % LOOPS_PER_SEC == 0:
          print(count / LOOPS_PER_SEC)

        if (count >= LOOPS_PER_SEC * MAX_PRESS_SEC):
          break

        time.sleep(1.0 / LOOPS_PER_SEC)

      if (count >= LOOPS_PER_SEC * SHUTDOWN_SEC):
        print(u'Shutdown now...')
        GPIO.cleanup()
        command = u'sudo shutdown -h now'
        subprocess.call(command,
                        shell = True)
        break
      elif (count >= LOOPS_PER_SEC * REBOOT_SEC):
        print(u'Reboot now...')
        GPIO.cleanup()
        command = u'sudo shutdown -r now'
        subprocess.call(command,
                        shell = True)
        break
      GPIO.output(GPIO_OUT, GPIO.LOW)
      count = 0

  except KeyboardInterrupt:
    GPIO.cleanup()

if __name__ == '__main__':
  with DaemonContext(pidfile = PIDLockFile(u'/var/run/gpio_shutdownd.pid')):
    waitShutdownButton() # <--このスコープにデーモン化させたい処理を書く
  sys.exit(0)

デーモンの起動

続いて、OS起動時にデーモンを起動させるためにsystemdを使用する。
qiita.com

ここでいうサービスは、デーモンの言い換えといって差し支えないみたい。
gpio_shutdownd.serviceのような名前の設定ファイルを用意し、中に下記の内容を書き込む。この名前がサービス名となる。設定ファイルは/etc/systemd/system/の下におく。

[Unit]
Description=Shutdown Daemon
[Service]
ExecStart=/home/pi/projects/gpio_shutdown/gpio_shutdownd.py
Restart=always
Type=forking
PIDFile=/var/run/gpio_shutdownd.pid
[Install]
WantedBy=multi-user.target

この設定ファイルの書き方についてはこちらの記事が参考になった。
enakai00.hatenablog.com

あとは

$ sudo systemctl daemon-reload

で設定ファイルの再読込を行い、

$ sudo systemctl start gpio_shutdownd

でデーモンgpio_shutdowndが起動する。
デーモンの状態は下記のコマンドで確認できる。

$ systemctl status gpio_shutdownd

スクリプトの場合はデーモンが起動するとLEDが点灯するのでそれでも確認できる。
あとは自動起動するため、

$ sudo systemctl enable gpio_shutdownd

を実行後、再起動してみてデーモンが自動起動することを確認すれば終了。

その他の参考文献

systemdについて調べて参考になった記事をメモ。
dreamerdream.hateblo.jpequj65.net
postd.cc