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配信を行います。

構成

ざっくりこんな感じです。

f:id:tomo_watanabe:20140820234129p:plain

もうちょっと詳しく書くと

f:id:tomo_watanabe:20140820234656p:plain

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アプリ

スクリーンネームを入れて、スタートボタンを押して「振る」単純なアプリです。加速度センサーをチェックして、振れ幅が大きくなったら位置情報を送信します。

f:id:tomo_watanabe:20140820235916p:plain

Arduino

UIは無いです。Cdsセルで周囲の照度を測定して、一定値以下になったら「暗くなった」と通知します。

f:id:tomo_watanabe:20140821101028j:plain

AndroidにはPushで通知が飛んできます。

f:id:tomo_watanabe:20140821101840p:plain

Web

Node.jsでWebSocketを使用して、送信された位置情報とスクリーンネームマッピングします。

f:id:tomo_watanabe:20140821000241p:plain

作成の流れ

  1. Androidのセンサー応用例のデモを作ろう
  2. ふるふるっぽいのなら、加速度センサーとGPSセンサーでできそうだな(ここまで前日案)
  3. 位置情報を表示するMapが欲しいな。んじゃMQTTを使ってNode.jsで受けるか
  4. MQTTブローカサーバ立てる
  5. PyhtonでMQTTのPubSubのサンプルを作成して動作を確認
  6. Node.js + Socket.IOでGoogleMap表示部作成
  7. MQTTライブラリをNode.jsに組み込んで、ブローカをSubscribeするようにする
  8. PythonでMQTTのサンプルのPublishを作成して、ダミーの位置情報をブローカに送信するとNode.js上のGoogleMap上にマッピングできることを確認
  9. Androidアプリで加速度センサーとGPSを組み合わせてアプリを作る
  10. MQTTライブラリを送信用に組み込んで、8で作成したtopicフォーマットに沿って送信するようにする
  11. Andoridアプリをふるふるすると、無事GoogleMapに表示できた\(^o^)/
  12. Arduinoも講義で扱うし、ArduinoにもMQTTクライアントがあるからこれも繋げてみよう
  13. 題材で使用する照度センサーとArduino Ethernetを組み合わせてMQTTでブローカに投げるようにする
  14. Androidアプリの方にArduinoの通知を受け取れるように、MQTTブローカへのSubscribeを追加
  15. ほぼ完成\(^o^)/

ここまででほぼ1日8時間くらいの作業という感じでした(途中買い物行ったりしてので、出来たのは夜中近く)

参考情報(Topicの扱い)

  • Android

    • subscribe "PUBLIC/location/port/all/#"
    • publish "PUBLIC/location/state/hogefuga" ← hogefugaはスクリーンネームが入る
  • Node.js

    • subscribe "PUBLIC/location/state/#"
  • Arduino

    • 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
  • クライアント:Paho Python版クライアント

f:id:tomo_watanabe:20140418232239p:plain

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の実装はLinuxMacだけでなく、mbedやArduinoAndroidなどにもありますし、様々な言語での実装もあります。またNode.jsでWebSocketと組み合わせてブラウザへの出力もできます(この辺は後日)僕はこのM2M用途としてのMQTTはよく知らなかったのですが、実際使用してみると結構いろいろなことが出来そうな気がします。プロトコルはシンプルだし、QoSの概念が導入されているのも面白い点です。