[[Software/Android]] *AndroidでBLE(Bluetooth Low Energy)を使用する [#q1a24fbb] #contents *1.BLE(Bluetooth Low Energy)について [#u0b9f77a] Bluetooth Low Energyの略でBluetooth 4.0規格の一部です。 特徴:~ 超低消費電力(ボタン電池で一年以上動作)~ 2.4GHz帯を使用~ これまでのBluetooth(Classic Bluetooth)との互換性は無し~ 通信距離は2.5mから10m程度~ 同時接続数に制約無し(Classic Bluetoothでは最大7台)~ *2.BLEの通信についての基礎知識 [#s2588a83] BLEはGATT(Generic Attribute Profile)と呼ばれるクライアントサーバモデルで行われます。~ デバイス(サーバ)の仕様はプロファイル、サービス、キャラクタリスティック、ディスクリプタから構成されています。 #ref(./gatt.png); **プロファイル [#m0e8536d] GATT(Generic Attribute Profile)~ BLEデバイス間のデータ交換の論理的な仕様です。~ データ構造の読み出し、データの読み書き、データの変更通知を行います。~ GATTはサービス、キャラクタリスティック、ディスクリプタと呼ばれる3つの要素から構成されています。~ プロファイルは1つ以上のサービスから構成されます。~ **サービス [#i7451cdc] サービスはプロファイルの機能を表します。~ サービスは複数の入れ子になっているサービスと複数のキャラクタリスティックから構成されています。~ **キャラクタリスティック [#f771bd50] 単一の値を持った属性を表します。~ 自身の値と値へのアクセス方法を定義するプロパティ、複数のディスクリプタで構成されています。~ **ディスクリプタ [#y275f105] ディスクリプタはキャラクタリスティックに付加情報が必要な際に用いられる属性値を表します。~ キャラクタリスティックによってはディスクリプタが無いものもあります。~ *3.AndroidでのBLE [#uf86151e] Androidは4.3(API Level 18)から対応しています。 **Bluetooth パッケージ [#q183fa1b] android.bluetooth.*~ **BluetoothManager [#ec596057] Bluetoothの機能管理を行います。~ BluetoothManagerからBluetoothAdapterを取得します。~ **BluetoothAdapter [#bc15a37e] 端末のBluetoothを制御します。~ Bluetoothデバイスのスキャンを行います。~ **BluetoothGatt [#p5ea484e] GATTプロファイルを操作します。~ サービスの検索、キャラクタリスティックのread/write、ディスクリプタの設定を行います。~ **BluetoothDevice [#iceea4d5] BluetoothAdapterのスキャン結果として受け取ります。~ Bluetoothデバイス情報です。~ **BluetoothGattService [#rcc5482d] (GATT)サービス情報です。~ キャラクタリスティック(BluetoothGattCharacteristic)を保持しています。~ **BluetoothGattCharacteristic [#i5902700] キャラクタリスティック情報です。~ ディスクリプタ(BluetoothGattDescriptor)を保持しています(無いケースもあります)~ **BluetoothGattDescriptor [#r1b8377e] ディスクリプタ情報です。~ *4.アプリの実装 [#hed0d226] **パーミッション [#u66c19cc] マニフェスト(AndroidManifest.xml)に以下の記述を追加します。 パーミッション設定 <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> アプリがBLE対応端末のみに対応していることを宣言するには以下を追加します。 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> **初期化 [#f580a6a7] BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); **BLEの有効化 [#b957ce6f] 実行時にBluetoothが利用可能かどうかをチェックし、無効になっている場合はBluetoothを有効にする為の確認ダイアログを表示します。 BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { return false; } // Bluetooth機能が有効になっているかのチェック。無効の場合はダイアログを表示します。 if (mBluetoothAdapter.isEnabled()) { // Bluetoothが利用可 } else { // Bluetoothが利用不可。確認ダイアログを表示 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); activity.startActivityForResult(enableBtIntent, 1); } **スキャン [#bb5b1b12] // 10秒後にスキャンを停止 private static final long SCAN_PERIOD = 10000; ... private void scanLeDevice(final boolean enable) { if (enable) { // Stops scanning after a pre-defined scan period. mHandler.postDelayed(new Runnable() { @Override public void run() { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } ... } private LeDeviceListAdapter mLeDeviceListAdapter; ... // デバイススキャン後のコールバック関数 private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { String msg = "name =" + device.getName() + ", bondstate = " + device.getBondState() + ", address = " + device.getAddress() + ", type" + device.getType() + ", uuid = " + device.getUuid().toString(); Log.d("Scan", msg);} }); } }; **デバイスとの接続 [#qb926923] onLeScan()で返されるBluetoothDevice#connectGatt()を呼び出して接続します。 BluetoothDevice: BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) ***BluetoothGattCallbackの実装 [#l508541c] private final BluetoothGattCallback callback = new BluetoothGattCallback() { // 接続状態が変更されたときに実行される @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { // 接続された if (newState == BluetoothProfile.STATE_CONNECTED) { Log.i(TAG, "Connected to GATT server."); // サービスを検索する Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); // 切断された } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { Log.i(TAG, "Disconnected from GATT server."); } } @Override // サービスの検索結果を返す public void onServicesDiscovered(BluetoothGatt gatt, int status) { // 成功 if (status == BluetoothGatt.GATT_SUCCESS) { // サービスのリストを取得 List<BluetoothGattService> gattService = getSupportedGattServices(address); for(BluetoothGattService service : gattService) { UUID uuid = service.getUuid(); // サービスからCharacteristicsのリストを取得 List<BluetoothGattCharacteristic> charastics = service.getCharacteristics(); // Characteristic に Notification の受信要求を設定 for(BluetoothGattCharacteristic charastic : charastics) { // gatt.setCharacteristicNotification(charastic, true); } } } else { Log.w(TAG, "onServicesDiscovered received: " + status); } } // 読み込み通知 @Override public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { // Characteristicの読込成功 } } // 書き込み通知 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { // Characteristicの書込成功 } ... }; **読込 [#p547ccfa] 読込は非同期の為、読込要求と読込通知とからなります。 BluetoothGatt#readCharacteristic()を実行します。~ 結果はBluetoothGattCallback#onCharacteristicRead()に返ります。~ ***読込要求 [#f7d05b19] mBluetoothGatt.readCharacteristic(characteristic); ***読込通知 [#p01a6e6d] public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { // Characteristicの読込成功 } } **書込 [#wf89816b] 書込は非同期の為、書込要求と書込通知とからなります。 BluetoothGatt#writeCharacteristic()を実行します。~ 結果はBluetoothGattCallback#onCharacteristicWrite()に返ります。~ ***書込要求 [#o6f832ac] mBluetoothGatt.writeCharacteristic(characteristic); ***書込通知 [#c6b1e070] public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) { public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { // Characteristicの書込成功 } } **接続の解除 [#wb4063ed] BluetoothGatt#close()を実行します。 *複数のデバイスを接続する [#l0c9daee] 複数のデバイスを接続する場合、同時に接続処理を行うのではなく、順次接続を行うことが必要です。~ 接続結果を待たずに別のデバイスの接続を行うと動作が不安定になります。~ また、スキャン実行中の接続も動作が不安定になります。~ **[ケース] [#e1877340] デバイスA,B,Cを接続するケース **[処理手順] [#y13c25bb] 1.デバイスAの接続~ BluetoothDevice#connectGatt()~ 2.接続結果を待つ~ BluetoothGattCallback#onConnectionStateChange()~ 3.デバイスBの接続~ BluetoothDevice#connectGatt()~ 4.デバイスBの接続結果を待つ~ BluetoothGattCallback#onConnectionStateChange()~ 5.デバイスCの接続~ BluetoothDevice#connectGatt()~ 6.デバイスCの接続結果を待つ~ BluetoothGattCallback#onConnectionStateChange()~ *Embedded Technology 2015のデモについて [#nc9416ae] Android端末(Nexus7 2013)で6台のBLEデバイスに接続し、操作を行うデモを展示しました。 **同時接続 [#nd6c7738] BLEデバイスを複数接続~ 6台のデバイスを順次接続しています~ #youtube(FCtvmpk3XSE); **LED照明 [#j228c80f] 明るさを6段階で変更~ 個別/同時に操作 #youtube(e8rH0BX88Nc); **スイッチ [#c8b5b54b] 3個のリレーを搭載し、個別/同時にON/OFF操作 #youtube(115ipQ69tik); **LED電球 [#jc7c0129] LED電球(RGB)の色を操作 #youtube(u_36gwa3jsE); ---- RIGHT:YASHIRO Masayuki