You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:36:22