ITと雑記とド田舎と

ド田舎在住エンジニアがIT備忘録と雑記を書くブログです

Raspberry Piで人感通知システムを作ろう[3]

人感感知システム記事第3回です!

今回は前回作成したPython通知スクリプト自動起動する方法と、Raspberry Piを制御するWebアプリを説明したいと思います。

 

 

systemdでスクリプトをデーモン化

systemdRaspberry Piで利用されている公式OSのRaspbianを含めた、最近のLinux OSでシステムを管理する仕組みです。Linuxのプロセス(デーモン)はsystemdで管理されているため、自作したプログラムをデーモン化する場合systemdを設定してあげればよいです。

systemdではserviceファイルというファイルを作成してデーモンを新しく登録することできます。というわけで、前回説明した人感通知のPythonスクリプト「help_call.py」を管理するserviceファイルを作成しました。スクリプトは「/usr/local/bin」に置いてあることとします。

 

/usr/lib/systemd/system/helpcall.service

[Unit]
Description=HelpCall
After=syslog.target

[Service]
Type=simple
WorkingDirectory=/usr/local/bin
ExecStart=/usr/bin/python3 /usr/local/bin/help_call.py
Restart=always
PIDFile=/var/run/help_call.pid
StandardOutput=syslog
StandardError=syslog

続いてデーモン化するpyファイルに実行権限を与えます。

sudo chmod a+rx /usr/local/bin/help_call.py

デーモン化を有効にして、スタートさせます。

sudo systemctl daemon-reload
sudo systemctl enable helpcall.service
sudo systemctl start helpcall.service

これで通知プログラムがデーモンとして開始され、Raspberry Piの起動時に自動的にデーモンが開始されるようになりました。

 

Raspberry Piを操作するWebアプリ

Raspberry PiをWebアプリで操作して、通知機能のON/OFF、シャットダウン、再起動をできるようにしました。またしても、Python3でWebサーバーをRaspberry Pi内に構築します。PythonはWeb開発のフレームワークがいくつかあります。一番有名なものはDjangoでしょうか。Djangoは企業のシステム開発でも利用されるような大規模開発にも向いているフルスタックと呼ばれるWeb開発フレームワークですが、今回は非常にシンプルなWebアプリでいいので、必要な機能を自分で組み合わせていくタイプのTornadoを利用しました。

用意するファイルは3つです。Web画面になる「index.html」、スタイルシートの「style.css」、Pythonの「server.py」。htmlファイルとcssファイルはpyファイルと同じ階層に「templates」と「static」2つのディレクトリを作って、その中に配置します。templatesがhtmlファイル置き場です。

以下それぞれのコードです。

server.py

import os
import tornado.ioloop
import tornado.web
from tornado.web import url

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

class ShutdownHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("shutdown")
        os.system("sudo shutdown")

class RebootHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("reboot")
        os.system("sudo reboot")

class HelpCallONHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Call ON")
        os.system("sudo systemctl start helpcall.service")

class HelpCallOFFHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Call OFF")
        os.system("sudo systemctl stop helpcall.service")

BASE_DIR = os.path.dirname(__file__)

def make_app():
    return tornado.web.Application([url(r'/', MainHandler, name='index'),
                                    url(r'/shutdown/', ShutdownHandler, name='shutdown'),
                                    url(r'/reboot/', RebootHandler, name='reboot'),
                                    url(r'/callon/', HelpCallONHandler, name='callon'),
                                    url(r'/calloff/', HelpCallOFFHandler, name='calloff')],
            template_path=os.path.join(BASE_DIR, 'templates'),
            static_path=os.path.join(BASE_DIR, 'static'),)

if __name__=="__main__":
    app = make_app()
    app.listen(8888)
    print("Server is up ...")
    tornado.ioloop.IOLoop.current().start()

 

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Raspberry Pi Controller</title>

    <!-- CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="{{ static_url("style.css") }}"/>
  </head>
  <body>
    <div class="container-fluid">
      <div class="jumbotron">
        <div class="row">
          <div class="col-xs-12 col-md-6">
            <div class ="page-header" id="main">
              <h1>ラズパイ操作</h1>
            </div>
            <p>
              <div class="btn-toolbar">
                <div class="btn-group">
                  <form method = 'get' action="{{ reverse_url("callon")}}">
                    <input type="submit" value="Call ON">
                  </form>
                  <form method = 'get' action="{{ reverse_url("calloff")}}">
                    <input type="submit" value="Call OFF">
                  </form>
                </div>
              </div>
            </p>

            <p>
              <form method = 'get' action="{{ reverse_url("shutdown")}}">
                  <input type="submit" value="シャットダウン">
              </form>
            </p>
            <p>
              <form method = 'get' action="{{ reverse_url("reboot")}}">
                  <input type="submit" value="再起動">
              </form>
            </p>
          </div>
        </div>
      </div>
    </div>

    <!-- JavaScript -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
  </body>
</html>

 

style.css

body {
  padding-top: 50px;
  padding-bottom: 20px;
}

server.pyを実行するとWebサーバーが動き始めます。あとは適当なブラウザからRaspberry PiIPアドレスでアクセスしてみましょう。ポート番号は8888にしています。一例ですが、URLに「http://192.168.11.40:8888」などと入力すればよいです。Raspberry PiIPアドレスルーターの設定か、Raspberry Pi側の設定で固定IPアドレスにしておくことをお勧めします。うまくいけばブラウザでWebページが開くはずです。

f:id:kdn-1017-wttd:20180625000634p:plain

これで、スマートフォンのWebブラウザからアクセスすればPCが無くてもRasbperry Piの操作を行えます。もちろんスマートフォンRaspberry Piと同じWiFiに接続されていることが前提です。また、セキュリティ面は一切考えていないので、インターネットからWebページにアクセスできるようにはしないでください。こちらもRaspberry Piの起動時に、同時に実行されるように設定しました。

 

まとめ

今回でRaspberry Piを利用した人感検知システムの紹介は終わりになります(忘れていることがなければ)。実はこの後、人感センサーの設置、反応の調整などでも苦戦しました。もし機会があればそちらのお話も書くかもしれませんが、単なる苦労話になりそうな気がします。私はRaspberry PIを触り始めて3年程経過しているのですが、曲がりなりにもシステムと呼べそうなものをプライベートで作ったのは今回で2回目です。中々面白いものが作れたように思います。ただ、大学も情報工学専攻でしたし、就職してからもソフトよりの仕事ばかりしているので、もう少しハード側の勉強もしないと電子工作で本格的なものは作れないですね。精進したいと思います。

 

 参考

pythonをデーモン化するメモ

パーミッションなどを設定する!chmodコマンドの詳細まとめ【Linuxコマンド集】

Tornado入門(3):テンプレートを使った開発【実践編】

PythonのWebフレームワークTornadoを使ってみる その1