JavaでもBLEでIoT〜RaspberryPi Zero WからBLEデバイスにアクセスしてみる。
この記事について
この記事ではRaspberry Pi Zero W上でJavaからBLEデバイスにアクセスする方法を記載しています。Raspberry PiからBLEインタフェースを操作する方法はNode.jsやPythonを使うやり方が多く見られます。これらのライブラリはLinuxのBlutoothスタックであるBluezのライブラリリンクして実行するか、D-BUSインタフェース経由でBuezを呼び出すことで実現されています。そのため、Bluezのインタフェースにアクセスできれば、プログラミング言語に依存せずにBLEにアクセスすることができます。 しかし、いずれの方法も一から実装すると非常に時間がかかるためすでにあるライブラリを利用する方法が良いでしょう。今回はJavaからBluezを呼び出すライブラリを調査し、利用方法をまとめましたので紹介します。サンプルコードは以下のリポジトリに置いています。Raspberry PiやRaspbianに限らずLinux上であれば動かせると思います。
JavaのBLEライブラリ
今回調べた中で、JavaからBluezにアクセスしライブラリを呼び出すためのライブラリは以下の2つがありました。どちらもBluezをD-BUSインタフェース経由で動かしており、ライセンスはMITライセンスとなっています。この2つのライブラリの違いですが、tinybはintel-iot-devkitプロジェクトの中にあるようです。そのため、比較的作りがしっかりしていそうです。その反面、ライブラリを使うまでのコストが高く、ローカルで関連ライブラリをビルドしなければなりません。一方bluez-dbusの方は個人開発のライブラリのようです。tinybにインスパイアされたプロジェクトということですが、Mavenのセントラルリポジトリに登録されています。Maven プロジェクトからダウンロードして利用できるのが非常に使いやすく感じました。また必要なライブラリもapt-get でインストールできるため、今回はこのbluez-dbusを利用してみます。
ライブラリ名 | 備考 |
---|---|
tiny | C++ライブラリも同梱 |
bluez-dbus | Maven central リポジトリに登録 |
Raspberry Pi Zero Wの環境構築
OSはRaspbian STRECHを利用しました。このバージョンからデフォルトインストールのBluezが5.43となります。bluez-dbusはBluez 5.43対応のため、Raspbian JESSEIなどでうまくいかない場合はBluezのバージョンを揃えることをお勧めします。
$uname -a Linux raspberrypi 4.9.41+ #1023 Tue Aug 8 15:47:12 BST 2017 armv6l GNU/Linux
始める前にapt-getのアップデートを実施しておきます。
$sudo apt-get update & apt-get upgrade
libunixsocket-javaのインストール
bluez-dbusが唯一依存しているネイティブライブラリです。d-busのインタフェースライブラリからリンクされています。
$sudo apt-get install libunixsocket-java
Javaのインストール
JavaはOracle Javaをインストールしました。Open JDKでも問題はないと思います。
$ sudo apt-get install openjdk-8-jdk
Mavenのインストール
Mavenも以下のように設定します。
$ wget http://ftp.jaist.ac.jp/pub/apache/maven/maven-3/3.5.0/binaries/apache-maven-3.5.0-bin.tar.gz $ tar xzvf apache-maven-3.5.0-bin.tar.gz $ sudo mv apache-maven-3.5.0 /opt/
環境変数の設定
環境変数にJAVA_HOMEとPATHを設定します。~/.bashrcの末尾に以下を記載します。
$ export JAVA_HOME=$(readlink -f /usr/bin/javac | sed "s:/bin/javac::") $ export PATH=/opt/apache-maven-3.5.0/bin:$JAVA_HOME/bin:$PATH
サンプルコードのビルド
まずはじめにサンプルコードの実行方法を説明します。https://github.com/masato-ka/bluez-dbus-sample.gitをクローンしてください。 クローン後、チェックアウトされたフォルダに入りmavenを使いビルドします。
$git clone https://github.com/masato-ka/bluez-dbus-sample.git $cd bluez-dbus-sample $mvn package
ビルド完了後、libunixsocket-javaのライブラリをコピーします。これはbluez-javaライブラリの仕様によるもので、このライブラリは Javaの実行パスからlibフォルダを探して、libunix-javaを読み込みます。もし存在しなければクラスパスからライブラリを読み込むのですが依存jarの一つであるmatthew.jarにlibunix-java.soが含まれていて読み込みます。しかしこのsoはRaspberry Pi Zero wでは動かないので、あらかじめ実行パスにライブラリをコピーしておきます。
$cd bluez-dbus-sample $mkdir lib $cp /usr/lib/jni/libunix-java.so ./lib/
実はこのライブラリの読み込みにかなりはまりました。
これで実行の準備が整いました。実行にはsudoをつけて実行します。Raspbian STRECH + Bluez 5.43ではD-BUSにBluezのD-BUSインタフェースに接続するのにbluetoothユーザグループかルート権限が必要になります。 以下のフォーラムのようにpiユーザにユーザグループを追加すればsudoは必要なくなるはずです。(私はやってみましたが結局sudoが必要でした。。。)
$cd ~/bluez-dbus-sample $sudo java -jar target/bluz-sample-0.0.1-SNAPSHOT-jar-with-dependencies.jar SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. -----------------HCI Interface--------------------- hci0 -----------------Scan BLE Device--------------------- -----------------Result BLE Device---------------- EnvSensor-BL01 ID115 Choose device:EnvSensor-BL01 //ここで検索したいデバイス名を入力する。 -----------------Characteristics---------------- 00002a05-0000-1000-8000-00805f9b34fb 00002a29-0000-1000-8000-00805f9b34fb 00002a24-0000-1000-8000-00805f9b34fb 00002a25-0000-1000-8000-00805f9b34fb 00002a27-0000-1000-8000-00805f9b34fb 00002a26-0000-1000-8000-00805f9b34fb 0c4c3001-7700-46f4-aa96-d5e974e32a54 0c4c3002-7700-46f4-aa96-d5e974e32a54 0c4c3003-7700-46f4-aa96-d5e974e32a54 0c4c3004-7700-46f4-aa96-d5e974e32a54 0c4c3005-7700-46f4-aa96-d5e974e32a54 0c4c3006-7700-46f4-aa96-d5e974e32a54 0c4c3011-7700-46f4-aa96-d5e974e32a54 0c4c3012-7700-46f4-aa96-d5e974e32a54 0c4c3013-7700-46f4-aa96-d5e974e32a54 0c4c3014-7700-46f4-aa96-d5e974e32a54 0c4c3015-7700-46f4-aa96-d5e974e32a54 0c4c3016-7700-46f4-aa96-d5e974e32a54 0c4c3017-7700-46f4-aa96-d5e974e32a54 0c4c3018-7700-46f4-aa96-d5e974e32a54 0c4c3019-7700-46f4-aa96-d5e974e32a54 0c4c301a-7700-46f4-aa96-d5e974e32a54 0c4c3031-7700-46f4-aa96-d5e974e32a54 0c4c3032-7700-46f4-aa96-d5e974e32a54 0c4c3033-7700-46f4-aa96-d5e974e32a54 0c4c3034-7700-46f4-aa96-d5e974e32a54 0c4c3041-7700-46f4-aa96-d5e974e32a54 0c4c3042-7700-46f4-aa96-d5e974e32a54
実行すると上記のようにHCIインタフェース名、検出されたデバイス名、キャラクタリスティックのUUIDが出力されます。今回は前回の記事でも紹介したOMRONの2JCIE-BL01を対象としました。次にサンプルソースの説明を記載します。
サンプルコードの解説
サンプルコードの全文はGitHubで確認してください。やっつけで書いたので例外処理などあれな部分も含まれています。 サンプルコードの処理は全部で大きく以下の3つの部分に分かれています。
pom.xmlへblues-dbusを依存関係として追加します。
<dependency> <groupId>com.github.hypfvieh</groupId> <artifactId>bluez-dbus</artifactId> <version>0.0.2</version> </dependency>
1 DeviceManagerオブジェクトの取得
try { DeviceManager.createInstance(false); } catch (DBusException e1) { System.out.println("failed create instance caused by D-BUS."); } DeviceManager deviceManager = DeviceManager.getInstance();
- (1) DeviceManagerのインスタンスを作成しています。DeviceManager::createInstance(Boolean)で
true
を設定するとD-BUSのログインセッションを利用し、false
を設定するとシステム管理用セッションを利用します。(ライブラリのコメントから解釈しています。)
2 BLEデバイスのスキャン
List<BluetoothAdapter> result = deviceManager.getAdapters(); BluetoothAdapter bluetoothAdaptor = result.get(0); //(1) System.out.println("-----------------HCI Interface---------------------"); result.stream().map(e->e.getDeviceName()).forEach(System.out::println); System.out.println("-----------------Scan BLE Device---------------------"); bluetoothAdaptor.startDiscovery();//(2) Thread.sleep(5000); bluetoothAdaptor.stopDiscovery();//(3)
- (1) HCIデバイスのオブジェクトを取得します。今回は最初に見つかったHCIインタフェースを利用します。複数インタフェースがある場合(BLEドングルを刺しているなど)は選ぶ必要があります。
- (2) デバイスの検索を開始します。
- (3) 5秒待った後デバイスの検索を停止します。
3 BLEデバイスへの接続とキャラクタリスティックの取得
List<BluetoothDevice> devicies = deviceManager.getDevices(); //(1) devicies.stream().map(e->e.getName()).forEach(System.out::println); BufferedReader buffredReader = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Choose device:"); String deviceName = buffredReader.readLine(); System.out.println("-----------------Characteristics----------------"); for(BluetoothDevice bluetoothDevice : devicies){ if(bluetoothDevice.getName().equals(deviceName)){ try{ bluetoothDevice.connect();//(2) bluetoothDevice.refreshGattServices(); List<BluetoothGattService> servicies = bluetoothDevice.getGattServices();//(3) for(BluetoothGattService service : servicies){ List<BluetoothGattCharacteristic> characteristics = service.getGattCharacteristics();//(4) characteristics.stream().map(e->e.getUuid()).forEach(System.out::println); } }catch(DBusExecutionException e){ System.out.println("FailedConnection"); e.printStackTrace(); } } }
- (1) 検索したデバイスを取得します。
- (2) デバイスへの接続を行います。
- (3) 接続が完了したデバイスからサービスを取得します。
- (4) すべてのサービスからキャラクタリスティックを出力しています。
まとめ
BLEを扱えるJavaのライブラリを探していましたがMavenから追加できてなんとなく動くものが見つかりました。まだまだ発展の余地ありなライブラリですが、ちょっと遊ぶのには良さそうです。またLinux環境のみで開発するのは辛いのでOSXでも動くといいのですが。BLEについては基本的にプラットフォームに依存するためOSを超えての移植性は難しそうです。LinuxであればBluezになりますがOSXではCoreBluetoothをラップする必要があります。WIndowsは。。。Node.jsのnobleであれば全てのプラットフォームに対応できるようです。