仕事でBluetooth Low Energyを使用したのでまとめ
Bluetooth Low Energy(BLE)とは
Bluetooth Low Energy の概要からの引用。
Bluetooth Low Energy(BLE)は、クラシック Bluetooth と比較して、消費電力を大幅に抑えた設計となっています。そのおかげで Android アプリが、近接センサー、心拍数モニター、フィットネス端末など電力要件が厳しい BLE 端末と通信することが可能になっています。
Bluetooth Low Energy(BLE)ドキュメント
Androidデベロッパー向けのドキュメントのBluetooth Low Energy の概要を確認。
Bluetooth Low Energy(BLE)の参考コード
Androidでの使用方法はAndroidの中に存在する可能性が高いです。そのためAOSPを確認します。
上記内にBLEを使用するための参考コードがあるので参照してください。
ここでは参考コードの解説はしません。
Bluetooth Low Energy(BLE)の実装方法
パーミッションの追加
BLEを使用するためには次の3つのパーミッションを追加する必要がある。
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- If your app targets Android 9 or lower, you can declare
ACCESS_COARSE_LOCATION instead. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
上記パーミッションにはアプリの権限をリクエストする必要があるものが含まれます。
権限のリクエスト方法はアプリの権限をリクエストするを参照してください。
インストールの制限
次の宣言を記述するとBLEサポート端末のみしかインストール出来なくなります。BLE必須の場合は宣言しても良いとは思います。BLEはあくまでも一部の機能である場合は,後述する方法でBLEサポート端末化を確認可能。
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
BLE のセットアップ
Bluetooth
のアクティビティにはBluetoothAdapter
が必要なため,BluetoothAdapterを取得する方法を記載します。
BluetoothAdapter
を取得する
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
BLEサポート端末か確認
- *bluetoothManager.getAdapter()*がnullの場合
Bluetooth
はサポートされていません。 -
PackageManager.hasSystemFeature()
でBLE
を使用可能か判断
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
// BLE not supported
}
Bluetooth
のON,OFF確認
bluetoothAdapter.isEnabled()
でBluetooth
のON,OFF確認を行いOFFの場合は,ONに変更してもらう必要があります。
例:Bluetooth
をONに切り替えてもらうためのインテント発行方法
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
BLE 端末の検出
BLE
デバイスを検索するにはBluetoothLeScannerのインスタンスを取得する必要があります。
次のようにしてBluetoothLeScanner
を取得します。
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothLeScanner bleScanner bluetoothAdapter.getBluetoothLeScanner();
BluetoothLeScanner
のstartScan()
メソッドを使用する事でBLEデバイスの検索を行えます。
bleScanner.startScan(buildScanFilters(), buildScanSettings(), callback);
startScan()
メソッドへ渡すためのList<ScanFilter>
、ScanSettings
は下記のようにして設定します。
/**
* Return a List of {@link android.bluetooth.le.ScanFilter} objects to filter by Service UUID.
*/
private List<ScanFilter> buildScanFilters() {
List<ScanFilter> scanFilters = new ArrayList<>();
ScanFilter.Builder builder = new ScanFilter.Builder();
// Comment out the below line to see all BLE devices around you
builder.setServiceUuid(null);
scanFilters.add(builder.build());
return scanFilters;
}
/**
* Return a {@link android.bluetooth.le.ScanSettings} object set to use low power (to preserve
* battery life).
*/
private ScanSettings buildScanSettings() {
ScanSettings.Builder builder = new ScanSettings.Builder();
builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
return builder.build();
}
startScan()
メソッドへ渡すためのコールバックは下記のように記述します。
対象のBLE端末がスキャンされるたびにonScanResult()が呼び出されます。
/**
* Helper class for BLE scan callback.
*/
public class BleScanCallback extends ScanCallback {
private Set<ScanResult> mResults = new HashSet<>();
private List<ScanResult> mBatchScanResults = new ArrayList<>();
@Override
public void onScanResult(int callbackType, ScanResult result) {
if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
mResults.add(result);
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
// In case onBatchScanResults are called due to buffer full, we want to collect all
// scan results.
mBatchScanResults.addAll(results);
}
}
GATTサーバーへの接続(BLE端末への接続)
BLE端末に接続すること、厳密に言うと、BLE端末上の GATTサーバーに接続することです。BLE端末上の GATTサーバーに接続するには、BluetoothDevice
のconnectGatt()
メソッドを使用します。
BluetoothDevice
のインスタンスは次のように取得します。
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
GATTサーバーへの接続は次のように行います。
BluetoothGatt gatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
connectGatt()
メソッドへ渡すためのコールバックは下記のように記述します。
public class BleGattCallback extends BluetoothGattCallback {
/**
* {@inheritDoc}
*/
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
}
/**
* {@inheritDoc}
*/
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
}
/**
* {@inheritDoc}
*/
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
}
/**
* {@inheritDoc}
*/
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
}
/**
* {@inheritDoc}
*/
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
}
/**
* {@inheritDoc}
*/
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
}
}
-
onConnectionStateChange:接続・切断の際に呼び出されます。接続時は
discoverServices()
メソッドを呼びます。 -
onServicesDiscovered:接続時に
discoverServices()
メソッドを呼び出すと、完了後にこのメソッドが呼び出されます。この後、BLEからの通知を受け取るためのUUID
の登録やMtu
の値変更などすると良いです。 - onCharacteristicWrite:BLEデバイスに向けてAndroidからメッセージを送る(Write)と書き込み完了としてonCharacteristicWriteが呼び出されるようです。
- onCharacteristicChanged:BLEデバイスからAndroidへメッセージが送られるとこのメソッドが呼び出されます。どのUUIDからメッセージが送られたのかを確認して適切な処理を行うのが良いでしょう。
GATTサーバーの切断(BLE端末の切断)
connectGatt()
メソッド呼び出しで受け取ったBluetoothGatt
のdisconnect()
メソッドを呼び出すと切断できます。終了する際は、close()
メソッドも呼び出しておくと良いでしょう。
gatt.disconnect();
gatt.close();
その他注意点
-
onCharacteristicChanged()
メソッドでBLEからメッセージを受け取って、BLEデバイスに何か値を返却するなどあると思いますが、onCharacteristicChanged()
のスレッド内(処理内)でBLEデバイスに書き込むのは非常に危険です。別スレッド(メインスレッド)から書き込みましょう。 - BLEデバイスに対して書き込みを連続で行うのは非常に危険です。書き込んだらその都度
onCharacteristicWrite()
メソッドの書き込み完了を待ちましょう。
サンプルコード
- Java版
- Kotlin版
内容はBLEデバイスをスキャンして、選んだBLEに接続するだけのシンプルなものになります。