Android Low Energy Bluetooth (BLE) 開発 (2)

前回の記事Android Low Energy Bluetooth (BLE) 開発 (1)では、BLE の関連概念について学習しましたが、ここでは実際にコードを使用して、Android の BLE 接続と通信の機能をデモします。この記事のコードは Android 5.0 以降 (API 21) をベースにしています。

1. 権限を宣言する

AndroidManifest.xml ファイルに BLE 関連の権限宣言を追加します。

<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- 安卓12开始需要下列权限 compileSDK 32+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

<!--安卓6.0以及以上版本需要添加定位的权限 (需要在代码中动态申请)-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" />
    
<!--如果你的app只为具有BLE的设备提供,请声明-->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

2. デバイスが BLE をサポートしているかどうか、および Bluetooth がオンになっているかどうかを確認します。

/**
  *  判断设备是否支持BLE
  */
fun checkSupportBLE(context: Context):Boolean{
    val packageManager: PackageManager = context.packageManager
    return packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
}

/**
  * 判断蓝牙是否打开
  */
fun isBluetoothEnabled(context: Context):Boolean{
    val bluetoothManager = BluetoothAdapter.getDefaultAdapter()
    return bluetoothAdapter == null || bluetoothAdapter?.isEnabled == false
}

3.スキャン

val scanCallback: ScanCallback = object : ScanCallback() {
    override fun onScanResult(callbackType: Int, result: ScanResult?) {
         val device= result?.getDevice()
          // 处理扫描到的设备
    }
}

val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
bluetoothAdapter.bluetoothLeScanner.startScan(scanCallback)

4. 接続を確立して監視する

BluetoothGattCallback で関連するコールバックを監視する

val gattCallback = object : BluetoothGattCallback() {

    override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // 连接成功,进行服务发现
            gatt?.discoverServices()
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // 连接断开,处理断开逻辑
        }
    }

    override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // 服务发现成功,处理服务和特征值
            val services = gatt?.services
            services?.let {
                for (service in it) {
                    // 处理服务和特征值
                }
            }
        } else {
            // 服务发现失败
        }
    }

    override fun onCharacteristicRead(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        value: ByteArray,
        status: Int
    ) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "读取特征值")
            // 从特征值读取数据
            // characteristic 是特征值,而特征值是用 16bit 或者 128bit,16bit 是官方认证过的,128bit 是可以自定义的
            val sucString = characteristic.value
        }
    }

    override fun onCharacteristicWrite(
        gatt: BluetoothGatt?,
        characteristic: BluetoothGattCharacteristic?,
        status: Int
    ) {
        super.onCharacteristicWrite(gatt, characteristic, status)
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "写入特征值")
        }
    }

    override fun onCharacteristicChanged(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        value: ByteArray
    ) {
        Log.i(TAG, "特征值${characteristic.uuid.toString()}变化")
    }

    override fun onDescriptorRead(
        gatt: BluetoothGatt,
        descriptor: BluetoothGattDescriptor,
        status: Int,
        value: ByteArray
    ) {
        Log.i(TAG, "描述符${descriptor.uuid.toString()}读取")
        Log.i(TAG, "描述符值:${String(descriptor.value)}")
    }

    override fun onDescriptorWrite(
        gatt: BluetoothGatt?,
        descriptor: BluetoothGattDescriptor?,
        status: Int
    ) {
        Log.i(TAG, "描述符${descriptor?.uuid.toString()}写入")
    }

    override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) {
        super.onReadRemoteRssi(gatt, rssi, status)
        //rssi值是蓝牙的信号值,离得越远信号越小
        Log.i(TAG, "蓝牙信号值:$rssi")
    }
}

val gatt: BluetoothGatt = bluetoothDevice.connectGatt(context, false, gattCallback)

5. 特性値を読み取る

 /**
   * 读取特征值,读取成功后将回调在BluetoothGattCallback的onCharacteristicRead方法中
   */
fun readCharacteristic(characteristic: BluetoothGattCharacteristic){
    //设置特征值变化通知,必须设置,否则无法监听特征值变化情况
    bluetoothGatt?.setCharacteristicNotification(characteristic, true)
    //读取特征值
    bluetoothGatt?.readCharacteristic(characteristic)
}

6. 特性値の書き込み

/**
  *  写入特征值,完成后将回调在BluetoothGattCallback的onCharacteristicWrite方法中
  */
fun writeCharacteristic(characteristic: BluetoothGattCharacteristic){
    bluetoothGatt?.writeCharacteristic(characteristic)
}

7. 切断する

fun disconnect(){
    bluetoothGatt?.disconnect()
    bluetoothGatt?.close()
}

8. 下請け

AndroidのBLE通信では、送信するデータのサイズがMTU(最大送信単位)の制限を超える場合、データのパケット化処理が必要となります。BLE データのパケットは最大 20 バイトであるため、Android システムで大量のデータを送信するために BLE を使用しないことをお勧めします。以下は、BLE データ パケット送信を実装する一般的な方法です。

(1) MTU サイズを取得します。まず、次のようにBluetoothGattオブジェクトのrequestMtu()メソッドを呼び出して MTU サイズを要求します。

val mtu = 20// 设置期望的MTU大小
bluetoothGatt.requestMtu(mtu)

注: requestMtu の設定は成功しない可能性があります。Bluetooth のバージョンごとに最大値が異なりますので、設定はできるだけ小さくする必要があります。通常、このメソッドは、接続デバイスが onConnectionStateChange で成功したと判断した後に呼び出され、検索が行われますサービス用

(2) MTU 更新の監視: MTU 更新結果をコールバック メソッドで処理しますBluetoothGattCallbackonMtuChanged()

override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        // MTU更新成功,可以开始发送数据
        sendData(bluetoothGatt, data, mtu)
    } else {
        // MTU更新失败,处理失败逻辑
    }
}

(3) データサブパケット送信:送信するデータを MTU サイズに応じて複数のサブパケットに分割し、sum方式BluetoothGattCharacteristic送信します。簡単な例を次に示します。setValue()writeCharacteristic()

/**
  * 往指定特征值写数据,分包
  */
fun sendData(characteristic: BluetoothGattCharacteristic, data: ByteArray, mtu: Int) {
    Thread {
        val packetSize = mtu - 3 // 减去3个字节的包头
        // 将数据拆分为分包并发送
        var offset = 0
        while (offset < data.size) {
        val packet = data.sliceArray(offset until minOf(offset + packetSize, data.size))
            characteristic?.value = packet
            bleClient?.bluetoothGatt?.writeCharacteristic(characteristic)
            offset += packet.size
        }
        Log.d(BleClient.TAG, "发送完毕..")
    }.start()
}

この例では、MTU サイズから 3 バイト (ヘッダー) を減算して、各パケットのサイズを取得します。次に、送信するデータをサブパケットのサイズに応じて複数のサブパケットに分割し、setValue()メソッドを通じてサブパケットのデータを設定し、writeCharacteristic()メソッドを通じてサブパケットを送信します。

各パケットのサイズは、MTU から 3 バイトを引いた値以下である必要があることに注意してください。さらに、データの受信側でも、元のデータが正しく受信されて復元されるように、サブパッケージを結合して処理する必要があります。

おすすめ

転載: blog.csdn.net/gs12software/article/details/131228434