Flaskを使って外出先からURL経由でRaspberryPiを操作する
はじめに
この記事はオートロックマンション用不在時荷物受け取りシステムの開発記事です。
前回の記事で、WebIOPiを使って外出先からリモートでRaspberryPiにつないだサーボモータを操作した。
WebIOPiではURLによるGPIOの操作をサポートしているが、HTTP POSTでアクセスする必要があった。 通常のURLによるアクセスはHTTP GETのため、前回はRaspberryPiに接続したサーボモータを操作するために一旦WebIOPiのGPIO操作画面を開き、そこからサーボモータを操作していた。
今開発しているオートロックマンション用不在時荷物受け取りシステムでは、配達員がインターホンを鳴らしてから不在を判断して持ち帰ってしまうまでの間に通知を受けたユーザが反応してサーボモータをリモート操作、共同玄関を解錠する必要がある。 WebIOPiではURLを表示してからどうしても2タップ必要になるため、そこの時間がもったいない。 また、WebIOPiを使ってGPIOを操作する場合はGUI画面でピン番号をタップすることになるが、短時間で焦って押すと間違って別のピンをタップしてしまう恐れもある。
そこで、より短時間・確実にユーザがインターホン通知に反応するため、今回Flaskを導入してURL1タップで共同玄関をサーボモータで解錠するところまでやってみた。
なお、RaspberryPi 3B+、Python3.7.3を使っている。
Flaskとは
Flask(フラスク)は、Python用の、軽量なウェブアプリケーションフレームワークである。小規模で簡単なウェブアプリケーションを作るのに適しているらしい。
今回の場合は「URLタップ」→「サーボモータを操作」だけなので、このような用途に向いているようだ。
Flaskのインストール
python3を使っているので、
sudo pip3 install flask
でFlaskをインストール。
FlaskでHello World
ウィキペディアに載っているFlaskのサンプルコードをもとにHello Worldしてみる。
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run("0.0.0.0")
内容としては、app = Flask(__name__)
でFlaskのインスタンスを作成し、app.run("0.0.0.0")
でローカルサーバでアプリケーションを実行する。run()に何もIPを指定しないとローカルホストで動作するため外部からアクセスすることはできないが、"0.0.0.0"
を指定すると自分以外の外部からアクセスすることが可能だ。(この段階ではポートマッピングしていないのでアクセスできるのは同じLAN内の端末のみ)
Flaskはポート番号5000を使うため、例えばルート/
にアクセスしたい場合は、簡易的にはブラウザでhttp://[ローカルIP]:5000
にアクセスすれば良い。例えば、以下はFlaskを動かしているRaspberryPiのローカルIPが192.168.0.6の場合。もちろん、これは同じLANに接続されている端末からだけアクセス可能だ。
こんな感じに”Hello World!”できる。簡単。
加えて、例えばサーボモータを操作するURLを実装するには以下のようなコードを追加実装し、http://[RaspberryPiのIP]:5000/push_servo/
にアクセスすれば良い。
@app.route("/push_servo/") def push_servo(): pushServo() # サーボを動かす関数に飛ばす return "Push Servo!"
アクセスしたユーザには"Push Servo!"が返る。
こんな感じにして、アクセスするURLを返ることで自由な操作を実装、実行することが可能である。
Flaskをスレッド化
Flaskは大変便利なのだが、app.run()するとそこでブロックされてしまうため、その他の処理を行うことができない。 今回の場合は常にウェブカメラから音声データを取得してインターホン検知を行う必要があるが、それがブロックされてしまう。 そこで、Flaskの処理をスレッド化し、メインルーチンの処理をブロックしないようにする。スレッド化にはthreadingを使う。
from flask import Flask import time import threading app = Flask(__name__) @app.route("/push_servo/") def push_servo(): return "Push Servo!" @app.route("/") def hello(): return "Hello World!" if __name__=="__main__": rest_service_thread = threading.Thread(name='rest_service', target=app.run, args=('0.0.0.0',), kwargs=dict(debug=False)) rest_service_thread.start() while True: print("Main routine!") time.sleep(1) rest_service_thread.join()
これを実行した上で例えばLANの別のPCからhttp://[RaspberryPiのローカルIP]:5000/
やhttp://[RaspberryPiのローカルIP]:5000/push_servo/
にアクセスすると、
$ python3 flasktest.py Main routine! * Serving Flask app "flasktest" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) Main routine! Main routine! Main routine! Main routine! Main routine! Main routine! 192.168.0.4 - - [12/Jan/2020 22:54:19] "GET / HTTP/1.1" 200 - Main routine! Main routine! Main routine! Main routine! 192.168.0.4 - - [12/Jan/2020 22:54:23] "GET /push_servo/ HTTP/1.1" 200 - Main routine! Main routine! Main routine! Main routine!
上のように、メインルーチンをブロックせずにHTTPのリクエストを受けることができる。これで、ウェブカメラでインターホンを監視しながら同時にURLでユーザのリモート操作を受け付けられるようになった。
外出先からアクセスする
これまではLAN内での操作だったが、実際には外出先からRaspberryPiに接続されたサーボモータを操作したい。ここでは前回と同じく、ルータのポートマッピングの設定を行い外部からアクセス可能にする。
ポートマッピングの方法は前回の記事を参照。前回はWebIOPiのポート8000を設定していたが、今回のFlaskはポート5000なので、5000を設定すること。
これで、例えば/unlock/
というサーボモータを動かす機能を持ったURLを用意しておけば、http://[グローバルIP]:5000/unlock/
というURLにユーザがアクセスすることで外出先からサーボモータを操作し、共同玄関を解錠する事が可能である。
さらに、このURLとインターホンの画面(以前の記事参照)をPushBulletで通知することで、ユーザはインターホン画面を確認したら1タップで共同玄関を解錠することが可能である。
PushBulletでの通知については、過去記事を参照。