obnizとNode-REDをつなげるためにMQTT over WebSocketを使ったメモ

この記事は Node-RED Advent Calendar 2018 1日目です。

いよいよスタートです!参加される、Node-RED UG のみなさまよろしくおねがいします!

今回のネタ

image

さて、今回のネタはobniz×M5Stack×NefryBT三つ巴Meetup!【ESP32開発ボードLT祭】 – connpassで使った、obnizとNode-REDをつなげるためにMQTT over WebSocketを使ったメモです。

なぜ、MQTT over WebSocketを使いたいのか?

このイベントでは3つのデバイスを動かす必要がありました、そのときに、MQTTでIoTを動かしやすい状況でしたので、obnizクラウドではフロントエンドだけWebSocketを連携しても良かったんですが、技術的挑戦として「MQTTで全部揃えてしまいたい!」というものがありました。

image

これを実現するためにMQTT over WebSocketです。

初めての MQTTにあるように、

MQTT over WebSocket
MQTT はプロトコル、つまり通信規格の一つで基本的には TCP/IP 上での動作を前提としています。

ただし、WebSocket 上でも動作させることが出来ます。つまりブラウザ上で MQTT を使える用になります。

もともはセンサーや、何かしらのリアルタイムな情報を WebSocket を使ってブラウザに実際にグラフを作ったりするのが目的で使われ始めた機能のようです。

実際、管理画面側でのブラウザで見れるグラフを作成するのにとても向いている用です。

ということで、こちらを採用してMQTTの作法に揃えることにしました。

Node-REDはさくらのクラウドで用意

Node-REDはさくらのクラウド用意しています。最近の展示ごとでは、かなり重宝しています。

なぜ、さくらのクラウドかと言うとスタートアップスクリプトでNode-REDが揃っていて使いやすいからです。

sakura-internet/cloud-startupscripts: さくらのクラウド スタートアップスクリプト用リポジトリ

実際、CentOSにインストール後、使えるようにするには、フロントエンジニアの私としては一息で作れるものではないので、こういうスクリプトはありがたいですね!

さくらのクラウドにNode-REDでMQTTブローカーを立てる

ということで、さくらのクラウドにNode-REDでMQTTブローカーを立てます。

詳しくはこちらの記事をご参考ください。

さくらのクラウドのスタートアップスクリプトでNode-REDをインストールするメモ – 1ft-seabass.jp.MEMO

現状では、さらにIDとパスワードも設定できるようにもなっています。このあたり、PllRequestでちょっと私もサポートさせていただきました!

私は、CentOS7のイメージでインストールしています。

Node-REDのフローの準備

サーバーが出来上がったら、Node-REDのフローの準備です。MQTTブローカーのノード(node-red-contrib-mqtt-broker)があるのでこちらでシンプルに構成します。

image

詳しい node-red-contrib-mqtt-broker の使い方は、Choregraphe入っているPCにNode-REDを入れてPepperとMQTT連携するメモにも書いてありますのでご参考ください。

注意点としてはMQTTとMQTT over WebSocket用のポートを開ける必要がある

さて、MQTTブローカーもこれで立てれたのですが、さくらのクラウドでは注意点があります。

image

MQTTブローカーノードの設定を見てみますとポートがわかります。つまり、MQTT 1880番とMQTT over WebSocket用の8080番のポートを開ける必要があります。

ここは、さくらのクラウドでサーバーに入って、SSHターミナルからfirewall-cmdでポートを開けてやります。

firewall-cmd --add-port=8080/tcp --permanent

まず、MQTT over WebSocketの8080ポートを開ける設定をします。successと出たら成功です。

firewall-cmd --add-port=1883/tcp --permanent

まず、つづいてMQTTの1883ポートを開ける設定をします。successと出たら成功です。

firewall-cmd --reload

最後にファイアウォールを再起動して設定は完了です。

obnizクラウドのソースコード

obnizクラウドではこのようにソースコードを使います。

MQTT_OVER_WEBSOCKET_SERVER_URLは今回立てた、さくらのクラウドのサーバーのURLを指定しましょう。

<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@1.13.1/obniz.js" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.2/mqttws31.min.js"></script>
</head>

<body>

  <div id="obniz-debug"></div>
  <h1>obniz instant html</h1>
  <button id="on">ON</button>
  <button id="off">OFF</button>
  <div id="print"></div>

  <script>
    var obniz = new Obniz("OBNIZ_ID_HERE");
    obniz.onconnect = async function () {

      // Create a client instance
      console.log("Create a client instance");
      var client = new Paho.MQTT.Client('MQTT_OVER_WEBSOCKET_SERVER_URL', Number(8080), "obniz001");

      // set callback handlers
      client.onConnectionLost = onConnectionLost;
      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('/sub/obniz');
        var message = new Paho.MQTT.Message(JSON.stringify({ "message": "Hello obniz" }));
        message.destinationName = "/pub/obniz";
        client.send(message);
      }

      // called when the client loses its connection
      function onConnectionLost(responseObject) {
        if (responseObject.errorCode !== 0) {
          console.log("onConnectionLost:" + responseObject.errorMessage);
        }
      }

      // called when a message arrives
      function onMessageArrived(message) {
        console.log("onMessageArrived:" + message.payloadString);

        // console.log('Message from server ', event.data);
        var json = JSON.parse(message.payloadString);
        console.log('Message from server ', json.message);

        obniz.display.clear();
        obniz.display.print(json.message);
      }

      obniz.display.clear();
      obniz.display.print("Hello World");

      obniz.switch.onchange = function (state) {
        if (state === "push") {
          obniz.display.clear();
          obniz.display.print("push");
          var sendData = {
            "flag": "push"
          };
          var message = new Paho.MQTT.Message(JSON.stringify(sendData));
          message.destinationName = "/pub/obniz";
          client.send(message);
        } else if (state === "right") {
          obniz.display.clear();
          obniz.display.print("right");
          var sendData = {
            "flag": "right"
          };
          var message = new Paho.MQTT.Message(JSON.stringify(sendData));
          message.destinationName = "/pub/obniz";
          client.send(message);
        } else if (state === "left") {
          obniz.display.clear();
          obniz.display.print("left");
          var sendData = {
            "flag": "left"
          };
          var message = new Paho.MQTT.Message(JSON.stringify(sendData));
          message.destinationName = "/pub/obniz";
          client.send(message);
        } else {
          obniz.display.clear();
          obniz.display.print("--");
        }
      }


      $('#on').click(function () {
        obniz.display.clear();
        obniz.display.print("ON");
      });
      $('#off').click(function () {
        obniz.display.clear();
        obniz.display.print("OFF");
      });
    }

  </script>
</body>

</html>

仕組みとしては、paho-mqttのフロントエンドライブラリをCDN経由で呼び出してあげて、Paho.MQTT.Messageでやりとりをしています。MQTT over WebSocketでは、publish関数ではなくてPaho.MQTT.MessageでdestinationNameという形でトピックを指定しています。

ちょっとここは言葉はtopicとなっていなかったので戸惑いましたが、こういうものかな?と思えば、慣れればなんとかなるかなというレベルです。

実際に動かしてみる

ということで自在に動かしてみるとこうなります。

image

obnizのボタンを左右に動かしたり押したりすると、Node-REDでメッセージが飛びます。

image

Node-REDからメッセージを送ると、obnizでメッセージが表示されます。

ということで、フロントエンドでもMQTT連携ができて、プロトコルが揃ってスッキリですね!

obnizクラウドではフロントエンドなJavaScriptやHTML5の実装ができるので、こういった連携をまた進めてみようと思います!

明日は

arrowmeiwaracing さんの「ダイソーBluetoothリモコンシャッター用ノードを作ったよ」です!

楽しみです!