LoginSignup
7
7

More than 5 years have passed since last update.

【IoT】Node-REDを使ってBLE経由のセンサーデータをとってみた

Last updated at Posted at 2018-09-02

Node-REDを使ってBLEセンサーの値をとってみた

はじめに

仕事の中で、Node-REDを使ってBLEでセンサーデータを取る必要がありました。
node.js未経験・Node-RED未経験の状態だったんですが、頑張ってやってみたので、やったことをまとめておきたいと思います。
node初心者なのでいろいろとツッコミどころがあるかと思うので、バシバシ突っ込んでいただけると嬉しいです。

環境については、Node-REDが標準で入ってるIoTゲートウェイにて開発しました。

開発の準備

BLE取得ライブラリ「noble」

node.jsで周辺のBLEデバイスを探したりペアリングしたりもろもろできるライブラリ「noble」を使って開発しました。
https://github.com/noble/noble

まずは、Node-REDがあるディレクトリに移動してグローバルインストール。
npm install -g noble

ライブラリはこれだけ。

Nodeをつくる

今回のプロジェクトでは、自分の他にコードが書ける人がいなかったため、デバイスのMACアドレスなどを誰でも設定できるようにする必要がありました。
そこで、GUI上で設定変更可能なNodeを作成しました。
(コードを勝手に編集される心配も減ると思います。)

Node-REDのrootディレクトリ配下に以下のディレクトリがあると思います。
/nodes/core/
こちらに、設定用のHTMLファイルとプログラムを実行するjsファイルを作成します。
今回作った設定ファイルの雛形は以下のとおりです。
(業務用のソースから一部変更しているので、おかしな部分があったらごめんなさい)

ble.html
<script type="text/javascript">
    RED.nodes.registerType('ble',{
        category: 'ble',
        color: '#A5DF00',
        defaults: {
            filter: {value:""},
            serviceuuid: {value:""},
            charactaristicsuuid: {value:""},
            GWName: {value:""},
            appKey: {value:""}
        },
        inputs:1,
        outputs:1,
        icon: "dataparsefix.png",
        label: function() {
            return this.name||"ble";
        }
    });
</script>
<script type="text/x-red" data-template-name="ble">
    <div class="form-row">
        *MACアドレス<br>
        <label for="node-input-macaddress"> MACアドレスを入力してください</label>
        <input type="text" id="node-input-macaddress" placeholder="aabbcc112233" style="width: 300px" maxlength='32'><br><br>
        *Service UUID<br>
        <label for="node-input-serviceuuid"> Service UUIDを入力してください</label>
        <input type="text" id="node-input-serviceuuid" placeholder="UUID" style="width: 300px" maxlength='32'><br><br>
        *Characteristics UUID<br>
        <label for="node-input-charactaristicsuuid">Characteristics UUIDを入力してください</label>
        <input type="text" id="node-input-charactaristicsuuid" placeholder="UUID" style="width: 300px" maxlength='32'><br><br>
        *送信するAPIのURLとKEY<br>
        <label for="node-input-url">URLを入力してください</label>
        <input type="text" id="node-input-url" placeholder="https://xxxxx.xxxxx" style="width: 300px" maxlength='32'><br><br>
        <label for="node-input-key">KEYを入力してください</label>
        <input type="text" id="node-input-key" placeholder="XXXX-XXXX-XXXX-XXXX-XXXX" style="width: 300px" maxlength='60'><br><br>
    </div>
</script>
<script type="text/x-red" data-help-name="ble">
    <p>BLEデータを取得します</p><br><br>
</script>
ble.js
module.exports = function(RED) {
    var noble = require('noble');
    var gwname = '';
    var appkey = '';

    function ble(config) {
        RED.nodes.createNode(this,config);
        this.macaddress = config.macaddress;
        this.serviceuuid = config.serviceuuid;
        this.charactaristicsuuid = config.charactaristicsuuid;
        url = config.url;
        key = config.key;

        (設定項目には関係ないため省略)

    }

    (設定項目には関係ないため省略)

    RED.nodes.registerType("ble",ble);
}

以上で下準備は完了です。

BLE関連処理

ble.jsで実施したBLE関連の処理を順に書いていきます。

各処理を行う際

以下の中で実行します。

this.on('input', function(msg) {
    (各処理を実装)
});

状態管理(接続状態が変わったときに実行される)

スキャン履歴を削除しないと再度同じMACアドレスのデバイスに接続できないので注意

noble.on('stateChange', function(state) {
    console.log('stateChange Function : ' + state);
    if (state === 'poweredOn') {    // stateが'poweredOn'になった時
        noble.startScanning();      // BLEのスキャンを開始
    } else {                        // stateが'poweredOn'でなくなった時
        noble.stopScanning();       // BLEのスキャンを終了
        noble.removeAllListeners(); // スキャン履歴を削除
    }
});

スキャン開始・終了

noble.on('scanStart', function() {
    console.log('on -> scanStart');
    node.status({fill:"green",shape:"dot",text:"started"}); // Node-REDの上にステータス表示をするための記述
});
noble.on('scanStop', function() {
    console.log('on -> scanStop');
    node.status({fill:"red",shape:"ring",text:"stopped"}); // Node-REDの上にステータス表示をするための記述
});

読み取り

noble.on('discover', function(peripheral) {
    if (peripheral.uuid == node.macaddress) {  // MACアドレスを確認
        noble.stopScanning();
        peripheral.connect(function(error) {
            peripheral.discoverServices(null, function(error, services) {
                var service = services[2];
                service.discoverCharacteristics([node.charactaristicsuuid],function(error,characteristics) {
                    var charData = characteristics[0];
                    charData.on('data', function(data, isNotification) {
                        msg.serviceUuid = charData._serviceUuid;
                        msg.charUuid = charData.uuid;
                        msg.payload = data.readUInt8(0);
                        msg.type = 'data';
                        node.send(msg);
                    });
                    charData.subscribe(function(error) {
                        console.log('notification on');
                    });
                });
                var service2 = services[3];
                service2.discoverCharacteristics(['2a19'],function(error,characteristics) { // 電池残量のuuid 固定のためソース上に記載
                    var charData2 = characteristics[0];
                    charData2.on('data', function(data, isNotification) {
                        msg.serviceUuid = charData2._serviceUuid;
                        msg.charUuid = charData2.uuid;
                        msg.payload = data.readUInt8(0);
                        msg.type = 'battery';
                        sendVolt(data.readUInt8(0));
                        node.send(msg);
                    });
                    charData2.subscribe(function(error) {
                        console.log('notification on');
                    });
                });
            });
        });
    }
    peripheral.on('disconnect', function() {
        noble.startScanning();
    )};
)};

sarvicesの2と3だけほしかったのでこういう書き方をしましたが、本当はもっとイケてる実装があったと思います。。。
内容的にはセンサーデータとバッテリー情報を取得してmsgのpayloadに詰めて送る。という流れです。

正直あってるのか不安になりながらの実装で、「本当に動いてるのかこれ?」って思いながらやってました。
詳しい人いたら教えてー

あとはAPIに送るなりなんなりという感じですので省略します。

まとめ

  • BLEが目に見えないので初取得まですごい不安
  • ソース上でdisconnectの処理を書いていたら再取得できずに困ってたらnobleのバグでできないらしく、exitでNode-REDを強制的に落とすというやばめの対応をしてしまった
  • logを書きまくるの大事(記事では見づらいので極力減らしてます)

以上です。
今回はOpenBlocksで開発したのですが、ライブラリを入れるのにめちゃめちゃ苦労しました。
多分非公式の方法なので、あえて記載していません。
ツッコミ等お待ちしております。よろしくおねがいします。

7
7
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
7
7