概要
PythonアプリからブラウザアプリへMQTTでメッセージ送信するための簡単なテストプログラムを作成し、動作確認した。
背景と目的
Raspberry Pi上のPythonアプリ側から、そのUI系として用いているクライアントサイドWebアプリ(以下、ブラウザアプリ)に対してプッシュ通信をする必要が出た。なので、MQTTでPython側からパブリッシュし、Webアプリ側でサブスクライブして受け取るというのを実装し、動作確認する。
詳細
0.構成、動作確認したいこと
- Rasbperry Pi上にMQTTブローカーを立てる
- Rasbperry Pi上のPythonアプリケーションからパブリッシュする
- (Raspberry Pi上のWebサーバーでホスティングし)別デバイスで表示しているブラウザアプリのjavascriptで、サブスクライブしてメッセージを受信
1.MQTTブローカー
Raspberry Piにブローカーをインストールするには、mosquittoというアプリがあるので、それを使う。
sudo apt-get install mosquitto
とする。起動の前に、WebSocketで通信できるようにするため、/etc/mosquitto/mosquitto.confに以下の2行を追記。これは、後述のjavascript用MQTTクライアントライブラリを動かすために必要。(WebSocketを使わないのであれば不要)
listener 1883 protocol websockets
編集完了したら、サービスを起動。
sudo systemctl start mosquitto
2.Pythonアプリ上でパブリッシュ
PythonでMQTTクライアントを扱うには、こちらを参考に、
pip3 install paho-mqtt
でインストール。
パブリッシュのサンプルスクリプトは、以下。1秒ごとにパブリッシュする。大事なのは、WebSocketで通信するため、Clientインスタンス作成時に引数transport="websockets"を与える部分。
client.loop_start()は、重要!2020/06/05追記
あるプログラムで、比較的長い周期でpublishする部分があったのだが、どうもしばらくすると動かなくなってしまった。接続自体は切れていないのにおかしいなと思ったら、どうやらclient.loop_start()をやるのを忘れていた。なぜ重要かというと、こちらのKQTT Keep Aliveの説明にあるように、PINGをブローカーと送りあっていて、その周期がKeepAliveIntervalすなわちconnectの第3引数なのだが、loop_startをさせないとどうやらこのping送信がされないようだ。すると、ある程度の時間たつとpublishできなくなってしまう。(内部的には切断のような状態なのだろう。) というわけで、loop_startは忘れないこと。 (なお、以下のサンプルは短い周期でpublishしているので、最悪なくても動いてしまう)
import paho.mqtt.client as mqtt import time def on_connect(client, userdata, flag, rc): """ ブローカーに接続できたときの処理 """ print("Connected with result code " + str(rc)) def on_disconnect(client, userdata, flag, rc): """ ブローカーが切断したときの処理 """ if rc != 0: print("Unexpected disconnection.") def on_publish(client, userdata, mid): """ publishが完了したときの処理 """ print("publish: {0}".format(mid)) if __name__ == '__main__': client = mqtt.Client(transport="websockets") # クラスのインスタンス(実体)の作成 client.on_connect = on_connect # 接続時のコールバック関数を登録 client.on_disconnect = on_disconnect # 切断時のコールバックを登録 client.on_publish = on_publish # メッセージ送信時のコールバック client.connect("localhost", 1883, 60) # 接続先は自分自身 # 通信処理スタート client.loop_start() # subはloop_forever()だが,pubはloop_start()で起動だけさせる # 永久に繰り返す while True: client.publish("topic/test", "Hello, World!") time.sleep(1)
3.ブラウザアプリ(javascript)でサブスクライブ
https://projects.eclipse.org/projects/iot.paho/downloadsから、javascript用のライブラリをダウンロード。
サブスクライブ動作確認用のコードを書いた。参考にしたのは、ダウンロード先のURLの下のほうについていたもの。HTML部分は省略するが、id="message"というdiv要素のテキストを、メッセージを受け取ってその内容に書き換えている。
// クライアントのインスタンスを作成 client = new Paho.MQTT.Client(RaspberryPiのIPアドレス, Number(1883), "ClientId"); // ハンドラのセット client.onConnectionLost = onConnectionLost; // 接続切断時 client.onMessageArrived = onMessageArrived; // メッセージ受信時 // 接続する client.connect({ onSuccess: onConnect }); // 接続完了時のコールバック function onConnect() { $("#status").text("onConnect"); // サブスクライブ開始 client.subscribe("topic/test"); } // 接続切断時 function onConnectionLost(responseObject) { if (responseObject.errorCode !== 0) { console.log("onConnectionLost:" + responseObject.errorMessage); } } // メッセージ受信時 function onMessageArrived(message) { $("#status").text("onMessageArrived"); $("#message").text(message.payloadString); }
4.動作確認
以下の通り、受け取ったメッセージが表示され、正しく通信できた。
まとめと今後の課題
Pythonアプリからのプッシュをブラウザアプリで受け取ることができた。