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

# 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ファイルを作成します。
今回作った設定ファイルの雛形は以下のとおりです。
(業務用のソースから一部変更しているので、おかしな部分があったらごめんなさい)

```html: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>
```

```javascript: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関連の処理を順に書いていきます。

### 各処理を行う際
以下の中で実行します。

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

### 状態管理(接続状態が変わったときに実行される)
スキャン履歴を削除しないと再度同じMACアドレスのデバイスに接続できないので注意

```javascript:
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(); // スキャン履歴を削除
    }
});
```

### スキャン開始・終了

```javascript:
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の上にステータス表示をするための記述
});
```

### 読み取り

```javascript
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で開発したのですが、ライブラリを入れるのにめちゃめちゃ苦労しました。
多分非公式の方法なので、あえて記載していません。
ツッコミ等お待ちしております。よろしくおねがいします。