続いてserver側となるPeripehralの実装を行いたいと思います。
基本概念はこちらをClient側となるCentralの実装はこちらを
参照してください。
必要なクラスの定義と初期化
1 2 3 4 5 6 7 8 9 10 11 |
private lateinit var bleGattCharacteristic: BluetoothGattCharacteristic private lateinit var bleGattServer: BluetoothGattServer private lateinit var bleLeAdvertiser: BluetoothLeAdvertiser private lateinit var bleAdapter: BluetoothAdapter private lateinit var bleManager: BluetoothManager override fun onCreate(savedInstanceState: Bundle?) { // 省略... bleManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager bleAdapter = bleManager.getAdapter() } |
Advertiseをスタートする
Advertiseは広告とか宣伝などの意味がありますが、AdvertiseをスタートしてそのAdvertiseをcentralが
キャッチして通信を開始するという流れになります。
advertiseを開始するのに BluetoothLeAdvertiser classを使用しますが、
対応していない端末があるので、有効化されているかをチェックします。
1 2 3 4 |
bleLeAdvertiser = bleAdapter.getBluetoothLeAdvertiser() if (bleLeAdvertiser != null) { } |
次に、gattServerに設定するためのservice、characteristic、descriptorの設定をします。
gattの意味がよくわからんかったのでwikiから引用します。
GATT(Generic Attribute Profile 汎用属性(アトリビュート)プロファイル)に定義されているのは、あるデバイスが保持しているデータ要素の検索方法や、書き込み・読み出し・プッシュをおこなうための基本的なデータモデル[25]を定義している。
では、gattServerがserviceを追加するところまで見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 |
// serviceの設定このuuidを元にcentralがserviceを開始することができる btGattService = BluetoothGattService(UUID.fromString(getString(R.string.uuid_service)), BluetoothGattService.SERVICE_TYPE_PRIMARY) // 1.characteristicの設定 bleGattCharacteristic = BluetoothGattCharacteristic(UUID.fromString(getString(R.string.uuid_characteristic)), BluetoothGattCharacteristic.PROPERTY_NOTIFY or BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattDescriptor.PERMISSION_WRITE or BluetoothGattCharacteristic.PERMISSION_READ) btGattService.addCharacteristic(bleGattCharacteristic) // Descriptorの設定 val dataDescriptor = BluetoothGattDescriptor( UUID.fromString(getString(R.string.uuid_characteristic_config)), BluetoothGattDescriptor.PERMISSION_WRITE or BluetoothGattDescriptor.PERMISSION_READ) bleGattCharacteristic.addDescriptor(dataDescriptor) // 結果を受け取るcallbackを入れる bleGattServer = bleManager.openGattServer(this, gattServerCallback) bleGattServer.addService(btGattService) |
1.characteristicの設定
BluetoothGattServiceクラスを生成します。
第1引数にcharacteristicをcentral側が検索できるようにuuidを設定し、第2引数に権限の設定をします。
Descriptorにも同じように必要な権限を与えてください
続いて、advertiseを開始するまでを見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
val dataBuilder = AdvertiseData.Builder() val settingsBuilder = AdvertiseSettings.Builder() dataBuilder.setIncludeTxPowerLevel(false) dataBuilder.addServiceUuid(ParcelUuid.fromString(getString(R.string.uuid_service))) settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) settingsBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) // Start Bluetooth LE Advertising bleLeAdvertiser.startAdvertising(settingsBuilder.build(), dataBuilder.build(), object : AdvertiseCallback() { override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { } override fun onStartFailure(errorCode: Int) { } }) |
startAdvertising関数を呼ぶためにAdvertiseDataとAdvertiseSettingsの設定をする必要があります。
AdvertiseDataの設定
AdvertiseDataには、serviceのuuidをセットするaddServiceUuid関数
とTxPowerLevelをパケットに含めるかを設定する
setIncludeTxPowerLevel関数を呼んでいます。
この関数説明は以下の通りです。
Whether the transmission power level should be included in the advertise packet. Tx power level field takes 3 bytes in advertise packet.
他にも有用的な関数としてパケット内にdeviceの名前を含めるかどうかの設定をする
setIncludeDeviceName関数などがあります。
AdvertiseSettingsの設定
AdvertiseSettingsには、Advertiseを出力するパワーを設定します。
検証してはいませんが、高ければ、消費電力が強くなり
広範囲に飛ぶ(実際に記述にある)ということだと思います。
Advertiseの開始
startAdvertise関数により、Advertiseを開始することができます。
AdvertiseCallbackクラスにより結果を受け取ることができます。
では、advertiseを開始する全体のコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) fun prepareBle() { bleLeAdvertiser = bleAdapter.getBluetoothLeAdvertiser() if (bleLeAdvertiser != null) { val btGattService = BluetoothGattService(UUID.fromString(getString(R.string.uuid_service)), BluetoothGattService.SERVICE_TYPE_PRIMARY) bleGattCharacteristic = BluetoothGattCharacteristic(UUID.fromString(getString(R.string.uuid_characteristic)), BluetoothGattCharacteristic.PROPERTY_NOTIFY or BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattDescriptor.PERMISSION_WRITE or BluetoothGattCharacteristic.PERMISSION_READ) btGattService.addCharacteristic(bleGattCharacteristic) val dataDescriptor = BluetoothGattDescriptor( UUID.fromString(getString(R.string.uuid_characteristic_config)), BluetoothGattDescriptor.PERMISSION_WRITE or BluetoothGattDescriptor.PERMISSION_READ) bleGattCharacteristic.addDescriptor(dataDescriptor) bleGattServer = bleManager.openGattServer(this, gattServerCallback) bleGattServer.addService(btGattService) val dataBuilder = AdvertiseData.Builder() val settingsBuilder = AdvertiseSettings.Builder() dataBuilder.setIncludeTxPowerLevel(false) dataBuilder.addServiceUuid(ParcelUuid.fromString(getString(R.string.uuid_service))) settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) settingsBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) // Start Bluetooth LE Advertising bleLeAdvertiser.startAdvertising(settingsBuilder.build(), dataBuilder.build(), object : AdvertiseCallback() { override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { } override fun onStartFailure(errorCode: Int) { } }) } else { Toast.makeText(this, "Bluetooth Le Advertiser not supported", Toast.LENGTH_SHORT).show() } } |
続いて、central端末からの接続状況などを受け取るBluetoothGattServerCallback内の関数を見ていきます。
central端末の接続・切断を受け取れるのがcallback関数onConnectionChangeです。
ここでは、一定の間隔でcentral側にデータを送信するために、timerを開始し
central側にデータを送信し続けています。
他はUIを更新しています。UIThreadと同期させる必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
override fun onConnectionStateChange(device: android.bluetooth.BluetoothDevice, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { val address = device.address // connect Log.d(TAG, "call onConnectionStateChange newState = " + newState) Log.d(TAG, "device name = " + device.name) Log.d(TAG, "device address = " + address) // create centralDeviceData val bleDevice = BleDevice(device, true) connectedCentralDevices.add(bleDevice) // add list val deviceData = DeviceData(device.address, "new Device") listAdapter.centralList.add(deviceData) runOnUiThread { listAdapter.notifyItemInserted(listAdapter.centralList.size - 1) } // timer start if (timer == null) { timer = Timer() peripheralDataTimerTask = PeripheralDataTimerTask() // 第二引数:最初の処理までのミリ秒 第三引数:以降の処理実行の間隔(ミリ秒). timer?.schedule(peripheralDataTimerTask, 1000, 1000) } } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { Log.d(TAG, "call onConnectionStateChange STATE_DISCONNECTED") for (myDevice in connectedCentralDevices) { if (myDevice.bleDevice == device) { connectedCentralDevices.remove(myDevice) listAdapter.centralList.removeAt(connectedCentralDevices.size) break; } } runOnUiThread { listAdapter.notifyDataSetChanged() } } } |
centralから書き込みリクエストを受けての対応
centralからの書き込みリクエストはonCharacteristicWriteRequest関数で受け取ることができます。
centralで書き込んだcharacteristicの値はBluetoothGattCharacter内で参照することができます。
byteデータで返却されるので、String型を受け取るためには、以下のように書く方法があります。
1 |
receivedNum = characteristic.getStringValue(offset) |
また、centralからperipheral側で受信したかどうかを返却する設定をした場合、
responseNeededにtrueが入ってくるので、sendResponse関数を呼んで、
central側に受信されたことを伝えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
override fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { Log.d(TAG, "call onCharacteristicWriteRequest") super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) // set written value to characteristic. characteristic.value = value // get value receivedNum = characteristic.getStringValue(offset) // search uuid and update message for (list in listAdapter.centralList) { if (device.address == list.uuid) { list.message = receivedNum } } runOnUiThread { listAdapter.notifyDataSetChanged() } if (responseNeeded) { bleGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, value) } } |
だいたいperipehral・centralの受信送信に必要な機能はこんなところだろうと思います。
全体のコードはgithubに上げているのでこちらを参照してください。