Android与Arduino BLE通信onCharacteristicChanged未被调用问题
Android BLE对接Arduino回显服务 onCharacteristicChanged不触发问题修复
核心问题原因
代码存在三个直接导致通知回调无法触发的问题:
- 漏掉Android端本地通知开关调用:仅做了描述符写入操作,没有调用
BluetoothGatt.setCharacteristicNotification()打开本地特征监听,系统即使收到外设发的通知,也不会向应用层抛onCharacteristicChanged回调。 - 用错了通知开关对应的描述符UUID:BLE协议里控制特征通知/指示开关的**客户端特征配置描述符(CCC)**是SIG标准定义的,固定UUID为
00002902-0000-1000-8000-00805f9b34fb。自定义UUID为beca6057-955c-4f8a-e1e3-56a1633f04b1的是普通用户自定义描述符,向这个描述符写入开启通知的值完全无效,Arduino端不会识别这个操作,不会主动向Android端发通知。 - 存在异步时序问题:BLE描述符写入是异步操作,原有代码没有校验写入结果就直接测试,可能出现描述符还没写成功就发数据的情况。
用nRF Connect能正常收到回显,是因为nRF Connect会自动查找标准2902 CCC描述符写入开关值,不会操作自定义的无关描述符,所以功能正常。
修复步骤
1. 补全Android端本地通知开关代码
在获取到目标特征btGattCharacteristic之后,第一时间调用本地通知开启方法:
// 核心必加代码,漏掉永远收不到回调 boolean enableSuccess = btGatt.setCharacteristicNotification(btGattCharacteristic, true); MainThreadPrint("本地特征通知开启结果:" + enableSuccess);
2. 替换描述符UUID,移除Arduino端多余自定义描述符
Android端不要查找自定义UUID的描述符,改为查找标准CCC描述符,写入开启通知的值:
// 标准CCC描述符固定UUID final UUID CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); BluetoothGattDescriptor cccDescriptor = btGattCharacteristic.getDescriptor(CCC_DESCRIPTOR_UUID); if (cccDescriptor == null) { MainThreadPrint("未找到标准CCC描述符"); } else { cccDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); btGatt.writeDescriptor(cccDescriptor); }
Arduino端删掉手动添加的自定义描述符相关代码即可,ArduinoBLE库在给特征配置BLENotify属性时,会自动挂载标准2902 CCC描述符,不需要手动添加,自定义的描述符和通知逻辑完全无关。修正后的Arduino初始化代码:
#include <ArduinoBLE.h> BLEService echoService("00000000-0000-1000-8000-00805f9b34fb"); BLEStringCharacteristic charac ("741c12b9-e13c-4992-8a5e-fce46dec0bff", BLERead | BLEWrite | BLENotify,40); String var = ""; void setup(){ Serial.begin(9600); while(!Serial); if(!BLE.begin()){ Serial.println("starting BLE failed."); while(1); } BLE.setLocalName("Arduino BLE Echo"); BLE.setAdvertisedService(echoService); echoService.addCharacteristic(charac); BLE.addService(echoService); BLE.advertise(); Serial.println("Bluetooth device active, waiting for connections..."); } // loop逻辑保持不变即可
3. 增加异步回调校验,规避时序问题
在BluetoothGattCallback实现中增加onDescriptorWrite回调,确认描述符写入成功后再进行数据发送测试:
@Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); MainThreadPrint("CCC描述符写入结果:" + (status == BluetoothGatt.GATT_SUCCESS ? "成功" : "失败,错误码:"+status)); }
4. 权限校验
如果应用targetSdkVersion在31及以上(Android 12+),确认已经在Manifest中声明BLUETOOTH_CONNECT、BLUETOOTH_SCAN权限,且完成动态权限申请,否则BLE相关操作会被系统拦截。
内容的提问来源于stack exchange,提问作者tonykrjhc
相关产品推荐
相关产品推荐

