Re:Readme

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

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

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

デーモンとは

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

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

参考文献はこちら。
qiita.com

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

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

from __future__ import with_statement

import os
import sys
import time
import subprocess

import RPi.GPIO as GPIO


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()

def daemonize(func):
  pid = os.fork()

  if pid > 0:
    pidfile = open(u'/var/run/gpio_shutdownd.pid', 'w')
    pidfile.write(str(pid) + "\n")
    pidfile.close()
    sys.exit()
  else:
    func()

if __name__ == '__main__':
  daemonize(waitShutdownButton)

デーモンの起動

続いて、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