2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Kotlin】省エネbluetooth規格 BLE通信機器情報を取得

Posted at

はじめに

bluetoothの中でも省エネタイプのBLE(Bluetooth Low Energy)を使用した通信機器が増えています。ここではBLE通信が行われている機器数や識別IDの取得方法を記載していきます。

準備

他のサンプルとは異なり準備が意外と大変です。

マニフェストファイルに下記の5行を追加します。

Manifest.xml
    <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つが必要となるため

permission.kt
val permissions = arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION)

このように配列で定義しておいてActivityCompat.requestPermissionsを使用すると便利です。
パーミッションが取得出来たか確認する方法は複数形がないようなので

permission.kt
    for (permission in permissions){
        if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) return false
    }
    return true

このようにループで確認します。

確認の開始と停止

開始方法はAndroid6より前とそれ以降で異なります。Android8からはさらに別の方法も使えるのですが、ここではAndroid6以降の方法を使用します。

開始は

start.kt
    val adapter = BluetoothAdapter.getDefaultAdapter()
    if (adapter == null) return
    val scanner = adapter!!.bluetoothLeScanner
    if (scanner == null) return
    scanner!!.startScan(scanFilterList,scanSettings,mScanCallback)

停止は

stop.kt
    val adapter = BluetoothAdapter.getDefaultAdapter()
    if (adapter == null) return
    val scanner = adapter!!.bluetoothLeScanner
    if (scanner == null) return
    scanner!!.stopScan(mScanCallback)

コールバックは

callback.kt
    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が処理されますのでその中で判断と開始処理を書けばスキャンが始まります。

sample.kt
    override fun onRequestPermissionsResult(requestCode : Int,permissions : Array<String>,grantResults: IntArray){
        if (requestCode == bluetooth.requestCode && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            if (bluetooth.isPermission()) {
                bluetooth.start()
            }
        }
    }
MainActivity.kt
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
    }
}
2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?