RaspberryPiでスマートホーム 〜MQTTで指令を受ける〜
2019/09/11 |
|
2017/12/26 |
|
前回RaspberryPiとUSB赤外線リモコンアドバンスを使って照明をラズパイからコントロールできるようになりました。
ただ現状ではラズパイ上でコマンドを叩かないと操作できません。cronで決められた時間に決められた操作をするだけならそれでもいいんですけど、自分で好きな時に好きな操作をするには不便です。
そこでよそから指令を投げてその指令を実行するようにします。
必要なもの
MQTT
まずはラズパイが指示を受けられる体制を整えようと思います。その指示のやりとりにはMQTTを使うことにしました。
MQTTとは…
要はコンピューター同士がコミュニケーションするための仕組みだそうです。メッセージを中継してくれるブローカー(サーバー)に接続してどのトピック(チャンネルのようなもの)に送信するのか(パブリッシュ)、どのトピックから受信するのか(サブスクライブ)、をブローカーに伝えます。
コンピュータ用のLINEみたいなものですね。複数の端末で同時に使用できるので、さらにいうとグループトークといったとこでしょうか。
このMQTTを使う場合どこかにメッセージを中継してくれるブローカーが必要です。家の中だけならラズパイにそのブローカーの役割をやらせてもいいんですが、せっかくなら外からも操作できると便利です。
AWS IoT
なのでAmazonのAWSを使うことにしました。
AWSのサービスの中にAWS IoTというのがあり、これがMQTTのブローカーとしてメッセージの中継をしてくれます。
AWSを使うにはあらかじめアカウントを作成しておく必要があります。アカウント作成の説明は省略しますが、アカウント作成してIAMでユーザー作成して…といったお決まりの約束をあらかじめ済ましておきます。
AWS IoTは有料ですが、今回のような使い方ではほぼ無料に等しいので、AWSに決定しました。
インターネット上には無料のMQTTブローカーもあるようなのでAWSが嫌ならそれらを使ってもいいと思います。
準備
AWSにログインしたらAWS IoTのページに行き、ラズパイを登録します。
ポリシーを作成
まず、このラズパイはAWS上で何をすることが許可されているのか、を指定するポリシーを作成します。
画面左の”安全性”→”ポリシー”を選択。そして表示された画面で”ポリシーの作成”ボタンを押します。
必要な情報を入力します。
- 名前→PiPolicy
- アクション→iot:*
- リソースARN→”replaceWithATopic”を*に変更
安直にiot関係は全て操作できるように指定しています。名前は何でも構いません。リソースARNは”replaceWithATopic”の部分を”*”に置き換え全トピックを使えるようにしてますが、使えるトピックを制限したいなら具体的なトピック名にします。
ラズパイを登録
次にラズパイを登録します。画面左のメニューの”管理”→”モノ”を選択し、”モノの登録”ボタンを押します。
”単一のモノを作成する”ボタンを押します。
名前を入力します。何でも構いませんがここでは”Pi”としています。
同じ画面の下の方にスクロールして、”次へ”ボタンを押します。
次に証明書を作成します。何を証明するのかというと、この証明書をラズパイ側に置くことで、AWSに対してこれは確かに私が登録したラズパイです、ということを証明します。
作成された証明書をダウンロードします。上3つはダウンロードをクリックします。左下にある”AWSのIoTルートCAダウンロード”はクリックすると…
別ページに飛びます。”Amazon Root CA 3”を右クリックして、リンク先をダウンロードします。
ダウンロードした証明書は、以下の名前で保存しました。
- このモノの証明書…xxxxxxxxxx-certificate.pem.crt
- パブリックキー…xxxxxxxxxx-public.pem.key
- プライベートキー…xxxxxxxxxx-private.pem.key
- AWSのIoTルートCA…AmazonRootCA1.pem
パブリックキーとプライベートキーのxxxxxxxxxxの部分は実際はidになっています。これらの証明書はPi上で後ほど作成する、Pythonのスクリプトと同じフォルダにコピーします。
先ほどの画面に戻り、忘れずに証明書を”有効化”して、”ポリシーをアタッチ”ボタンを押します。
次は最初に作成したポリシーをアタッチします。一つしかないのでチェックを入れて、”モノの登録”ボタンを押します。
ラズパイがAWS IoTに登録されました。これでラズパイがAWS IoTとメッセージのやりとりができるようになります。
作ってみる
接続する
次にブローカーに接続してサブスクライブするスクリプトを作成します。Pythonで作成しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import paho.mqtt.client import ssl import subprocess import json endpoint = "(エンドポイント)" port = 8883 topic_to = "homeapp/to" topic_from = "homeapp/from" rootCA = "AmazonRootCA1.pem" cert = "xxxxxxxxxx-certificate.pem.crt" key = "xxxxxxxxxx-private.pem.key" def on_connect(client, userdata, flags, respons_code): print("connected") client.subscribe(topic_to) def on_message(client, userdata, msg): print("received:" + msg.payload.decode("utf-8")) data = json.loads(msg.payload.decode("utf-8")) par = data["resource"] + "_" + data["command"] + ("_" + data["parameters"] if data["parameters"] else "") subprocess.call("/bin/bash ir.sh " + par, shell=True) if __name__ == '__main__': client = paho.mqtt.client.Client() client.on_connect = on_connect client.on_message = on_message client.tls_set(rootCA, certfile=cert, keyfile=key, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None) client.connect(endpoint, port=port, keepalive=60) client.loop_forever() |
AWSに接続して繋がったら”homeapp/to”というトピックをサブクライブするスクリプトです。”homeapp.py”という名前で作成しました。
トピック名ですが”homeapp/to”はラズパイへの送信、今回は使いませんが”homeapp/from”はラズパイからの送信に使おうと思ってます。
7行目の(エンドポイント)はAWSで自分のendpointを確認してそれに置き換えます。先ほど登録した”Pi”を選択し…
”操作”を押したら表示される画面の”HTTPS”下のARNがそれです。
12,13行目の証明書と秘密鍵のファイル名も実際にダウンロードしたファイル名に置き換えます。あらかじめこのスクリプトと同じフォルダにコピーしておいてください。
paho mqttというコンポーネントを使用していますので実行する前に
pip install paho-mqtt
であらかじめインストールしておきます。
python homeapp.py
で実行します。スクリプトの最後で”loop_forever()”してるのでプロンプトには帰ってきません。ずっと受信し続けます。接続時とメッセージ受信時には画面に表示します。
MQTTでJSONのメッセージを受信したら各パラメータを取り出し、前回作成したir.shコマンドを呼び出してリモコンを操作します。
動かしてみる
AWS Iotの左側のメニューから”テスト”を選択します。
topicには”homeapp/to”、JSONは以下のように入力して”トピックに発行”を押します。
1 2 3 4 5 |
{ "resource": "light", "command": "power", "parameters": "on" } |
ライトが付きました!
これでMQTTを使って指示を待ち受ける準備ができました。JSON形式の指示をMQTTで送信することができればどのデバイスからも部屋の電気やエアコンを操作できるようになりました。
運用する
このスクリプトが起動し続けてないと当然受信できませんので
nohup python homeapp.py &
等でバックグラウンドで動かし続けておく必要があります。
デーモン化しておくとよりよいかと思います。”python スクリプト デーモン化”で検索すると参考になるページが引っかかります。私はsupervisorを使ってます。
エラー処理
エラー処理してないのでメッセージが予期した内容と違うと落ちます。必要ならtryで補足して無視する、ログを吐く、等行ってください。前述のnohupコマンドで起動した場合は、出力が同じディレクトリのnohup.outというテキストに書き込まれます。
メッセージの重複
あとMQTTにはQoSで品質が定められています。
- QoS0…とりあえず1回投げる。届こうが届くまいが知らない。
- QoS1…最低1回は届く。2回以上届くかもしれない。
- QoS2…確実に1回届ける。
AWS IoTはQoS0,1なので同じメッセージが何回か来る可能性があります。つまり同じメッセージが2回くると2回実行されてしまいます。JSONにシリアル番号などを付加しておいて既に実行した番号なら無視する、といった処理も場合によっては必要かもしれません。
感想
ネットワーク越しに家電を操作できるようになりました。ちょっと感動です。今回はAWS IoTを使ったので、セキュリティがらみでちょっと面倒くさかったですが、普通のMQTTサーバーとかを使うのであれば、もっと簡単だと思います。
送信はどうする?
受信はできるようになりました。でも毎回AWSのテストツールからJSONで指示を組み立てて送信するのも面倒なので、次は送信側を作らないといけないですね。PCやスマホ等いろいろなものから送信できると便利です。
どうやらJavascriptでMQTTのメッセージが送れるようです。HtmlとJavaScriptならブラウザさえあれば、どの端末からでも送信できて便利そうですね。ちょっとそちらを検討してみます。
追記
作りました。
Amazon Echoからもコントロールできるようになりました。
さらにスマートホームスキルも作ってみました。
- アイリスオーヤマ(IRIS OHYAMA)