屋内環境を手軽にモニタリングする～②Bluetooth LEアドバタイズ信号をJavaでキャッチする(Bluetooth LE / bluez-dbus)～

## はじめに
プラットフォームにJava / Raspberry Pi 3Bを選択しましたので、この環境で各種センサーと通信するJavaライブラリを探します。

まず最初に、Bluetooth LEについてです。ここ数年、BLE通信でセンサーデータを取得できるデバイスが出てきています。今回、メジャーどころの[テキサス・インスツルメンツ社のSensorTag CC2650](http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide)を選択しました。CC2650は、温度、湿度、照度、気圧、角速度、加速度、磁気を測定できる、5cm x 4cm程度のボタン電池駆動型のBLEデバイスです。

CC2650の使い方は、以下の流れになります。
1. CC2650(ペリフェラル)の電源を入れて、アドバタイズ信号を発信
2. セントラルは、アドバタイズ信号をキャッチして、CC2650に接続(CC2650はアドバタイズ信号を停止)
3. セントラルは、BLE GATTプロファイルを用いて、CC2650からセンサーデータを取得する

なお、CC2650は、アドバタイズ信号を発信し始めて3分の間に、どこからも接続されないと自動的に電源を切ります。なので、接続に備えるには、再度、電源を入れる必要があります。今回、アドバタイズ信号をJavaでキャッチするにあたり、BLE通信に対応したJavaライブラリとして、[bluez-dbus](https://github.com/hypfvieh/bluez-dbus)を採用しました。

以前、試験的に、CC2650からセンサーデータを取得するために、Javaでコードを書いたことがありました。その時は、bluez-dbusは未だ無く、他にJavaでBLE通信するためのライブラリとして、ちょうど[Intel TinyB](https://github.com/intel-iot-devkit/tinyb)がリリースされた頃でした。ただ、リリース間もないTinyBは不安定で、当時、BLEのNotification機能も使えませんでした。何より、bluezが公開しているdbusのI/F定義ファイルをC/C++のコードに自動変換して(1万行を超えるコードが自動生成)、これに更に手を入れてTinyBは作成されており、メンテナンス性はとても低そうに感じました。

BLEをdbus I/Fで使用する場合、dbus自体はUNIXドメインソケットの上で定義されているので、本質的には、このUNIXドメインソケットの部分だけがJNIで書かれ、その上で定義されるbluez用のdbusプロトコルは、このJNIを呼び出す形で実装するのがシンプルなのに、と思っていました。

当時は、仕方なくbluezの各種コマンドを常駐プロセスとして連携させるJavaのコードを書いて、凌いだことがあります。複雑なアーキテクチャで、ダサイなあと思いました。当時のコードは忘れましたが、このダサイ印象だけは今も残っています。

あれから月日は流れ、今では、BLEに対応したbluez-dbusなるライブラリが公開されています。Githubに書かれた開発者の動機を読むと、わたしが思ったのと同じようです。(まあ、そう思いますよね･･･)

そこで、今回はプライベートで、bluez-dbusを採用したシンプルなアーキテクチャでコードを書きました。前置きが長くなりましたが、本文は短いです。基本的に、[Github](https://github.com/s5uishida/bluetooth-scanner)に書いた内容そのままです。詳細はコードを参照して下さい。

## OSインストール
Raspberry Pi 3BのOSには、[Raspbian Buster Lite OS](https://www.raspberrypi.org/downloads/raspbian/) (2019-07-10)を使用しています。このOSには、bluez-dbusが対応している[BlueZ](http://www.bluez.org/) 5.50が最初から入っており、改めて、BlueZをビルドする必要がありません。

## Javaインストール
ARM版Linux用のJavaは幾つかあると思いますが、わたしは[BELLSOFT](https://bell-sw.com/)のjdk11を[apt-get](https://apt.bell-sw.com/)で入れました。

```
# wget -q -O - https://download.bell-sw.com/pki/GPG-KEY-bellsoft | apt-key add -
# echo "deb [arch=armhf] https://apt.bell-sw.com/ stable main" | tee /etc/apt/sources.list.d/bellsoft.list
# apt-get update
# apt-get install bellsoft-java11
```

## bluetooth-scannerとは
BLEアドバタイズ信号をキャッチするためのモジュールです。用途は、セントラルのBLE通信の範囲にBLEデバイスが入ったことを動的に認識することです。具体的には、CC2650が接近して、セントラルがその存在を認識し、接続するためのトリガーにします。

また、接続しているCC2650がセントラルの通信範囲から外れてBLE通信が切断され(アドバタイズ信号を再開します)、その後、再度、接近して、セントラルがアドバタイズ信号をキャッチして自動的にCC2650に再接続するトリガーとしても想定しています。

**[bluetooth-scanner](https://github.com/s5uishida/bluetooth-scanner)**の処理の肝は、[dbus-java](https://github.com/hypfvieh/dbus-java)が提供する`AbstractPropertiesChangedHandler.java`を`extends`したクラスを用意して、`public void handle(PropertiesChanged properties)`を実装することです。詳細は、[Github](https://github.com/s5uishida/bluetooth-scanner)のコードを参照して下さい。**bluetooth-scanner**の使い方のサンプルは以下の通りです。`IScanHandler`interfaceを実装して`ScanProcess.java`のインスタンスを作成し、`start()`を呼び出します。

```java
import com.github.hypfvieh.bluetooth.DiscoveryFilter;
import com.github.hypfvieh.bluetooth.DiscoveryTransport;
import com.github.hypfvieh.bluetooth.wrapper.BluetoothDevice;

import io.github.s5uishida.iot.bluetooth.scanner.IScanHandler;
import io.github.s5uishida.iot.bluetooth.scanner.ScanData;
import io.github.s5uishida.iot.bluetooth.scanner.ScanProcess;

public class MyScan {
    public static void main(String[] args) throws IOException, InterruptedException {
        Map<DiscoveryFilter, Object> filter = new HashMap<DiscoveryFilter, Object>();
        filter.put(DiscoveryFilter.Transport, DiscoveryTransport.LE);
        
        ScanProcess scanProcess = new ScanProcess("hci0", new MyScanHandler(), filter);
        scanProcess.start();
    }
}

class MyScanHandler implements IScanHandler {
    private static final Logger LOG = LoggerFactory.getLogger(MyScanHandler.class);
    
    @Override
    public void handle(BluetoothDevice device, ScanData data) {
        LOG.info(device.toString());
        LOG.info(data.toString());
    }
}
```

実行ログの例は以下の通りです。なお、行の先頭を省略しています。

```Console
MyScanHandler handle - BluetoothDevice [device=org.bluez:/org/bluez/hci0/dev_24_71_89_06_9D_82:interface org.bluez.Device1, adapter=/org/bluez/hci0, getBluetoothType()=DEVICE, getDbusPath()=/org/bluez/hci0/dev_24_71_89_06_9D_82] 
MyScanHandler handle - [hci0] 24:71:89:06:9D:82 name:CC2650 SensorTag rssi:-63 txPower:0 date:2019-09-03 22:40:25.483
```

## 一連の記事
このシリーズは、以下の記事から構成されます。
1. [動機とコンセプト](https://qiita.com/items/9c31e6fd9b6eb0fa9c96)
2. **Bluetooth LEアドバタイズ信号をJavaでキャッチする(Bluetooth LE / bluez-dbus)(今回)**
　関連するGithubは[こちら](https://github.com/s5uishida/bluetooth-scanner)。
3. [TI SensorTag CC2650から温度/湿度/照度などをJavaで取得する(Bluetooth LE / bluez-dbus)](https://qiita.com/items/79a2de6a7026ef230e9b)
　関連するGithubは[こちら](https://github.com/s5uishida/cc2650-driver)。
4. [MH-Z19BからCO2濃度をJavaで取得する(シリアル通信 / jSerialComm)](https://qiita.com/items/1759498f8f706c4c3314)
　関連するGithubは[こちら](https://github.com/s5uishida/mh-z19b-driver)。
5. [PPD42NSからPM2.5濃度をJavaで取得する(GPIO / Pi4J)](https://qiita.com/items/4ae7784aeaf50df1e3d3)
　関連するGithubは[こちら](https://github.com/s5uishida/ppd42ns-driver)。
6. [産業オートメーション機器の稼動情報をJavaで取得する(OPC-UA / Eclipse Milo)](https://qiita.com/items/60954d356a3ea1048c61)
　関連するGithubは[こちら](https://github.com/s5uishida/rainy-opcua)。
7. [簡易ツールにまとめる](https://qiita.com/s5uishida/items/c56f1444c6856656b128)
　関連するGithubは[こちら](https://github.com/s5uishida/rainy)。
8. [後記](https://qiita.com/s5uishida/items/d2a888eb2d68e72f32ae)

## 追記
[2019.11.16]
簡易ツールの最新情報は、[こちら](https://github.com/s5uishida/rainy)をご参照下さい。