Android低功耗蓝牙通讯原理简介

BLE技术与Android开发实践指南

BLE技术概述

1. BLE发展历程

BLE(Bluetooth Low Energy)是专为低功耗应用设计的蓝牙技术,起源于蓝牙4.0规范。与传统蓝牙相比,BLE显著降低了能耗,适合功耗敏感的设备。

2. BLE应用场景

BLE技术在健康监测设备、智能家居、物联网等领域广泛应用,因其低功耗特性而备受青睐,能够实现移动设备与低功耗外设间的快速、稳定连接。

3. BLE技术优势

  • 快速建立连接,简化配对过程,支持即连即用。
  • 低功耗,适合长时间运行的便携式设备。
  • 适中的传输距离,一般在010米,最远可达0100米。

4. BLE技术挑战

  • 与传统蓝牙不兼容,需专门的支持和驱动。
  • 传输速率较低,不适合大数据量传输。

蓝牙GATT协议详解

1. GATT协议概述

GATT(Generic Attribute Profile)基于ATT协议,ATT协议是一种双向的、基于应答的协议。GATT是蓝牙设备间进行数据交换的标准协议之一,用于描述和传输设备之间的数据。


GATT-Based Profile hierarchy

2. GATT协议的核心组件

  • 服务(Service):表示某个功能,如心率服务,包含多个特征。
  • 特征(Characteristic):服务中的数据单元,包含数据和权限。
  • 描述符(Descriptor):描述特征的元数据,如单位、范围等。
  • 角色:Server(服务端)和Client(客户端),分别负责提供数据和读取/写入数据。

3. GATT协议的操作流程

  • 设备连接:Client与Server建立连接。
  • 服务发现:Client查询Server提供的服务。
  • 读取特征:Client从Server读取特征值。
  • 写入特征:Client向Server写入特征值。
  • 启用通知:Client订阅Server的特征通知。

4. GATT协议的常见问题与解决方案

  • 检查特征权限,确保客户端具有读取或写入权限。
  • 优化数据传输频率,减少通知的延迟和丢失。
  • 根据设备性能调整通知频率,避免过载。

Android BLE开发基础

1. BLE概述

BLE是蓝牙4.0规范的一部分,提供低成本、低功耗的无线通信方式。

2. Android BLE API

Android BLE API包括BluetoothAdapter、BluetoothDevice、BluetoothGatt等类,用于扫描设备、连接设备、读取和写入数据。

2.1 BLE连接流程

  • 扫描与广播:BLE设备通过广播发送信息,Android设备扫描这些广播以发现附近的BLE设备。
  • 连接建立:Android设备向发现的BLE设备发送连接请求。
  • 连接参数更新:在连接过程中,可以根据需要更新连接参数。
  • 连接断开:当不再需要数据交互时,可以主动断开连接。

2.2 数据交互机制

  • GATT服务与特性:服务是一组相关特性的集合,特性是数据的实际表示。
  • 读操作:Android设备可以读取BLE设备上特性中的数据。
  • 写操作:Android设备可以向BLE设备写入数据。
  • 通知与指示:BLE设备可以主动向Android设备发送通知。

2.3 数据安全与隐私

  • 加密与认证:保护数据安全,BLE通信可以采用加密和认证机制。
  • 隐私保护:BLE设备可以采用隐私保护措施,如随机化广播地址。
  • 权限控制:Android设备可以设置权限,限制对BLE设备数据的访问。
  • 安全配对:BLE设备间进行配对时,可以设置安全配对流程。

3. Android BLE开发流程

  • 权限申请:动态申请位置权限以进行BLE扫描。
  • 设备扫描:开启BLE扫描,收集周边设备的广告信息。
  • 设备连接:与用户选择的设备建立连接。
  • 数据交互:根据设备的服务和特征,读取或写入数据。

3.1 权限申请

  • 在Android 6.0及以上版本,需要动态申请位置权限以进行BLE扫描。同时,需要在AndroidManifest.xml中声明ACCESS_COARSE_LOCATION和ACCESS_FINE_LOCATION权限。
  • 在Android 12.0及以上版本,引入了 BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE 和 BLUETOOTH_CONNECT 权限,可让您的应用扫描附近的设备,而无需请求位置权限。
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permissionandroid:name="android.permission.BLUETOOTH_PRIVILEGED"
        tools:ignore="ProtectedPermissions" />
    <!--在Android 12以下 这个权限是必须要的-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!--在Android 12以上 可使用以下权限,考虑兼容都加上-->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

3.2 设备扫描

  • 通过BluetoothAdapter.startLeScan扫码低功耗蓝牙设备
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
    // Device does not support Bluetooth
} else {
    bluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // UI操作,显示扫描到的设备
                }
            });
        }
    });
}
  • 通过BluetoothLeScanner.startScan扫码低功耗蓝牙设备
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
    Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();
    return;
}
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
if (scanner != null) {
    scanner.startScan(new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            BluetoothDevice device = result.getDevice();
            // 处理扫描到的设备,如果你需要实时处理每个扫描到的设备,可以使用onScanResult。
            Log.i("TAG", "发现设备: " + device.getName() + " - " + device.getAddress());
        }
        @Override
        public void onBatchScanResults(List < ScanResult > results) {
            super.onBatchScanResults(results);
            for (ScanResult result: results) {
                BluetoothDevice device = result.getDevice();
                // 处理扫描到的设备,如果你希望减少回调次数,提高处理效率,可以使用onBatchScanResults。
                Log.i("TAG", "发现设备: " + device.getName() + " - " + device.getAddress());
            }
        }
        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            Log.e("TAG", "扫描失败: " + errorCode);
        }
    });
} else {
    Log.e("TAG", "无法获取BluetoothLeScanner");
}

3.3 设备连接

BluetoothGatt gatt = device.connectGatt(context, false, new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            gatt.discoverServices();
        }
    }
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // 获取服务和特性,这里具体根据实际设备情况处理
           BluetoothGattService linkLossService = mBluetoothGatt.getService(UUID.fromString("EA3A9D7C-9F3A-4BE5-A1B1-7BF0C7E01A5D"));
           if (linkLossService != null){
               BluetoothGattCharacteristic characteristicNotify = linkLossService.getCharacteristic(UUID.fromString("EA3A9D7C-9F3A-4BE5-A1B2-7BF0C7E01A5D"));
               BluetoothGattCharacteristic characteristicWrite = linkLossService.getCharacteristic(UUID.fromString("EA3A9D7C-9F3A-4BE5-A1B3-7BF0C7E01A5D"));
           }
        }
    }
});

3.4 数据交互

  • 读取和写入特性
BluetoothGattService service = gatt.getService(serviceUUID);
if (service != null) {
    BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUUID);
    if (characteristic != null) {
        gatt.readCharacteristic(characteristic); // 读取数据
        // 写入数据
        characteristic.setValue(data);
        gatt.writeCharacteristic(characteristic);
    }
}
  • 设置通知和写入数据
// 启用通知
mBluetoothGatt.setCharacteristicNotification(characteristicNotify, true);
// 设置通知的客户端描述符
BluetoothGattDescriptor descriptor = characteristicNotify.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (descriptor != null) {
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
}
// 写入数据
boolean resultSet = characteristicWrite.setValue(data);
if (resultSet) {
    boolean resultWrite = mBluetoothGatt.writeCharacteristic(characteristicWrite);
    if (!resultWrite) {
        // 处理写入失败
    }
}
  • 蓝牙BluetoothGattCallback中的结果处理
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        // 处理读取到的数据
    }
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        // 处理写入成功
        if (characteristic.getValue() != null && characteristic.getValue().length > 0){
            byte[] bytes = characteristic.getValue();
        }
    } else {
        // 处理写入失败
    }
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        // 处理描述符写入成功
    } else {
        // 处理描述符写入失败
    }
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
    if (characteristic.getValue() != null && characteristic.getValue().length > 0){
        byte[] bytes = characteristic.getValue();
    }
}

4. BLE开发注意事项

4.1 开发环境问题

  • 环境搭建:确保Android SDK版本与BLE支持版本匹配。
  • SDK选择:选择适合BLE开发的SDK,确保其稳定性和兼容性。
  • 权限设置:确保AndroidManifest.xml中添加了必要的BLE相关权限。

4.2 扫描与连接问题

  • 扫描性能优化:使用合适的扫描周期和窗口,避免长时间扫描导致的资源消耗。
  • 连接稳定性:在连接过程中处理各种异常情况,如连接超时、设备丢失等。
  • 数据传输效率:优化数据包大小,避免过大的数据包导致的传输效率低下。

4.3 服务与特性交互问题

  • 服务发现:在连接后及时发现设备上的服务。
  • 特性读取与写入:注意处理权限问题,有些特性可能需要特定的权限才能操作。
  • 特性通知:确保正确设置通知,以便中央设备能够及时获取更新。

4.4 安全性与隐私问题

  • 数据加密:对敏感数据使用加密机制,确保数据传输的安全性。
  • 设备配对与绑定:在设备配对时,使用适当的配对模式和加密级别。
  • 隐私保护:尊重用户隐私,不收集无关个人信息。

4.5 BLE设备扫描与连接

  • 设备扫描流程:分为主动扫描和被动扫描,主动扫描会请求设备发送完整的广告包。
  • 设备连接机制:连接过程涉及GATT协议,设备间通过交换GATT服务进行通信。
  • 扫描过滤与优化:设置扫描过滤条件可以提高扫描效率。
  • 断开连接与重连策略:应用程序需要处理设备断开连接的情况,并实施重连策略。

4.5 BLE数据传输

  • 数据读取与写入:数据读取通常涉及读取设备上的特征值,可能需要处理权限和配对过程。
  • Notify与Indicate机制:Notify机制允许设备主动发送数据给应用程序,Indicate机制用于确保数据传输的可靠性。
  • 数据加密与安全:在BLE通信中,可以使用加密算法来保护数据传输的安全。
  • 数据传输错误处理:在数据传输过程中,可能会出现错误,需要实施错误处理策略。

4.6 最大传输单位MTU

  • 此选项指定此选项的发送方能够为通道接受的最大SDU大小。
  • 指定小于通道最小MTU的MTU的请求可能会被拒绝。


    基本MTU交换

4.7 点对点连接和点对多点连接

  • 一般只需要点对点连接即可


    具有单个外设操作(a)、多外设操作(b)和分散网操作(c)

4.8 数据分包处理

  • 由于部分产品不支持MTU修改,默认只能传输20字节,为兼容需要分包发送,防止数据丢失。
  • 因为手动分包,Android数据延时不可控,需要在产品端加长延时
public static byte[][] splitByteArray(byte[] input, int chunkSize) {
    if (input == null || chunkSize <= 0) {
        throw new IllegalArgumentException("Invalid input: input cannot be null and chunkSize must be > 0");
    }
    int chunks = (input.length + chunkSize - 1) / chunkSize;
    byte[][] result = new byte[chunks][];
    for (int i = 0, start = 0; i < chunks; i++) {
        int length = Math.min(input.length - start, chunkSize);
        result[i] = new byte[length];
        System.arraycopy(input, start, result[i], 0, length);
        start += length;
    }
    return result;
}

5. Android BLE开发工具与库

  • Android BLE API:Android提供了官方的BLE API,用于简化BLE设备的开发过程。
  • 第三方BLE库:第三方库如BluetoothLe库,FastBle库,提供了更易用的API和更完善的错误处理机制。
  • BLE调试工具:使用BLE调试宝,nRF Connect,可以帮助开发者快速定位和解决问题。
  • 蓝牙核心文档下载
    https://www.bluetooth.com/specifications/specs/ 搜索Core Specification,即可下载文档
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容