West Gate Laboratory

人生を少しでも,面白く便利にするモノづくり

Flaskを使って外出先からURL経由でRaspberryPiを操作する

はじめに

この記事はオートロックマンション用不在時荷物受け取りシステムの開発記事です。

westgate-lab.hatenablog.com

前回の記事で、WebIOPiを使って外出先からリモートでRaspberryPiにつないだサーボモータを操作した。

westgate-lab.hatenablog.com

WebIOPiではURLによるGPIOの操作をサポートしているが、HTTP POSTでアクセスする必要があった。 通常のURLによるアクセスはHTTP GETのため、前回はRaspberryPiに接続したサーボモータを操作するために一旦WebIOPiのGPIO操作画面を開き、そこからサーボモータを操作していた。

今開発しているオートロックマンション用不在時荷物受け取りシステムでは、配達員がインターホンを鳴らしてから不在を判断して持ち帰ってしまうまでの間に通知を受けたユーザが反応してサーボモータをリモート操作、共同玄関を解錠する必要がある。 WebIOPiではURLを表示してからどうしても2タップ必要になるため、そこの時間がもったいない。 また、WebIOPiを使ってGPIOを操作する場合はGUI画面でピン番号をタップすることになるが、短時間で焦って押すと間違って別のピンをタップしてしまう恐れもある。

f:id:kaname_m:20200109215354p:plain
WebIOPiのGPIO操作画面。ピンの番号をタップしてOn/Offを操作する

そこで、より短時間・確実にユーザがインターホン通知に反応するため、今回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に接続されている端末からだけアクセス可能だ。

f:id:kaname_m:20200112224247p:plain
FlaskのHello World実行結果

こんな感じに”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を設定すること。

westgate-lab.hatenablog.com

これで、例えば/unlock/というサーボモータを動かす機能を持ったURLを用意しておけば、http://[グローバルIP]:5000/unlock/というURLにユーザがアクセスすることで外出先からサーボモータを操作し、共同玄関を解錠する事が可能である。

さらに、このURLとインターホンの画面(以前の記事参照)をPushBulletで通知することで、ユーザはインターホン画面を確認したら1タップで共同玄関を解錠することが可能である。

PushBulletでの通知については、過去記事を参照。

westgate-lab.hatenablog.com