はじめに
bluetoothの中でも省エネタイプのBLE(Bluetooth Low Energy)を使用した通信機器が増えています。ここではBLE通信が行われている機器数や識別IDの取得方法を記載していきます。
準備
他のサンプルとは異なり準備が意外と大変です。
マニフェストファイルに下記の5行を追加します。
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
上から順にbluetoothを使用するためのパーミッション、管理者権限で使用するパーミッション。3行目4行目はOSの違いでどちらか片方しか必要ないですが合わせて位置情報を取得するパーミッションが必要であることを示します。
最後の行はbluetoothの中でもBLEしか使いませんよという意味だそうです。
さらにユーザーへの使用許可も複数必要です。
requestPermissionでユーザーに確認を要求するわけですが今回は
Manifest.permission.BLUETOOTH
Manifest.permission.BLUETOOTH_ADMIN
Manifest.permission.ACCESS_FINE_LOCATION
の3つが必要となるため
val permissions = arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION)
このように配列で定義しておいてActivityCompat.requestPermissionsを使用すると便利です。
パーミッションが取得出来たか確認する方法は複数形がないようなので
for (permission in permissions){
if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) return false
}
return true
このようにループで確認します。
確認の開始と停止
開始方法はAndroid6より前とそれ以降で異なります。Android8からはさらに別の方法も使えるのですが、ここではAndroid6以降の方法を使用します。
開始は
val adapter = BluetoothAdapter.getDefaultAdapter()
if (adapter == null) return
val scanner = adapter!!.bluetoothLeScanner
if (scanner == null) return
scanner!!.startScan(scanFilterList,scanSettings,mScanCallback)
停止は
val adapter = BluetoothAdapter.getDefaultAdapter()
if (adapter == null) return
val scanner = adapter!!.bluetoothLeScanner
if (scanner == null) return
scanner!!.stopScan(mScanCallback)
コールバックは
mScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
Log.d("scanResult ", "${result.device.address} ${result.device.name}")
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
Log.d("ScanFailed",errCode.toString())
}
}
正常に取得出来たらonScanResultが、何か異常が発生したらonScanFailedが呼ばれます。
注意
下記ソースで処理開始時にstart()終了時にstop()を呼びますがstartを実行しても取得出来ない場合があります。またstartしたからといって常にスキャンが続くというわけではなく一定時間でスキャンは終了するようです。
そのため実際にプログラムにするときは結果が一定時間返ってこない事を判断してstop()からのstart()処理を行う必要があります。
その他
下記ソースはそのまま使うとBLE機能を持つ様々な機器が反応を返します。
反応があった機器からはresult.device.addressに機器固有のIDが返ってきますがペアリングなどを行わない限りこれ以上の情報は取得出来ません。
そのBLE機器が何かを示すUUIDを指定したい場合はscanFilterAddにUUID "xxxxxxxx-9999-9999-9999-xxxxxxxxxxxx"形式の文字列を渡すと、その機器のみが結果を返すようになります。
ソース
isPermissionでパーミッションが取得出来ていない事を判断したらrequestPermissionでパーミッション取得処理を行います。
ユーザーによる使用許可が得られるとonRequestPermissionsResultが処理されますのでその中で判断と開始処理を書けばスキャンが始まります。
override fun onRequestPermissionsResult(requestCode : Int,permissions : Array<String>,grantResults: IntArray){
if (requestCode == bluetooth.requestCode && grantResults[0] == PackageManager.PERMISSION_GRANTED){
if (bluetooth.isPermission()) {
bluetooth.start()
}
}
}
class BluetoothLeScanner(private val activity : Activity){
var requestCode : Int = 1000
private var enable : Boolean = false
private var adapter : BluetoothAdapter? = null
private var scanner : android.bluetooth.le.BluetoothLeScanner? = null
private lateinit var mScanCallback : ScanCallback
private val permissions = arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION)
private var scanFilter: ScanFilter? = null
private var scanFilterList: ArrayList<ScanFilter> = ArrayList()
fun start(){
if (enable) return
val scanSettings: ScanSettings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_BALANCED)
.build()
mScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
Log.d("scanResult ", "${result.device.address} ${result.device.name}")
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
}
}
adapter = BluetoothAdapter.getDefaultAdapter()
if (adapter == null) return
scanner = adapter!!.bluetoothLeScanner
if (scanner == null) return
scanner!!.startScan(scanFilterList,scanSettings,mScanCallback)
enable = true
}
fun stop(){
if (!enable) return
if (scanner == null) return
scanner!!.stopScan(mScanCallback)
enable = false
}
fun scanFilterAdd(uuid : String){
scanFilter = ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuid)).build()
scanFilterList.add(scanFilter!!)
}
fun scanFilterClr(){
scanFilterList = ArrayList()
}
fun requestPermission(){
ActivityCompat.requestPermissions(activity, permissions, requestCode)
}
fun isPermission() : Boolean{
for (permission in permissions){
if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) return false
}
return true
}
}