Node.js＋MQTT.js＋Mosca を使ってみた

# はじめに

最近になって Raspberry Pi を使い始めました。
Raspberry Pi とサーバで通信するのに MQTT プロトコルを使うことにしました。
また、Raspberry Pi 、サーバともに Node.js で書きたいと思いました。

# MQTT とは

MQTT とは、IoT や M2M のためのメッセージ転送プロトコルです。
多数のデバイスの間で短いメッセージを頻繁に送受信することを想定しています。
プロトコルヘッダが小さく、通信量、CPU 負荷、電力消費などを抑えることができます。

メッセージの送信側を、Publisher (発行者) と呼びます。
メッセージの受信側は、Subscriber (購読者) です。Subscriber は Publisher に対してメッセージの購読を予め希望します。
このとき Publisher と Subscriber は直接通信でなく、それぞれ Broker (仲介者) に接続します。Publisher と Subscriber はクライアント、Broker はサーバに位置づけられます。
Publisher と Subscriber は１対１だけでなく、容易に１対多、多対多に対応できます。
Publisher が発行するメッセージは、トピックをつけて送信します。Subcriber は、トピックを指定して購読を希望します。

# MQTT.js を使う

MQTT.js は、MQTT クライアントのためのライブラリです。Node.js およびブラウザの JavaScript で使用できます。

公式 https://www.npmjs.com/package/mqtt

まず Node.js で使用してみます。

* Node.js 8.1.4
* mqtt.js 2.9.2

## ワークスペースを作る

## インストールする

```
$ npm install mqtt
```

## Publisher の実装

```js:publisher.js
var mqtt = require('mqtt');
var client = mqtt.connect('mqtt://mosquitto.org');

client.on('connect', function(){
    console.log('publisher.connected.');
});

setInterval(function(){
    var message = Date.now().toString()/
    client.publish('topic0', message);
    console.log('publisher.publish:', message);
}, 1000);
```

まず、```mqtt.connect()``` で、インスタンスを生成すると同時にサーバ (Broker) に接続します。
サーバ (Broker) は、既存のサービス (mqtt://mosquitto.org) を利用させて貰います。
続いて、```client.publish()``` で、サーバ (Broker) にメッセージ送信します。このときトピックを指定します。
ここでは setInterval() を使ってループしています。Ctrl+C で終了させて下さい。

## Subscriber の実装

```js:subscriber.js
var mqtt = require('mqtt');
var client = mqtt.connect('mqtt://mosquitto.org');

client.on('connect', function(){
    console.log('subscriber.connected.');
});

client.subscribe('topic0', function(err, granted){
    console.log('subscriber.subscribed.');
});

client.on('message', function(topic, message){
    console.log('subscriber.on.message', 'topic:', topic, 'message:', message.toString());
});

```

まず、```mqtt.connect()``` で、インスタンスを生成すると同時にサーバ (Broker) に接続します。
サーバ (Broker) は、既存のサービス (mqtt://mosquitto.org) を利用させて貰います。
続いて、```client.subscribe()``` で、サーバ (Broker) に購読を希望します。このときトピックを指定します。前述の Publisher の実装と合わせて下さい。 
サーバ (Broker) からメッセージが届くと ```client.on('messege',``` で受信できます。

## 実行する

```
$ node publisher.js
$ node subscriber.js
```

# Mosca を使う

Mosca は、MQTT broker のためのライブラリです。Node.js で使用できます。

公式 http://www.mosca.io/

* Node.js 8.1.4
* Mosca 2.5.2

## ワークスペースを作る

## インストールする

```
$ npm install mosca
```

## Broker の実装

```js:broker.js
var mosca = require('mosca');
var server = new mosca.Server({
    port: 1883,
});

server.on('ready', function(){
    console.log('Server is ready.');
});
```

まず、```new mosca.Server()``` でインスタンス生成します。
```port``` は任意です。
これで、Publisher からメッセージを受信し、Subscriber にメッセージを転送できるようになっています。

```js:broker.js
server.on('clientConnected', function(client){
    console.log('broker.on.connected.', 'client:', client.id);
});

server.on('clientDisconnected', function(client){
    console.log('broker.on.disconnected.', 'client:', client.id);
});

server.on('subscribed', function(topic, client){
    console.log('broker.on.subscribed.', 'client:', client.id, 'topic:', topic);
});

server.on('unsubscribed', function(topic, client){
    console.log('broker.on.unsubscribed.', 'client:', client.id); 
});

server.on('published', function(packet, client){
    if (/\/new\//.test(packet.topic)){
        return;
    }
    if (/\/disconnect\//.test(packet.topic)){
        return;
    }
    console.log('broker.on.published.', 'client:', client.id);
});
```

Publisher および Subscriber が接続すると ```server.on('clientConnected',``` で確認できます。なくても構いません。
Subscriber が購読希望すると ```server.on('subscribed',``` で確認できます。なくても構いません。
Publiser がメッセージ送信すると ```server.on('published',``` で確認できます。なくても構いません。Publisher や Subscriber が接続したときも発火するようです。なので ```packet.topic``` を確認します。

## Publisher の実装

前述のコードを若干変更します。

```js:publisher.js
var client = mqtt.connect({
    host: 'localhost',
    port: 1883,
    clientId: 'mqtt.publisher',
});
```

```host``` および ```port``` は、前述の Broker の実装に合わせて下さい。
```clientId``` は、指定しなければ自動でセットされます。ここでは確認のため指定しています。

## Subscriber の実装

前述のコードを若干変更します。

```js:subscriber.js
var client = mqtt.connect({
    host: 'localhost',
    port: 1883,
    clientId: 'mqtt.subscriber',
});
```

```host``` および ```port``` は、前述の Broker の実装に合わせて下さい。
```clientId``` は、指定しなければ自動でセットされます。ここでは確認のため指定しています。

## 実行する

```
$ node publisher.js
$ node subscriber.js
$ node broker.js
```

# MQTT クライアントをブラウザで動かす

## MQTT.js を使う

MQTT.js は、MQTT クライアントのためのライブラリで、Node.js およびブラウザの JavaScript で使用できます。

ブラウザで動くプログラムを作ってみます。

## インストールする

今回は CDN を利用します。
https://unpkg.com/mqtt/dist/mqtt.min.js

## Publisher の実装

```html:publisher.html
<html>
<head>
    <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
</head>
<body>
    <script>
        var client = mqtt.connect({
            host: 'localhost',
            port: 3000,
            clientId: 'http.publisher'
        });
    </script>

    <div id='buttonPublish' v-on:click='doPublish'>[発行する]</div>

    <script>
        new Vue({
            el: '#buttonPublish',
            methods: {
                doPublish: function(){
                    client.publish('topic0', Date.now().toString());
                }
            }
        });
    </script>
</body>
</html>
```

```mqtt.connect()``` で、インスタンスを生成すると同時にサーバ (Broker) に接続します。
```host``` および ```port``` は、後述する Broker の実装に合わせて下さい。
```clientId``` は、指定しなければ自動でセットされます。ここでは確認のため指定しています。
```client.publish()``` で、サーバ (Broker) にメッセージ送信します。このときトピックを指定します。

ボタン押下時の処理は Vue.js を使っています。別のフレームワークでも構いません。

## Subscriber の実装

```html:subscriber.html
<html>
<head>
    <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
</head>
<body>
    <script>
        var client = mqtt.connect({
            host: 'localhost',
            port: 3000,
            clientId: 'http.subscriber'
        });

        client.subscribe('topic0');

        var data = {};
        data.messages = [];

        client.on('message', function(topic, payload){
            var message = String.fromCharCode.apply(null, payload);
            console.log(message);
            data.messages.push(message);
        });
    </script>

    <ul id='listMessage'>
        <li v-for='message in messages'>
            {{ message }}
        </li>
    </ul>

    <script>
        new Vue({
            el: '#listMessage',
            data: data
        });
    </script>
</body>
</html>
```

```mqtt.connect()``` で、インスタンスを生成すると同時にサーバ (Broker) に接続します。
```host``` および ```port``` は、後述する Broker の実装に合わせて下さい。
```clientId``` は、指定しなければ自動でセットされます。ここでは確認のため指定しています。
```client.subscribe()``` で、サーバ (Broker) に購読を希望します。このときトピックを指定します。前述の Publisher の実装と合わせて下さい。 
サーバ (Broker) からメッセージが届くと ```client.on('messege',``` で受信できます。

受信結果を表示する処理は Vue.js を使っています。別のフレームワークでも構いません。

## 実行する

```publisher.html``` および ```subscriber.html``` をブラウザで開きます。

# サーバをブラウザの MQTT クライアントに対応させる

## Mosca を使う

Mosca は、MQTT broker のためのライブラリで、Node.js で使用できます。

## Broker の実装

前述のコードを利用します。以下のコードを追加します。

```js:broker.js
var http = require('http');
var httpServer = http.createServer();
server.attachHttpServer(httpServer);
httpServer.listen(3000);
```

```server``` は、従来の MQTT サーバのインスタンスです。```attachHttpServer()``` で HTTP サーバのインスタンス ```httpServer``` と結びつけます。
```httpServer.listen()``` で指定するポート番号は、前述のクライアントの実装に合わせて下さい。

## 実行する

```
$ node publisher.js
$ node subscriber.js
$ node broker.js
```

ブラウザの Publisher でメッセージ送信すると、ブラウザの Subscriber でメッセージ受信します。同時に Node.js の Subscriber でも従来通りメッセージ受信します。
Node.js の Publisher で送信されたメッセージも、ブラウザの Subscriber および Node.js の Subscriber で受信され表示されます。