BluetoothデバイスのデータをRaspberry Piで受け取る by Simulink

## この記事について
SimulinkによるRaspberry Piでのアルゴリズム開発に、BLE(Bluetooth Low Energy)のデバイスを接続します。デバイスの例としてBLE対応の温湿度計で試しますが、基本的にはBLEのCLIコマンドであるbluetoothctlでRaspberry Piに接続できるデバイスであれば、何でもSimulinkにデータを受け渡すことができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583335/41f12102-3e41-e96a-7b69-4a0f2871d9fa.png" width="200")>

https://www.amazon.co.jp/dp/B082Y4KJHD/ref=syn_sd_onsite_desktop_0?th=1

## システム構成
先の記事で、プロセス間通信でUSB接続されたArduinoから高速サンプリングされたデータをRaspberry Piで受け取り、Simulinkモデルに渡す例を紹介しました。

https://qiita.com/kureta90/items/22ed24ba8b00064b0723

LinuxのCLIコマンドでシェルスクリプトを作成し、プロセス間通信でSimulinkに渡しています。同様に、CLIコマンドで直接デバイスのデータにアクセスできる場合は、同じ手法でSimulinkからのアクセスが可能です。BLEの場合、bluetoothctlのgatttoolが利用できます。

![gatttool_system.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583335/a2103f90-99ee-10fb-9fa7-d7699749a297.png)

bluetoothctlについてはこちらの記事を参考にさせて頂きました。

https://qiita.com/KentaHarada/items/ebe301497a4cea0835e6

BLEデバイスからデータを受け取る部分はLinux OSに任せて、受け取ったデータをプロセス間通信でSimulinkに渡そうというわけです。BLEの接続は後ほど述べますが、まずはプロセス間通信と、データを受け取るSimulinkについて見ていきます。


## プロセス間通信
SimulinkのRaspberry Pi向けサポートパッケージには"NNG"というブロックがあります。NNGはNanomessage Next Gen の略で、従来共有メモリやFIFOを用いるしかなく、やや敷居の高かったプロセス間通信を、メッセージとしてやり取りする技術です。

https://github.com/nanomsg/nng

NNGとSimulinkの接続は以下を参照ください。

https://qiita.com/drafts/22ed24ba8b00064b0723/edit

NNGのデータを受け取るSimulink側ではNNG Receiveブロック(subscribe)を配置するだけです。
![NNG_Simulink.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583335/6af12497-3eb1-84fa-e565-42734d422503.png)
ブロックのパラメータとしてNNGのプロセス名(ipcアドレス)とデータ型/サイズを入力します。
![NNG_Block_parameter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583335/bfa1f7f1-a430-2b01-46d4-02f7415ff4bc.png)

## BLE関連
以上で、BLEのデータさえ受け取れればSimulinkで利用できるようになりました。ここからはOSのCLEであるbluetoothctlでデバイスにアクセスします。最終的に必要なコマンドは一行で一発なのですが、事前にいくつか調べておく必要があります。
まずデバイスアドレスの特定です。対象デバイスの電源を入れて、Raspberri Piのターミナルで$ bluetoothctl と入力すると、デバイススキャンをして接続できるデバイスのアドレス一覧が表示されます。
```
$ bluetoothctl
# scan on
Discovery started
[CHG] Controller 00:1B:DC:F5:EC:70 Discovering: yes
[NEW] Device 44:B0:35:B1:33:BE 44-B0-35-B1-33-BE
[NEW] Device D6:6D:55:9C:3A:7E D7500_2025627
[NEW] Device 9E:2F:46:00:00:A1 9E-2F-46-00-00-A1
[NEW] Device 78:DB:2F:B8:5A:B4 sps
[bluetooth]# exit
```
対象のデバイスの電源をON/OFFするなどで、アドレスを特定し控えておきます。
次にデーターフィールドの確認です。gatttoolで対象デバイスのIDを指定し、データーフィールド(GATT characteristics)の一覧を確認します。handle = 0x****とあるのがフィールドのインデックスです。
$ gatttool -b * : * : * : * : * : * --characteristics

```
$ gatttool -b 78:DB:2F:B8:5A:B4 --characteristics
handle = 0x0002, char properties = 0x12, char value handle = 0x0003, uuid = 00002a19-0000-1000-8000-00805f9b34fb
handle = 0x0007, char properties = 0x02, char value handle = 0x0008, uuid = 00002a00-0000-1000-8000-00805f9b34fb
handle = 0x0009, char properties = 0x02, char value handle = 0x000a, uuid = 00002a01-0000-1000-8000-00805f9b34fb
handle = 0x000b, char properties = 0x0a, char value handle = 0x000c, uuid = 00002a02-0000-1000-8000-00805f9b34fb
```
例えば温湿度計IBS-TH1の場合、handle = 0x0024に温度と湿度のデータがありました。
$ gatttool -b * : * : * : * : * : * --char-read --handle=0x0024
とすると、温度と湿度のデータが返ってきます。
```
$ gatttool -b 78:DB:2F:B8:5A:B4 --char-read --handle=0x0024
Characteristic value/descriptor: 7b 07 12 16 00 6f 1b
```
この14バイトに温度と湿度の情報が含まれています。
7b 07 12 16 00 6f 1b　　-> 先頭4バイトが気温、先頭4バイトが湿度
各4バイトのうち最初の2バイトが下位
7b 07  -> 0x077b -> (10進)1915　-> 19.15℃
12 16 -> 0x1612 -> (10進)5650　-> 56.5%

困ったことにデーターのフォーマットは規格化されておらず、IBS-TH1の場合は先人の試行錯誤によって明らかになりました(しかし出荷時期によりhandleも0x002d / 0x0028 / 0x0024などと変遷していて、ちゃんと定まっていないようです)。GATTについては以下の記事を参考にさせて頂きました。

https://qiita.com/hirotosaito/items/4c653d0cdb5716d8878c

また、規格化が進んでいるヘルスケア関連製品などは、もう少し調査が楽かもしれません。

https://www.bluetooth.com/specifications/specs/generic-health-sensor-profile/


## BLEデータ読み出し&プロセス間通信
ともあれBLEデバイスを接続するのに必要なコマンドは以下のみです。
$ gatttool -b * : * : * : * : * : * --char-read --handle=0x****

あとはシェルスクリプトで繰り返しデータを取得し、必要な変換を加えて、プロセス間通信でデータをpubするだけです。下の例ではwhile文の最後の行でプロセス名blenng.ipcにpubしています。

``` gatt_read.sh
#!/bin/bash

while true
do
  msg=$(gatttool -b 49:21:08:30:66:3D --char-read --handle=0x0024 &)
  sleep 0.5
  data=${msg#*:}
  arr=($data)
  thhu=${arr[1]}${arr[0]}${arr[3]}${arr[2]}
  echo ${thhu}
  nngcat --pub --listen="ipc:///tmp/blenng.ipc" --data="${thhu}" --delay=1
done
```

シェルスクリプト実行の際は、シェルスクリプトにアクセス権を設定して、
(現在のパス直下に対象スクリプトがある場合)
$ sudo chmod 755 ./usbnng.sh

シェルスクリプトのパスを指定して実行します。
$ ./usbnng.sh


## Simulinkでの受け取り
SimulinkではNNG Receiveブロックでデータを受け取った後、10進変換と100で割る処理のみ行えば、温度、湿度として値が読めます。
![Simulink_model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583335/6c0e0d08-dae5-ae0d-4a95-b9e613ba52ec.png)


# まとめ
Simulinkの外でNNG pubを含んだシェルスクリプトを走らせるだけですので、作業自体は簡単です。今回はBLEの部分の説明が長くなってしまいましたが、CLIコマンドでデバイスにアクセスできるなら、BLEに限らずどんなデバイスでもNNG経由でSimulinkに接続できる、という記事でした。