Check! Raspberry Pi で Bluetooth 経由のセンサーデータを取得する 〜 準備編

こんにちは、オークファンの @dz_ こと大平かづみです。

Prologue - はじめに
====
Raspberry Pi で Bluetooth を使ってセンサーデバイスのデータを取得できるように、Bluetooth の扱い方について調べました。

なお、この試みは当初 Raspberry Pi Zero でやりはじめたのですが、メモリ不足で必要なライブラリがインストールできなかったので、 Raspberry Pi 2 Model B にて進めることとなりました。

Raspberry Pi で Bluethooth を使う
====

Bluetooth アダプタの用意
----
今回使う Raspberry Pi には Bluetooth は搭載されていないので、USBアダプタを利用します。

### Bluetooth のUSBアダプタあれこれ
よく利用されているのは、PLANEX の製品のようです。私は、某電気屋さんにいったら PLANEX はおいてなかったので、一番安かった PRINCETON のものを購入しました（笑）特に問題なく動作しています。

- [PLANEX BT-Micro4](http://amzn.to/1SrkTwI "PLANEX Bluetooth USBアダプター Ver.4.0+EDR/LE(省エネ設計)対応 BT-Micro4")
- [PRINCETON PTM-UBT7](http://amzn.to/1SEHtaA "プリンストン Bluetooth USBアダプター (通信距離 25m:Ver4.0接続、10m:Ver3.0 Class2接続) PTM-UBT7")

Bluetooth で接続するセンサーデバイス
----
10種のセンサーが搭載されている [SensorTag CC2650](http://www.tij.co.jp/sensortag "Simplelink センサタグ - TI") を利用します。これは Bluetooth で接続ができ、サンプルコードやユーザーによる情報もちらほらあるため扱いやすそうです。

Linux で Bluetooth を扱う場合の周辺事情
----
私もまだ勉強中なのですが、Linux で Bluetooth を扱うには以下の仕組みを使っていくことになるようです。

### [BlueZ](http://www.bluez.org/ "BlueZ")
- Official Linux Bluetooth protocol stack
- 参考: [Bluetoothのはなし(4)｜Wireless・のおと｜サイレックス・テクノロジー株式会社](http://www.silex.jp/blog/wireless/2012/12/bluetooth4.html "Bluetoothのはなし(4)｜Wireless・のおと｜サイレックス・テクノロジー株式会社")

### [GATT (Generic Attribute Profile)](https://developer.bluetooth.org/TechnologyOverview/Pages/GATT.aspx "GATT | Bluetooth Development Portal")
- Attribute Protocol (ATT) を基にデータの転送や保存の仕組みを共通化、フレームワーク化したもの
- 参考: [Bluetoothのはなし(2)｜Wireless・のおと｜サイレックス・テクノロジー株式会社](http://www.silex.jp/blog/wireless/2012/10/bluetooth2.html "Bluetoothのはなし(2)｜Wireless・のおと｜サイレックス・テクノロジー株式会社")

動作確認
----
難しいことはおいおい学んでいくとして、手はじめに、 [RPi Bluetooth LE - eLinux.org](http://www.elinux.org/RPi_Bluetooth_LE "RPi Bluetooth LE - eLinux.org") を参考に、コマンドラインから動作確認をしてみました。

```bash
# 接続されているUSB機器を確認する
$ lsusb
...
Bus 001 Device 011: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
...

# Bluetoothアダプタの状態を確認する
$ hciconfig
hci0:   Type: BR/EDR  Bus: USB
        BD Address: XX:XX:XX:XX:XX:XX  ACL MTU: 310:10  SCO MTU: 64:8
        UP RUNNING 
        RX bytes:610 acl:0 sco:0 events:36 errors:0
        TX bytes:942 acl:0 sco:0 commands:36 errors:0

# もし上記で `DOWN` と表示されていたら、以下のコマンドで有効にする
$ sudo hciconfig hci0 up

# 周辺の Bluetooth 機器をスキャンする
$ sudo hcitool lescan
LE Scan ...
XX:XX:XX:XX:XX:XX (unknown)
XX:XX:XX:XX:XX:XX CC2650 SensorTag
...
```

スキャン結果に SensorTag の表示がありました。無事、認識することができそうですね。

Python から Bluetooth を使う
====
それでは早速、プログラムから Bluetooth を使ってみようと思います。

言語は Raspberry Pi 界隈ではメジャーな Python を、復習がてら使ってみます。Bluetooth を扱うためのライブラリは `pybluez` を導入してみます。

- [karulis/pybluez: Bluetooth Python extension module](https://github.com/karulis/pybluez "karulis/pybluez: Bluetooth Python extension module")
- [pybluez - Documentation.wiki](https://code.google.com/archive/p/pybluez/wikis/Documentation.wiki "pybluez - Documentation.wiki")

### `pybluez` のインストール
インストール自体は以下をご参考ください。`pybluez` で BLE 対応の機器を扱うには `gattlib` も必要なので、それもインストールします。

```bash
# pyBluez の依存パッケージをインストール
$ sudo apt-get install python-dev libbluetooth3-dev

# pyBluez のインストール
$ sudo pip install pybluez

# gattlib の依存パッケージをインストール
$ sudo apt-get install libglib2.0 libboost-python-dev libboost-thread-dev

# BLEを使う場合に必要な gattlib をインストール
$ sudo pip install gattlib
```

### サンプルコード
それでは、python から SensorTag を探してみましょう。

SensorTag は BLE対応なので、このサンプルコードでスキャンすることができます。

- [pybluez/examples/ble/scan.py](https://github.com/karulis/pybluez/blob/master/examples/ble/scan.py "pybluez/scan.py at master · karulis/pybluez")

また、データを取得するには、こちらのサンプルコードが参考になります。

- [pybluez/examples/ble/read_name.py](https://github.com/karulis/pybluez/blob/master/examples/ble/read_name.py "pybluez/read_name.py at master · karulis/pybluez")

なお、もうお気づきかもしれませんが、実は pybluez は、BLE対応機器を扱う際は pybluez を介して gattlib を利用しているだけなので、 gattlib 自体のドキュメントも参考になります。

- [OscarAcena / pygattlib   — Bitbucket](https://bitbucket.org/OscarAcena/pygattlib "OscarAcena / pygattlib   — Bitbucket")

また、GATT では「ハンドル」と呼ばれる番地（？）を用いてデータを取得・設定します。SensorTag の GATTテーブルは、 [分解 | Simplelink センサタグ - TI](http://www.tij.co.jp/ww/wireless_connectivity/sensortag2015/tearDown.html "Simplelink センサタグ - TI") の「センサとサービス」に掲載されていますので、ご参照くださいませ。

これで、好みのセンサーデータが取得できるようになりましたね！？

（実際には、取得したデータは LSB, MSB の転換などが必要となります。)

Epilogue - おわりに
====
大好きな Raspberry Pi Zero で動かせなかったのは残念でしたが、これで Raspberry Pi 2 から Bluetooth を利用する準備ができました！

もりもり進んでいきますよー！

----

以降は、トラブルシューティングに関して記載しました。

`pybluez` インストール時のトラブルシューティング
====
`Python.h` が見つからない場合
----
以下のように、`fatal error: Python.h: No such file or directory` というエラーが発生する場合は、`python-dev` をインストールする必要があります。

### エラー内容
```bash
    arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno
-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-se
curity -fPIC -I./port3 -I/usr/include/python2.7 -c bluez/btmodule.c -o build/temp.linux-arm
v6l-2.7/bluez/btmodule.o
    In file included from bluez/btmodule.c:20:0:
    bluez/btmodule.h:4:20: fatal error: Python.h: No such file or directory
     #include "Python.h"
                        ^
    compilation terminated.
    error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1
    
    ----------------------------------------
Command "/usr/bin/python -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-4nXTDu
/pybluez/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\
r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-fWWiRf-record/install-record.txt
 --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-
4nXTDu/pybluez/
```
### 対処
```bash
$ sudo apt-get install python-dev
```

`bluetooth/Bluetooth.h` が見つからない場合
----
以下のように、`fatal error: bluetooth/bluetooth.h: No such file or directory
` というエラーが発生する場合は、`libbluetooth3-dev` をインストールする必要があります。

### エラー内容
```bash
    arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno
-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-se
curity -fPIC -I./port3 -I/usr/include/python2.7 -c bluez/btmodule.c -o build/temp.linux-arm
v6l-2.7/bluez/btmodule.o
    In file included from bluez/btmodule.c:20:0:
    bluez/btmodule.h:5:33: fatal error: bluetooth/bluetooth.h: No such file or directory
     #include <bluetooth/bluetooth.h>
                                     ^
    compilation terminated.
    error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1
    
    ----------------------------------------
Command "/usr/bin/python -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-OWwZct
/pybluez/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\
r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-_zZYsg-record/install-record.txt
 --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-
OWwZct/pybluez/
```

### 対処
```bash
$ sudo apt-get install libbluetooth3-dev
```

`gattlib` インストール時のトラブルシューティング
====
`boost/python/list.php` が見つからない場合
----
以下のように、`fatal error: boost/python/list.hpp: No such file or directory` というエラーが発生する場合は、`libboost-python-dev` をインストールする必要があります。

### エラー内容
```bash
    arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno
-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-se
curity -fPIC -DVERSION="5.25" -I/usr/include/glib-2.0 -I/usr/lib/arm-linux-gnueabihf/glib-2
.0/include -Isrc/bluez -I/usr/include/python2.7 -c src/gattservices.cpp -o build/temp.linux
-armv6l-2.7/src/gattservices.o
    cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not
 for C++
    In file included from src/gattservices.cpp:12:0:
    src/gattlib.h:11:33: fatal error: boost/python/list.hpp: No such file or directory
     #include <boost/python/list.hpp>
                                     ^
    compilation terminated.
    error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1
    
    ----------------------------------------
Command "/usr/bin/python -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-lIdNTN
/gattlib/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\
r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-h_pTpQ-record/install-record.txt
 --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-
lIdNTN/gattlib/
```

### 対処
```bash
$ sudo apt-get install libboost-python-dev
```

glib-2.0 がみつからない場合
----
以下のように、`No package 'glib-2.0' found` というエラーが発生する場合は、`libglib2.0` をインストールする必要があります。

### エラー内容
```bash
$ sudo pip install gattlib
Collecting gattlib
  Downloading gattlib-0.20150805.tar.gz (1.7MB)
    100% |████████████████████████████████| 1.7MB 86kB/s 
    Complete output from command python setup.py egg_info:
    Package glib-2.0 was not found in the pkg-config search path.
    Perhaps you should add the directory containing `glib-2.0.pc'
    to the PKG_CONFIG_PATH environment variable
    No package 'glib-2.0' found
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-ix7ocM/gattlib/setup.py", line 12, in <module>
        "pkg-config --cflags glib-2.0".split()).decode('utf-8')
      File "/usr/lib/python2.7/subprocess.py", line 573, in check_output
        raise CalledProcessError(retcode, cmd, output=output)
    subprocess.CalledProcessError: Command '['pkg-config', '--cflags', 'glib-2.0']' returne
d non-zero exit status 1
    
    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-ix7ocM/gattli
b/
```

### 対処
```bash
$ sudo apt-get install libglib2.0
```

`-lboost_thread` が見つからない場合
----
以下のように、`cannot find -lboost_thread` というエラーが発生する場合は、`libboost-thread-dev` をインストールする必要があります。

### エラー内容
```bash
  c++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -
DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector-s
trong -Wformat -Werror=format-security -Wl,-z,relro -D_FORTIFY_SOURCE=2 -g -fstack-protecto
r-strong -Wformat -Werror=format-security build/temp.linux-armv7l-2.7/src/gattservices.o bu
ild/temp.linux-armv7l-2.7/src/beacon.o build/temp.linux-armv7l-2.7/src/bindings.o build/tem
p.linux-armv7l-2.7/src/gattlib.o build/temp.linux-armv7l-2.7/src/bluez/lib/uuid.o build/tem
p.linux-armv7l-2.7/src/bluez/attrib/gatt.o build/temp.linux-armv7l-2.7/src/bluez/attrib/gat
trib.o build/temp.linux-armv7l-2.7/src/bluez/attrib/utils.o build/temp.linux-armv7l-2.7/src
/bluez/attrib/att.o build/temp.linux-armv7l-2.7/src/bluez/src/shared/crypto.o build/temp.li
nux-armv7l-2.7/src/bluez/src/log.o build/temp.linux-armv7l-2.7/src/bluez/btio/btio.o -lglib
-2.0 -lboost_python -lboost_thread -lbluetooth -o build/lib.linux-armv7l-2.7/gattlib.so
  /usr/bin/ld: cannot find -lboost_thread
  collect2: error: ld returned 1 exit status
  error: command 'c++' failed with exit status 1
```

### 対処
```bash
$ sudo apt-get install libboost-thread-dev
```

internal compiler error: Killed の場合
----
このエラーのときは、なかなかプロンプトが返ってこないで、返ってきたと思ったらこのエラーが表示されます。

### エラー内容
```bash
  arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-s
trict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-secu
rity -fPIC -DVERSION="5.25" -I/usr/include/glib-2.0 -I/usr/lib/arm-linux-gnueabihf/glib-2.0
/include -Isrc/bluez -I/usr/include/python2.7 -c src/bindings.cpp -o build/temp.linux-armv6
l-2.7/src/bindings.o
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not f
or C++
  arm-linux-gnueabihf-gcc: internal compiler error: Killed (program cc1plus)
  Please submit a full bug report,
  with preprocessed source if appropriate.
  See <file:///usr/share/doc/gcc-4.9/README.Bugs> for instructions.
  error: command 'arm-linux-gnueabihf-gcc' failed with exit status 4
```

### 原因
これは、どうやらメモリ不足のようです。
当初試していた Raspberry Pi Zero ではこれ以上 gattlib のインストールが進められませんでしたが、 Raspberry Pi 2 Model B であれば、このエラーは出ずに gattlib をインストールすることができました。