mosquittoでMQTTとWebSocket両方に対応させる
久々にMQTTブローカーサーバ使ってたら、mosquittoがMQTTとWebSocketに対応していたのでメモ
何が嬉しいか
今まではMQTTとWebSocketをバインドするためにNode.jsなどを使い、MQTTからWebSocketへ変換していました。例えばこんな感じに。 まぁこの頃はSocket.IOとか使ってたので、すでに古いのですが・・・
// WebSocket -> MQTT io.sockets.on('connection', function(socket) { socket.on('message', function(data) { // MQTTで送信 client.publish(PUSH_LOCATION, JSON.stringify(data.value)); }); }); // MQTT -> WebSocket client.on('message', function (topic, message) { // WebSocketで送信 io.sockets.emit('SYS_STATUS', message); });
これがmosquittoをWebSocketに対応させることでNode.jsは必要なく、nginx + HTML + JSで済むようになります。
画面をデザイナーさんに頼むときにNode.js動かさないと画面作れなかったりしたんですが、HTML + JSだとデザインする方もファイルだけなのでかなり楽になるんじゃないかと思います。
mosquittoの設定ファイルの修正
対応しているmosquittoのバージョンはver1.4.2以降のようです。Ubuntuではバイナリがすでに対応済みなのでそれを導入しますが、デフォルトではWebSocketには対応していません。
$ sudo apt-get install mosquitto
Ubuntuでaptでインストールした場合、設定ファイルは /etc/mosquitto/mosquitto.confになります。
# Place your local configuration in /etc/mosquitto/conf.d/ # # A full description of the configuration file is at # /usr/share/doc/mosquitto/examples/mosquitto.conf.example pid_file /var/run/mosquitto.pid persistence true persistence_location /var/lib/mosquitto/ log_dest file /var/log/mosquitto/mosquitto.log include_dir /etc/mosquitto/conf.d
これに以下の行を追加して、WebSocketに対応するようにします。
listener 1883 listener 9090 protocol websockets
WebSocketに対応させるにはprotocol websocketsだけで良いみたいですが、その場合MQTTが動作しなくなります。 この例ではMQTTは1883ポート、WebSocketは9090ポートで動作します。
設定ファイルを書き換えたら、サービスをリスタートさせます。
$ sudo service mosquitto restart
mosquittoのログ(/var/log/mosquitto/mosquitto.log)を見てみると、起動時にMQTTとWebSocketの両方が動作しているはずです。
1453293506: mosquitto version 1.4.7 (build date Tue, 22 Dec 2015 12:47:28 +0000) starting 1453293506: Config loaded from /etc/mosquitto/mosquitto.conf. 1453293506: Opening ipv4 listen socket on port 1883. 1453293506: Opening ipv6 listen socket on port 1883. 1453293506: Opening websockets listen socket on port 9090.
HTML + JS の記述例
動作環境はnginxにHTML + JSです。まずはHTMLファイル。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>TEST</title> <script type="text/javascript" src="mqttws31.js"></script> <script type="text/javascript" src="client.js"></script> </head> <body> </body> </html>
mqttw31.jsは Paho MQTTのJavaScriptクライアントです。こちらからダウンロードします。
次にJavaScriptファイル(client.js)の例です。サーバアドレスにはmqtt://やws://などのスキーマは不要です。ポートにmosquitto.confで追加したWebSocketの9090を指定しています。
// Create a client instance var client = new Paho.MQTT.Client("サーバアドレス", 9090 , "clientId" + new Date().getTime()); // set callback handlers client.onMessageArrived = onMessageArrived; // connect the client client.connect({onSuccess:onConnect}); // called when the client connects function onConnect() { // Once a connection has been made, make a subscription and send a message. console.log("onConnect"); client.subscribe("PUBLIC/log/#"); } // called when a message arrives function onMessageArrived(message) { console.log('payload: ' + message.payloadString); }
これで、他クライアントからMQTTでデータをブローカーサーバに送信すると、subscribeしていればデータを受信してペイロードを取得できます。
実際の動作としては、mqttw31.jsがMQTTクライアントとして公開されているので、WebSocketは関係なくて、ブラウザレベルでMQTTで接続できるんじゃないか?と思いますが、このMQTTクライアントは内部でWebSocketに変換しています。そのため、ブローカーがWebSocketに対応する必要があります。
これでNode.jsでサーバアプリ書かなくてもよくなるので、かなり楽になるんじゃないかなーと。。。
参考にしたサイト
たった1日で出来たWeb + Android + Arduinoのリアルタイム連携プロトタイピング
はじめに
今回のネタは、早稲田大学で行われているAndroidアプリ開発養成講座TechInstituteで、センサー回りの講座を受け持つことになり、Androidのセンサーを使った応用例として作成しました。
動作概要
Android
動作としては某L◯NEの「ふるふる」っぽい動作をAndroidでは行います。「加速度センサー」で、ある一定の加速度を超えたら、「GPSセンサー」で位置情報をサーバに送信します。Webサーバでは送信された位置情報をGoogleMapにマッピングします。
Arduino
Arduinoには何かセンサーを接続します。なんでもいいのですが、「照度センサー」とします。照度センサーにより、周囲が暗くなったらサーバに「暗くなった」ことを通知します。
Web
Webサーバは送られてきた位置情報のマッピングを行います。またブローカサーバがArduinoからのセンサー情報を、接続している全Android端末にPush配信を行います。
構成
ざっくりこんな感じです。
もうちょっと詳しく書くと
Androidアプリから、位置情報をMQTTプロトコルでブローカにpublishすることで、subscribeしているWebサーバがそれを受信して、GoogleMap上にマッピングします。Arduinoから「暗くなった」とブローカにpublishすると、Androidアプリでは事前にそのTopicをsubscribeしているようにしてあるので、Pushが飛んできます。※MQTTについてはこちらを参照
使っている技術は難しくありません。
- Androidアプリ:加速度センサー、GPSセンサー、MQTTクライアント
- Arduino:照度センサー、MQTTクライアント
- MQTTブローカサーバ:mosquitto
- Webサーバ:Node.js + WebSocket(Socket.IO) + MQTTライブラリ
それぞれの画面
Androidアプリ
スクリーンネームを入れて、スタートボタンを押して「振る」単純なアプリです。加速度センサーをチェックして、振れ幅が大きくなったら位置情報を送信します。
Arduino
UIは無いです。Cdsセルで周囲の照度を測定して、一定値以下になったら「暗くなった」と通知します。
AndroidにはPushで通知が飛んできます。
Web
Node.jsでWebSocketを使用して、送信された位置情報とスクリーンネームをマッピングします。
作成の流れ
- Androidのセンサー応用例のデモを作ろう
- ふるふるっぽいのなら、加速度センサーとGPSセンサーでできそうだな(ここまで前日案)
- 位置情報を表示するMapが欲しいな。んじゃMQTTを使ってNode.jsで受けるか
- MQTTブローカサーバ立てる
- PyhtonでMQTTのPubSubのサンプルを作成して動作を確認
- Node.js + Socket.IOでGoogleMap表示部作成
- MQTTライブラリをNode.jsに組み込んで、ブローカをSubscribeするようにする
- PythonでMQTTのサンプルのPublishを作成して、ダミーの位置情報をブローカに送信するとNode.js上のGoogleMap上にマッピングできることを確認
- Androidアプリで加速度センサーとGPSを組み合わせてアプリを作る
- MQTTライブラリを送信用に組み込んで、8で作成したtopicフォーマットに沿って送信するようにする
- Andoridアプリをふるふるすると、無事GoogleMapに表示できた\(^o^)/
- Arduinoも講義で扱うし、ArduinoにもMQTTクライアントがあるからこれも繋げてみよう
- 題材で使用する照度センサーとArduino Ethernetを組み合わせてMQTTでブローカに投げるようにする
- Androidアプリの方にArduinoの通知を受け取れるように、MQTTブローカへのSubscribeを追加
- ほぼ完成\(^o^)/
ここまででほぼ1日8時間くらいの作業という感じでした(途中買い物行ったりしてので、出来たのは夜中近く)
参考情報(Topicの扱い)
-
- subscribe "PUBLIC/location/port/all/#"
- publish "PUBLIC/location/state/hogefuga" ← hogefugaはスクリーンネームが入る
Node.js
- subscribe "PUBLIC/location/state/#"
-
- publish "PUBLIC/location/port/all/push"
※ArduinoのあたりのTopicの扱いが変だけど、最後取ってつけた関係上...
まとめ
ここに出てくる技術はひと通り経験したことがあるので、アイデアが出れば実装はさほど難しくありません。 だいたい1日の作業でこのくらいまでプロトタイピング実装できるんだなーと、あらためて今の技術の恩恵を感じました。逆に言えば、技術よりもアイデアの方が重要なのかも(こっちのが難しい)
さて、実装してみると色々展開できることが見えてきます。
MQTTなので上手くTopicのPublish/Subscribeのルールを決めてやることで
他にも
- ボタンでの通知を時間間隔での通知にして、ジオフェンスを使った遊びや監視?
- Arduino側をウェアラブルデバイスとしてアイデアを膨らますこともできそうです
- Arduino側を様々な家電に置き換えて考えると、コントローラ&通知システム作れますね
ソースコードは公開してもいいのですが、なんせ1日で作ったレベルなのでちょっと...
要望があれば整理して公開できるようにするかもしれません。
Mosquitto(MQTT)を動かしてみた
MQTTとは
MQTT(MQ Telemetry Transport : MQはMessage Queuing?)というプロトコルで、TCP/IP層で動作するWebSocketっぽいもの(ザックリ)。詳しくはそこはかとなく書くよん。を見てもらった方が分かりやすいです。簡単に言えば、M2Mでの使用を考えた軽量なメッセージプロトコルという感じです。Mosquittoはブローカの実装で使用します。
2013年のカンファレンスでは、2011年当時の「Beluga」(現Facebook Messenger)に使われているという発表があったようです
MQTTのブローカとクライアントの動作確認の構成
今回下記のような構成でMQTTの動作確認を行いました。
Mosquittoにsub.pyで接続し、pub.pyによってpublishされるとsub.py側に出力されることを確認します。
Mosquitto(ブローカ)のインストール
パッケージからインストール
Ubuntuでは基本的にはパッケージインストールができるので
$ sudo apt-get install mosquitto
以上でインストールと起動まで完了します。停止・再起動はserviceコマンドで行えます。
ソースからコンパイルする場合
開発環境のインストール
$ sudo apt-get install gcc make g++ libssl-dev libc-ares-dev
Mosquittoのダウンロードページからソースを落として展開し、makeしてインストールします。
$ wget http://mosquitto.org/files/source/mosquitto-1.3.1.tar.gz $ tar -xvzf http://mosquitto.org/files/source/mosquitto-1.3.1.tar.gz $ cd mosquitto-1.3.1 $ make $ sudo make install
環境設定ファイルを設定します。
$ sudo cp /etc/mosquitto/mosquitto.conf.example /etc/mosquitto/mosquitto.conf
デフォルトでMQTTは1883ポートを使用します。必要に応じてサーバのポートを開放します。
起動します。
※引数なしの起動方法では前述のconfファイルは使われず、デフォルトで起動します。-cオプションでファイルを指定する必要がありますが、何も修正していなければ動作はデフォルトとなります。
$ mosquitto 1397719018: mosquitto version 1.3.1 (build date 2014-04-17 15:13:13+0900) starting 1397719018: Using default config. 1397719018: Opening ipv4 listen socket on port 1883. 1397719018: Opening ipv6 listen socket on port 1883.
この場合、サーバがIPv4/6両対応なので、ポートそれぞれでリッスンしている状態になります。
PahoクライアントでPub/Subを実行する
Pahoの下の方に各言語用のダウンロードリンクがありますので、そこからcloneしてきて利用するのがいいのと思います。今回はPythonクライアントのexampleを少し修正してsub.pyとpub.pyを作成しました。
paho MQTT pythonライブラリのインストール
$ sudo pip install paho-mqtt
sub.pyの実行
詳しい解説は後回しにしてまずは実行してみましょう。まずは待ち受けのsub.pyを実行しておきます。
コードはsub.pyにあります。
起動して、ブローカサーバに接続するとこのようなメッセージになります。
$ python sub.py rc: 0 Subscribed: 1 (0,)
ブローカサーバへの接続が失敗した場合こんなメッセージが出ます。
Traceback (most recent call last): File "sub.py", line 60, in <module> mqttc.connect("HOST", 1883, 60) File "/Library/Python/2.7/site-packages/paho/mqtt/client.py", line 588, in connect return self.reconnect() File "/Library/Python/2.7/site-packages/paho/mqtt/client.py", line 710, in reconnect self._sock = socket.create_connection((self._host, self._port), source_address=(self._bind_address, 0)) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 571, in create_connection raise err socket.error: [Errno 61] Connection refused
接続に成功していればブローカサーバ(Mosquitto)の方では以下のようなメッセージが表示されているはずです
1397719236: New connection from 2001:3e0:0:*:*:*:*on port 1883. 1397719236: New client connected from 2001:3e0:0:*:*:*:* as paho/E917914937EA634F0F (c1, k60).
一部"*"でマスクしています。
pub.pyの実行
次にpub.pyを実行して、メッセージを送信します。
コードはpub.pyにあります。
コンソールをもう一つ開いてpub.pyを実行すると直ぐに終了します。
$ python pub.py 1
sub.pyの出力の方を見てみると、1行メッセージが追加されていると思います。
rc: 0 Subscribed: 1 (0,) message 0 Hello MQTT!
pub.pyを実行することにより、subscribeしていたsub.pyの方にメッセージ送信が行われたことがわかります。
解説
sub.pyの方から見ていきます。(以下抜粋)
# メッセージを受信した時に呼ばれる。 def on_message(mqttc, obj, msg): print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload)) # 接続先のブローカを設定する mqttc.connect("HOST NAME", 1883, 60) # "message"というtopicをsubscribeする mqttc.subscribe("message", 0)
sub.pyは起動すると"message"というtopicが来るまで待機状態になります。pub.pyを実行して。メッセージを受信すると、topicとqosとpayloadを表示します。今の場合は
- topic : message
- qos: 0
- payload : Hello MQTT!
pub.pyの方はこれだけですね。
publish.single("message", "Hello MQTT!", hostname="HOST NAME")
"message"というtopicに"Hello MQTT!"を送信しています。qosは省略されていますが、デフォルトが0になっています。今回は簡単にするためにtopcを"message"としましたが、これを"$SYS/#"のようにワイルドカードで指定したり、"paho/test/single"のようにすることもできます。この指定の仕方はMQTTの仕様のようで、これを利用して各々のM2M機器がsubscribeする対象を設定できるようになっています。
まとめ
MQTTを実動作させるテストを行いました。MQTTの実装はLinuxやMacだけでなく、mbedやArduino、Androidなどにもありますし、様々な言語での実装もあります。またNode.jsでWebSocketと組み合わせてブラウザへの出力もできます(この辺は後日)僕はこのM2M用途としてのMQTTはよく知らなかったのですが、実際使用してみると結構いろいろなことが出来そうな気がします。プロトコルはシンプルだし、QoSの概念が導入されているのも面白い点です。