wonder gadget

ガジェットはインターネットの夢をみるか? Intel edisonを中心に IoT, MAKE, Physical Computingしていきます。

WICEDのデータ転送にMQTTを利用してみる

WICED Senseからのデータが取れたので、次はそのデータをサーバーに送る方法を検討します。

MQTT as a Service sango MQTTとは、Message Queueing Telemetry Transport の略です。

Message Queueing は、送信側が送るデータをキューと呼ばれるデータ領域に一旦保持し、受信側の処理が完了するのを待たずに次の処理へ移る方式です。Telemetry Transport は、日本語に訳せば遠隔測定用の通信といったところでしょう。つまりMQTTは、遠隔にある測定用のセンサーやデバイスなどが収集したデータを、受信側の処理状況を気にせず効率よく送信することができるプロトコルです。

HTTPで送ってもいいのですが、秒間約10ものデータが来るようなセンサーなので、HTTPだとすぐに破綻しそうです。調べてみるとMQTTなるものを使うのが良さそうということが分かりました。 MQTTはサービスとして提供されているsangoを利用するのも良いと思います。

MQTT as a Service sango

ライトプランであれば無料なので、とりあえず試してみることもできます。

なんとなく自分のMacの中で全てを完結できないかと別のものを調べてみました。

Apache Apolloを入れることにしました。Homebrewを使いたくないので、Downloadページからダウンロードして解凍して、適当なディレクトリに設置します。

$ cd apache-apollo-1.7
$ bin/apollo create mybroker
$ mybroker/bin/apollo-broker run

解凍したらブローカーを作ればApolloを実行することができます。ApolloはJavaで書かれているため、Javaが入っていることが前提となります。

f:id:daikimura:20150206000033p:plain

ブラウザで https://127.0.0.1:61681/ にアクセスするとログイン画面が開きます。 ID/Passはデフォルトでは admin/password です。 ログインするとApollo Consoleが開きます。

次にCylonのコードにMQTTの配信側(パブリッシャー)のコードを足します。

var Cylon = require('cylon');
var date = require('date-utils');
var sprintf = require('sprintf-js').sprintf;
var mqtt = require('mqtt')
  , client = mqtt.connect('mqtt://admin:password@127.0.0.1:61613');

Cylon.robot({
  connection: {
    name: 'bluetooth',
    adaptor: 'central',
    module: 'cylon-ble',
    uuid: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
  },

  devices: [
    {name: 'battery', driver: 'ble-battery-service'},
    {name: 'deviceInfo', driver: 'ble-device-information'},
    {name: 'generic', driver: 'ble-generic-access'},
    {name: 'wiced', driver: 'wiced-sense'},
  ],

  display: function(err, data) {
    if (err) {
      console.log("Error:", err);
    } else {
      var dt = new Date();
      data["date"] = dt.toFormat("YYYY-MM-DDTHH24:MI:SS.") + sprintf("%03dZ", dt.getMilliseconds());
      console.log(" Data:", data);
      client.publish('WICED Sense', JSON.stringify(data));
    }
  },

  work: function(my) {
    my.generic.getDeviceName(function(err, data) {
      my.generic.getAppearance(function(err, data) {
        my.deviceInfo.getManufacturerName(function(err, data) {
          my.battery.getBatteryLevel(function(err, data){
            my.wiced.getData(function(err, data) {
              my.display(err, data);
            });
          });
        });
      });
    });
  }
}).start();

今までdisplayで表示をしていたところにclient.publishを足しています。dataに日付も加えてみました。 MQTTは文字列で送るようなので、JSONに変換しています。

次に受信側(サブスクライバー)のコードを書きます。

var mqtt = require('mqtt')
  , client = mqtt.connect('mqtt://admin:password@127.0.0.1:61613');

client.subscribe('WICED Sense');

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

こちらは極めてシンプルで受け取ったものを画面に出すだけです。 それでは実行してみます。

まずはパブリッシャーの実行結果です。

I, [2015-02-05T15:24:57.772Z]  INFO -- : Initializing connections.
W, [2015-02-05T15:24:57.773Z]  WARN -- : Specifying a single connection with the 'connection' key is deprecated. It will be removed in 1.0.0.
I, [2015-02-05T15:24:57.845Z]  INFO -- : Initializing devices.
W, [2015-02-05T15:24:57.845Z]  WARN -- : Specifying devices as an array is deprecated. It will be removed in 1.0.0.
I, [2015-02-05T15:24:57.852Z]  INFO -- : Starting connections.
I, [2015-02-05T15:25:16.223Z]  INFO -- : Starting devices.
I, [2015-02-05T15:25:16.224Z]  INFO -- : Working.
 Data: { accelerometer: { x: 2, y: 1, z: 87 },
  gyroscope: { x: -1789, y: 607, z: -5673 },
  magnetometer: { x: -562, y: 232, z: -249 },
  date: '2015-02-06T00:25:17.247Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 87 },
  gyroscope: { x: -2224, y: 791, z: -5460 },
  magnetometer: { x: -539, y: 237, z: -264 },
  date: '2015-02-06T00:25:17.449Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 87 },
  gyroscope: { x: -1865, y: 653, z: -4585 },
  magnetometer: { x: -561, y: 223, z: -238 },
  date: '2015-02-06T00:25:17.450Z' }
 Data: { accelerometer: { x: 2, y: 1, z: 87 },
  gyroscope: { x: -1596, y: 528, z: -3837 },
  magnetometer: { x: -555, y: 227, z: -256 },
  date: '2015-02-06T00:25:17.451Z' }
 Data: { accelerometer: { x: 2, y: 1, z: 86 },
  gyroscope: { x: -2606, y: 886, z: -6156 },
  magnetometer: { x: -552, y: 225, z: -248 },
  date: '2015-02-06T00:25:17.517Z' }
 Data: { accelerometer: { x: 2, y: 2, z: 87 },
  gyroscope: { x: -1797, y: 669, z: -4386 },
  magnetometer: { x: -557, y: 232, z: -256 },
  date: '2015-02-06T00:25:17.584Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 87 },
  gyroscope: { x: -1783, y: 596, z: -4318 },
  magnetometer: { x: -541, y: 227, z: -251 },
  date: '2015-02-06T00:25:17.652Z' }
 Data: { accelerometer: { x: 3, y: 2, z: 87 },
  gyroscope: { x: -2257, y: 873, z: -6399 },
  magnetometer: { x: -559, y: 243, z: -253 },
  date: '2015-02-06T00:25:17.922Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 87 },
  gyroscope: { x: -2218, y: 790, z: -5489 },
  magnetometer: { x: -564, y: 230, z: -255 },
  date: '2015-02-06T00:25:17.922Z' }
 Data: { humidity: 496,
  pressure: 10050,
  temperature: 270,
  date: '2015-02-06T00:25:17.924Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 87 },
  gyroscope: { x: -1700, y: 563, z: -4059 },
  magnetometer: { x: -562, y: 229, z: -248 },
  date: '2015-02-06T00:25:17.924Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 86 },
  gyroscope: { x: -2177, y: 724, z: -5086 },
  magnetometer: { x: -542, y: 231, z: -255 },
  date: '2015-02-06T00:25:17.989Z' }
 Data: { accelerometer: { x: 2, y: 1, z: 86 },
  gyroscope: { x: -1777, y: 638, z: -4314 },
  magnetometer: { x: -556, y: 240, z: -261 },
  date: '2015-02-06T00:25:18.124Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 86 },
  gyroscope: { x: -1675, y: 540, z: -3980 },
  magnetometer: { x: -538, y: 221, z: -254 },
  date: '2015-02-06T00:25:18.192Z' }
 Data: { accelerometer: { x: 2, y: 1, z: 87 },
  gyroscope: { x: -1575, y: 879, z: -6174 },
  magnetometer: { x: -545, y: 225, z: -256 },
  date: '2015-02-06T00:25:18.193Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 86 },
  gyroscope: { x: -2109, y: 751, z: -5109 },
  magnetometer: { x: -557, y: 244, z: -264 },
  date: '2015-02-06T00:25:18.260Z' }
 Data: { accelerometer: { x: 2, y: 1, z: 86 },
  gyroscope: { x: -1553, y: 547, z: -3722 },
  magnetometer: { x: -556, y: 230, z: -257 },
  date: '2015-02-06T00:25:18.327Z' }
 Data: { accelerometer: { x: 2, y: 1, z: 87 },
  gyroscope: { x: -1628, y: 571, z: -6020 },
  magnetometer: { x: -556, y: 226, z: -263 },
  date: '2015-02-06T00:25:18.462Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 86 },
  gyroscope: { x: -2236, y: 824, z: -5601 },
  magnetometer: { x: -556, y: 227, z: -262 },
  date: '2015-02-06T00:25:18.462Z' }
 Data: { humidity: 496,
  pressure: 10053,
  temperature: 270,
  date: '2015-02-06T00:25:18.529Z' }
 Data: { accelerometer: { x: 2, y: 1, z: 86 },
  gyroscope: { x: -1539, y: 532, z: -3646 },
  magnetometer: { x: -541, y: 233, z: -257 },
  date: '2015-02-06T00:25:18.597Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 86 },
  gyroscope: { x: -2498, y: 901, z: -6201 },
  magnetometer: { x: -547, y: 233, z: -258 },
  date: '2015-02-06T00:25:18.664Z' }
 Data: { accelerometer: { x: 3, y: 1, z: 87 },
  gyroscope: { x: -1806, y: 647, z: -4368 },
  magnetometer: { x: -549, y: 232, z: -253 },
  date: '2015-02-06T00:25:18.732Z' }

次にサブスクライバーの実行結果です。

WICED Sense: {"accelerometer":{"x":2,"y":1,"z":87},"gyroscope":{"x":-1789,"y":607,"z":-5673},"magnetometer":{"x":-562,"y":232,"z":-249},"date":"2015-02-06T00:25:17.247Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":87},"gyroscope":{"x":-2224,"y":791,"z":-5460},"magnetometer":{"x":-539,"y":237,"z":-264},"date":"2015-02-06T00:25:17.449Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":87},"gyroscope":{"x":-1865,"y":653,"z":-4585},"magnetometer":{"x":-561,"y":223,"z":-238},"date":"2015-02-06T00:25:17.450Z"}
WICED Sense: {"accelerometer":{"x":2,"y":1,"z":87},"gyroscope":{"x":-1596,"y":528,"z":-3837},"magnetometer":{"x":-555,"y":227,"z":-256},"date":"2015-02-06T00:25:17.451Z"}
WICED Sense: {"accelerometer":{"x":2,"y":1,"z":86},"gyroscope":{"x":-2606,"y":886,"z":-6156},"magnetometer":{"x":-552,"y":225,"z":-248},"date":"2015-02-06T00:25:17.517Z"}
WICED Sense: {"accelerometer":{"x":2,"y":2,"z":87},"gyroscope":{"x":-1797,"y":669,"z":-4386},"magnetometer":{"x":-557,"y":232,"z":-256},"date":"2015-02-06T00:25:17.584Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":87},"gyroscope":{"x":-1783,"y":596,"z":-4318},"magnetometer":{"x":-541,"y":227,"z":-251},"date":"2015-02-06T00:25:17.652Z"}
WICED Sense: {"accelerometer":{"x":3,"y":2,"z":87},"gyroscope":{"x":-2257,"y":873,"z":-6399},"magnetometer":{"x":-559,"y":243,"z":-253},"date":"2015-02-06T00:25:17.922Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":87},"gyroscope":{"x":-2218,"y":790,"z":-5489},"magnetometer":{"x":-564,"y":230,"z":-255},"date":"2015-02-06T00:25:17.922Z"}
WICED Sense: {"humidity":496,"pressure":10050,"temperature":270,"date":"2015-02-06T00:25:17.924Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":87},"gyroscope":{"x":-1700,"y":563,"z":-4059},"magnetometer":{"x":-562,"y":229,"z":-248},"date":"2015-02-06T00:25:17.924Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":86},"gyroscope":{"x":-2177,"y":724,"z":-5086},"magnetometer":{"x":-542,"y":231,"z":-255},"date":"2015-02-06T00:25:17.989Z"}
WICED Sense: {"accelerometer":{"x":2,"y":1,"z":86},"gyroscope":{"x":-1777,"y":638,"z":-4314},"magnetometer":{"x":-556,"y":240,"z":-261},"date":"2015-02-06T00:25:18.124Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":86},"gyroscope":{"x":-1675,"y":540,"z":-3980},"magnetometer":{"x":-538,"y":221,"z":-254},"date":"2015-02-06T00:25:18.192Z"}
WICED Sense: {"accelerometer":{"x":2,"y":1,"z":87},"gyroscope":{"x":-1575,"y":879,"z":-6174},"magnetometer":{"x":-545,"y":225,"z":-256},"date":"2015-02-06T00:25:18.193Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":86},"gyroscope":{"x":-2109,"y":751,"z":-5109},"magnetometer":{"x":-557,"y":244,"z":-264},"date":"2015-02-06T00:25:18.260Z"}
WICED Sense: {"accelerometer":{"x":2,"y":1,"z":86},"gyroscope":{"x":-1553,"y":547,"z":-3722},"magnetometer":{"x":-556,"y":230,"z":-257},"date":"2015-02-06T00:25:18.327Z"}
WICED Sense: {"accelerometer":{"x":2,"y":1,"z":87},"gyroscope":{"x":-1628,"y":571,"z":-6020},"magnetometer":{"x":-556,"y":226,"z":-263},"date":"2015-02-06T00:25:18.462Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":86},"gyroscope":{"x":-2236,"y":824,"z":-5601},"magnetometer":{"x":-556,"y":227,"z":-262},"date":"2015-02-06T00:25:18.462Z"}
WICED Sense: {"humidity":496,"pressure":10053,"temperature":270,"date":"2015-02-06T00:25:18.529Z"}
WICED Sense: {"accelerometer":{"x":2,"y":1,"z":86},"gyroscope":{"x":-1539,"y":532,"z":-3646},"magnetometer":{"x":-541,"y":233,"z":-257},"date":"2015-02-06T00:25:18.597Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":86},"gyroscope":{"x":-2498,"y":901,"z":-6201},"magnetometer":{"x":-547,"y":233,"z":-258},"date":"2015-02-06T00:25:18.664Z"}
WICED Sense: {"accelerometer":{"x":3,"y":1,"z":87},"gyroscope":{"x":-1806,"y":647,"z":-4368},"magnetometer":{"x":-549,"y":232,"z":-253},"date":"2015-02-06T00:25:18.732Z"}

データが受信できているのがわかると思います。

そのときにApollo Consoleはどうなるかというと、

f:id:daikimura:20150206003539p:plain

ConnectorsタブをクリックするとConnectionsに接続している一覧が出ます。これでなんとなくApolloに接続できていることが分かります。

今のコードではすべてのデータをそのまま送っているためデータ量が半端ないです。サブスクライバー側で処理をするには大変だと思うので、パブリッシャーから送るときにデータを間引きしたほうが良さそうです。 センサーの場合、何を目的にするかにもよりますが、ジャイロなどの動き系であれば、動いたことを検知してデータを送るのが良さそうです。 気温等のデータの場合は一定間隔で送る必要がありますが、例えば5分単位のデータがあれば十分なのではないかと思います。 ジャイロ・加速度センサーはしばらくいろいろと動かしてみて検証し、しきい値を決めて送る送らないを判定するロジックを入れようと思います。

MQTTを実装するにあたり、以下のブログを参考にさせていただきました。感謝!

Apache Apollo と Node.js で MQTT を試してみる - Null.ly