18
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

myThingsAdvent Calendar 2015

Day 3

Raspberry PiとSensorTagからMesubluにMQTT通信をする

Last updated at Posted at 2015-12-02

CC2650 SensorTag

2015年に発売された新しいCC2650 SensorTagはBLE通信に対応したIoT開発キットです。第1世代のCC2541からバージョンアップして搭載するセンサーも増え省電力化もされているようです。搭載しているセンサーは以下の10種類です。

  • デジタル・マイク
  • 磁気センサ
  • 湿度
  • 圧力
  • 加速度計
  • ジャイロスコープ
  • 磁力計
  • 物体温度
  • 周囲温度

SensorTagを使うとRaspberry PiにBlootoothのUSBアダプターが必要になりますが、面倒なブレッドボードの配線をしなくても簡単にBLE通信でセンサーデータの取得ができます。Raspberry Piで取得したデータはクラウドのメッセージブローカーに送ったり、閾値監視をしてアラートをあげたりとLinuxで好きなように加工することができます。

 Raspberry PiとSensorTagを使ってMQTTブローカーとメッセージの送受信を試してみます。

Meshblu

 MeshbluOctblu社がオープンソースで開発をしているIoTメッセージングプラットフォームです。myThings「IDCF」チャンネルではMQTTブローカーとして採用されています。HTTP REST、WebSocket、MQTT、CoAPなど複数の通信プロトコルに対応したIoTプラットフォームがいくつかありますが、Mesubluもそのうちの一つです。

 サーバー側で複数のプロトコルをブリッジしてくれるため、Raspberry PiのMQTTクライアントからパブリッシュしたメッセージをブラウザのWebSocketでサブスクライブすることもできます。

クラウドにセットアップ

 Debian 8.2.0 64-bitまたはUbuntu 14.04 64-bitの仮想マシンを用意します。こちらを参考にしてMesubluをセットアップします。

 listコマンドを実行すると初期設定された「デバイス」一覧が表示されます。「デバイス」はMeshbluがIoTデバイスやWebサービス、人を抽象化したものです。「デバイス」のuuidとtokenをRaspberry PiやMQTTクライアントなどMesubluを通してコミュニケーションをとりたいものに割り当てて使います。ここでは「trigger-1」と「action-1」を使いメッセージを送受信します。

$ docker-compose run --rm iotutil list

> iotutil@0.0.1 start /app
> node app.js "list"

┌───────────┬──────────┬──────────────────────────────────────┐
│ keyword   │ token    │ uuid                                 │
├───────────┼──────────┼──────────────────────────────────────┤
│ trigger-1 │ d286ba8d │ ffa6934d-f1b3-467f-98b3-766b330d436d │
├───────────┼──────────┼──────────────────────────────────────┤
...
├───────────┼──────────┼──────────────────────────────────────┤
│ action-1  │ 8a83d71f │ b61d3398-ac99-4694-9dc4-dd632faf6f6a │
├───────────┼──────────┼──────────────────────────────────────┤
...

Raspberry Pi

 こちらを参考にしてRaspberry Piがインターネットに接続できる状態から作業を進めます。先ほどクラウドの仮想マシンに割り当てたパブリックIPアドレスを指定してMeshbluの起動を確認します。{"meshblu":"online"}と出力すればMeshbluの起動に成功しています。

$ curl http://{仮想マシンのパブリックIPアドレス}/status

Node.js

 Node.jsはnode-armからパッケージをダウンロードしてインストールします。執筆時点ではnode_latest_armhf.debのv4.xがRaspbian
Wheezyで安定して動作しないためnode_latest_armhf.debのv.0.12.6を使います。パッケージ名にご注意ください。

$ wget http://node-arm.herokuapp.com/node_archive_armhf.deb
$ sudo dpkg -i node_archive_armhf.deb
$ node -v
v0.12.6

Bluetooth

 BlueZはLinuxで動作するオープンソースのBluetoothスタックです。Raspbian Wheezyのパッケージマネージャーからインストールできるパッケージは少し古くBLEのサポートに問題があるため、新しいバージョンをソースコードからビルドして使います。最初にビルドに必要なパッケージをインストールします。

$ sudo apt-get update
$ sudo apt-get install libdbus-1-dev libdbus-glib-1-dev libglib2.0-dev libical-dev libreadline-dev libudev-dev libusb-dev make

 ソースコードをダウンロードしてビルドとインストールをします。

$ wget https://www.kernel.org/pub/linux/bluetooth/bluez-5.36.tar.gz
$ tar xvf bluez-5.36.tar.gz
$ cd bluez-5.36
$ ./configure --disable-systemd
$ make
$ sudo make install

 hciconfigコマンドを使いBluetoothデバイスを有効にします。

$ sudo hciconfig hci0 up

SensorTagからBLE通信

 サンプルコードをダウンロードして必要なパッケージをインストールします。

$ git clone https://github.com/masato/meshblu-sensortag.git
$ cd meshblu-sensortag
$ npm install

config.jsをMesubluのデバイス情報をもとに編集します。パブリックIPアドレスのファイアウォールは1883ポートを開き仮想マシンにポートフォワードしている必要があります。

 センサーデータをMQTTブローカーに送信するクライアントにするためMeshbluのMQTTクライアントライブラリを追加します。Raspberry Piのパブリッシュするプログラムにtrigger-1のuuidを割り当て、サブスクライブするプログラムにaction-1デバイスを割り当てます。

config.js
var config = {
    waitTime: 5000,
    trigger_1_uuid: '{trigger-1デバイスのuuid}',
    trigger_1_token: '{trigger-1デバイスのuuid}',
    action_1_uuid: '{action-1デバイスのuuid}',
    action_1_token: '{action-1デバイスのtoken}',
    ip_address: '{仮想マシンのパブリックIPアドレス}',
    port: 1883
}

module.exports = config;

 以下のプログラムはRaspberry Piで物体温度(Object Temperature)、周囲温度(Ambient Temperature)、気圧(Pressure)の3つの環境データをSensorTagから取得します。データはJSON形式にフォーマットしてMeshbluのMQTTブローカーにMQTT通信でパブリッシュします。

app-pub.js
'use strict';

var async = require('async'),
    SensorTag = require('sensortag'),
    Meshblu = require('meshblu-mqtt'),
    config = require('./config');

var meshblu = new Meshblu({
    'uuid': config.trigger_1_uuid,
    'token': config.trigger_1_token,
    'hostname': config.ip_address,
    'port': config.port
});

SensorTag.discover(function(sensorTag) {
    console.log('discovered: ' + sensorTag);

    sensorTag.on('disconnect', function() {
        console.log('Tag Disconnected');
        process.exit(0);
    });

    process.on('SIGINT', function() {
        sensorTag.disconnect();
    });

    async.waterfall([
        function(callback) {
            console.log('connectAndSetUp');
            sensorTag.connectAndSetUp(callback);
        },
        function(callback) {
            console.log('enableIrTemperature');
            sensorTag.enableIrTemperature(callback);
        },
        function(callback) {
            console.log('enableBarometricPressure');
            sensorTag.enableBarometricPressure(callback);
        },
        function(callback) {
            setTimeout(callback, 2000);
        },
        function(callback) {
            meshblu.connect(function(response){
                console.log('meshblu ready');
                async.forever(function(callback) {
                    async.series([
                        function(callback) {
                            sensorTag.readIrTemperature(function(error,
                                                                 objectTemperature,
                                                                 ambientTemperature) {
                                var payload = {
                                    objectTemperature: objectTemperature.toFixed(1),
                                    ambientTemperature: ambientTemperature.toFixed(1)
                                };
                                callback(null, payload);
                            });
                        },
                        function(callback) {
                            sensorTag.readBarometricPressure(function(err, pressure){
                                var payload = {
                                    pressure: pressure
                                };
                                callback(null, payload);
                            });
                        }
                    ],
                    function(err, res) {
                        console.log('物体温度: '+ res[0].objectTemperature+'');
                        console.log('周囲温度: '+ res[0].ambientTemperature+'');
                        console.log('気圧    : '+ res[1].pressure+' mBa');

                        meshblu.message({
                            devices: [config.action_1_uuid],
                            topic: 'message',
                            payload: {
                                objectTemperature: res[0].objectTemperature,
                                ambientTemperature: res[1].ambientTemperature,
                                pressure: res[1].pressure
                            }
                        });

                        setTimeout(callback, config.waitTime);
                    });
                },
                function(err) {
                    if (err) throw err;
                });
            });
        }
    ],
    function (err) {
       if (err) throw err;
    });
});

 SensorTag本体の横についている電源スイッチを押します。root権限でnpm startコマンド経由でプログラムを実行するとRaspberry Piでセンサーデータが取得できます。このサンプルでは5秒間隔で計測したデータをMQTTブローカーにパブリッシュして、以下のようなログが標準出力に表示します。

$ sudo npm start
物体温度: 16.4 ℃
周囲温度: 23.9 ℃
気圧    : 1020.98 mBa

MQTTメッセージのサブスクライブ

 MQTTではブローカーが仲介してクライアント間でメッセージを送受信します。まずはRaspberry PiにMQTTクライアントのMosquittoをインストールしてメッセージのサブスクライブをテストします。

$ sudo apt-get update
$ sudo apt-get mosquitto-clients

 Mosquittoクライアントにも同様にデバイスのuuidを割り当てます。trigger-1が送信したメッセージを受信するため、Mosquittoクライアントにはaction-1デバイスのuuidを割り当てます。

$ mosquitto_sub \
  -h {仮想マシンのパブリックIPアドレス}  \
  -p 1883  \
  -t {action-1デバイスのuuid} \
  -u {action-1デバイスのuuid}  \
  -P {action-1デバイスのtoken}  \
  -d

 MQTTブローカーからサブスクライブしたメッセージが標準出力されます。

Subscribed (mid: 1): 0
 Received PUBLISH (d0, q0, r0, m0, '{action-1デバイスのuuid}', ... (257 bytes))
  {"topic":"message","data":{"devices":["{action-1デバイスのuuid}"],"topic":"message","payload":{"objectTemperature":"16.3","ambientTemperature":"23.1","pressure":1022.14},"callbackId":"8ebdc270-95b8-11e5-943c-e1cfdbaddec8","fromUuid":"{trigger-1デバイスのuuid}"}}

 次にNode.jsのプログラムを実行して同様にセンサーデータ取得してみます。

'use strict';

var async = require('async'),
    Meshblu = require('meshblu-mqtt'),
    config = require('./config');

var meshblu = new Meshblu({
    'uuid': config.action_1_uuid,
    'token': config.action_1_token,
    'hostname': config.ip_address,
    'port': config.port
});

meshblu.on('message', function(message){
    console.log(message);
});

 Mosquittoクライアントと同様にaction-1デバイスのuuidを使いメッセージをサブスクライブしたメッセージが標準出力に表示されました。

$ npm run sub
{ devices: [ '{action-1デバイスのuuid}' ],
  topic: 'message',
  payload:
   { objectTemperature: '16.5',
     ambientTemperature: '22.9',
     pressure: 1022.19 },
  callbackId: '79da6970-95be-11e5-b0c4-e15124f00dff',
  fromUuid: 'trigger-1デバイスのuuid' }

いろいろなデバイスをmyThingsにつなげるために

 コネクテッドデバイスをMeshbluと接続するには今回説明したようなuuidを間接的に割り当てる方法がプリミティブですが汎用的です。最後に他の方法をご紹介します。

Gatebluはインターネットに接続していないデバイスをMeshbluとコミュニケーション可能にするソフトウェアです。WindowsやOSXにインストールして使います。ホームオートメーションやAllJoynのゲートウェイとして機能するので「IDCF」チャンネルと連携すれば多くの対応デバイスをmyThingsと組み合わせることができそうです。

 Tentacleはマイコン用のライブラリです。Arduino UnoParticlePhotonなどのArduino互換のマイコンにArduino IDEからスケッチとしてインストールします。Octoblu Disignerを使うとコードを書かずにブラウザのGUIからフローとピンの制御ができるようになります。

 何かの条件を満たしたら、何かの処理を実行するというmyThingsのイベント駆動の考え方は身近なマイクロサービスの良い例です。多くのデバイスやサービスをインターネットにつなげ、もっと簡単にタスクやワーカーを実行させる方法を考えていきたいと思います。

 
 

18
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?