Android Low Energy Bluetooth (BLE) Development (2)

In the previous article Android Low Energy Bluetooth (BLE) Development (1) , we learned about the related concepts of BLE. Here we will actually use the code to demonstrate the function of Android for BLE connection and communication. The code in this article is based on Android 5.0 or above (API 21)

1. Declare permissions

Add BLE-related permission declarations in the AndroidManifest.xml file.

<!-- 蓝牙权限 -->
<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. Determine whether the device supports BLE and whether Bluetooth is turned on

/**
  *  判断设备是否支持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. Scan

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. Establish a connection and monitor

Monitor related callbacks in 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. Read the characteristic value

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

6. Write characteristic value

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

7. Disconnect

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

8. Subcontracting

In Android BLE communication, if the size of the data to be sent exceeds the MTU (Maximum Transmission Unit) limit, data subpacket processing is required. A packet of BLE data is up to 20 bytes, so it is best not to use BLE to transmit a large amount of data under the Android system. The following is a common method to implement BLE data packet transmission:

(1) Get the MTU size: First, request the MTU size by calling the method BluetoothGattof the object , for example:requestMtu()

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

Note: The requestMtu setting may not be successful. Each version of Bluetooth has a different maximum value, and the setting should be as small as possible. Generally, this method is called after judging that the connection device is successful in onConnectionStateChange, and then search for Service

(2) Monitor MTU update: process the MTU update result in the callback method BluetoothGattCallbackin :onMtuChanged()

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

(3) Data sub-packet sending: Split the data to be sent into multiple sub-packets according to the MTU size, and send them BluetoothGattCharacteristicthrough setValue()the sum method. writeCharacteristic()Here is a simple example:

/**
  * 往指定特征值写数据,分包
  */
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()
}

In this example, we subtract 3 bytes (the header) from the MTU size to get the size of each packet. Then, split the data to be sent into multiple sub-packets according to the sub-packet size, setValue()set the sub-packet data through the method, and then writeCharacteristic()send the sub-package through the method.

It should be noted that the size of each packet should be less than or equal to the MTU minus 3 bytes. In addition, the receiving end of the data also needs to combine and process the subpackages to ensure that the original data is correctly received and restored.

Guess you like

Origin blog.csdn.net/gs12software/article/details/131228434