Android BLE API:未收到GATT通知

Bon*_*i2k 68 android bluetooth bluetooth-lowenergy gatt android-4.3-jelly-bean

用于测试的设备:Nexus 4,Android 4.3

连接工作正常,但onCharacteristicChanged我的回调方法永远不会被调用.但是我正在使用setCharacteristicNotification(char, true)inside 注册通知onServicesDiscovered,该函数甚至返回true.

设备日志(当通知应该出现/通过蓝牙设备发送时,实际上根本没有消息):

07-28 18:15:06.936  16777-16809/de.ffuf.leica.sketch D/BluetoothGatt: setCharacteristicNotification() - uuid: 3ab10101-f831-4395-b29d-570977d5bf94 enable: true
07-28 18:15:06.936    4372-7645/com.android.bluetooth D/BtGatt.GattService: registerForNotification() - address=C9:79:25:34:19:6C enable: true
07-28 18:15:06.936    4372-7645/com.android.bluetooth D/BtGatt.btif: btif_gattc_reg_for_notification
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1018
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.GattService: onRegisterForNotifications() - address=null, status=0, registered=1, charUuid=3ab10101-f831-4395-b29d-570977d5bf94
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1016
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1018
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.GattService: onRegisterForNotifications() - address=null, status=0, registered=1, charUuid=3ab10102-f831-4395-b29d-570977d5bf94
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1016
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1013
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1013
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.976    4372-7645/com.android.bluetooth D/BtGatt.btif: btif_gattc_upstreams_evt: Event 9
Run Code Online (Sandbox Code Playgroud)

GATT通知使用iOS工作正常,应用程序基本上与Android上相同(注册通知等).

有没有其他人经历过可能的解决方案?

One*_*rld 82

好像你忘了写Descriptor来告诉你的BLE设备进入这种模式.请参阅http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#notification中处理描述符的代码行.

如果不设置此描述符,您将永远不会收到特征的更新.打电话setCharacteristicNotification是不够的.这是一个常见的错误.

代码剪断

protected static final UUID CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

public boolean setCharacteristicNotification(BluetoothDevice device, UUID serviceUuid, UUID characteristicUuid,
        boolean enable) {
    if (IS_DEBUG)
        Log.d(TAG, "setCharacteristicNotification(device=" + device.getName() + device.getAddress() + ", UUID="
                + characteristicUuid + ", enable=" + enable + " )");
    BluetoothGatt gatt = mGattInstances.get(device.getAddress()); //I just hold the gatt instances I got from connect in this HashMap
    BluetoothGattCharacteristic characteristic = gatt.getService(serviceUuid).getCharacteristic(characteristicUuid);
    gatt.setCharacteristicNotification(characteristic, enable);
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
    descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : new byte[] { 0x00, 0x00 });
    return gatt.writeDescriptor(descriptor); //descriptor write operation successfully started? 
}
Run Code Online (Sandbox Code Playgroud)

  • 应该真正记录这些额外的必要步骤.在Android上实现BLE通信时,开发人员需要注意有太多太重的未记录的注意事项. (6认同)
  • 这不是一个错误,它是API中的一个错误.setCharacteristicNotification的重点是启用通知 - *它应该设置这个. (3认同)
  • @ Boni2k GATT_NO_RESOURCES = -128或128; GATT_INTERNAL_ERROR = -127或129; GATT_ERROR = -123或133; GATT_ALREADY_OPEN = -115或141.我是从三星BLE Sdk家伙那里得到的.这些状态代码似乎与Android BLE SDK相同,除了将有符号字节作为类型.整个列表如下:http://img-developer.samsung.com/onlinedocs/samsung_ble_docs_200/index.html (2认同)

小智 41

@ Boni2k - 我有同样的问题.就我而言,我有3个通知特性和少数读/写特性.

我发现的是writeGattDescriptor和之间有一些依赖关系readCharacteristic. 在发出任何readCharacteristic调用之前,所有 writeGattDescriptors 必须先完成完成.

这是我使用的解决方案Queues.现在我收到通知,其他一切正常:

像这样创建两个队列:

private Queue<BluetoothGattDescriptor> descriptorWriteQueue = new LinkedList<BluetoothGattDescriptor>();
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();
Run Code Online (Sandbox Code Playgroud)

然后使用此方法在发现后立即编写所有描述符:

public void writeGattDescriptor(BluetoothGattDescriptor d){
    //put the descriptor into the write queue
    descriptorWriteQueue.add(d);
    //if there is only 1 item in the queue, then write it.  If more than 1, we handle asynchronously in the callback above
    if(descriptorWriteQueue.size() == 1){   
        mBluetoothGatt.writeDescriptor(d);      
    }
}
Run Code Online (Sandbox Code Playgroud)

和这个回调:

public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {         
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "Callback: Wrote GATT Descriptor successfully.");           
        }           
        else{
            Log.d(TAG, "Callback: Error writing GATT Descriptor: "+ status);
        }
        descriptorWriteQueue.remove();  //pop the item that we just finishing writing
        //if there is more to write, do it!
        if(descriptorWriteQueue.size() > 0)
            mBluetoothGatt.writeDescriptor(descriptorWriteQueue.element());
        else if(readCharacteristicQueue.size() > 0)
            mBluetoothGatt.readCharacteristic(readQueue.element());
    };
Run Code Online (Sandbox Code Playgroud)

通常读取特征的方法如下:

public void readCharacteristic(String characteristicName) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(kYourServiceUUIDString));
    BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(characteristicName));
    //put the characteristic into the read queue        
    readCharacteristicQueue.add(c);
    //if there is only 1 item in the queue, then read it.  If more than 1, we handle asynchronously in the callback above
    //GIVE PRECEDENCE to descriptor writes.  They must all finish first.
    if((readCharacteristicQueue.size() == 1) && (descriptorWriteQueue.size() == 0))
        mBluetoothGatt.readCharacteristic(c);              
}
Run Code Online (Sandbox Code Playgroud)

和我读回调:

public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        readCharacteristicQueue.remove();
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);                                
        }
        else{
            Log.d(TAG, "onCharacteristicRead error: " + status);
        }

        if(readCharacteristicQueue.size() > 0)
            mBluetoothGatt.readCharacteristic(readCharacteristicQueue.element());
    }
Run Code Online (Sandbox Code Playgroud)

  • 注意Android中BLE实现的同步特性.当您不使用此类队列时,您可以轻松地取消请求.请参阅http://stackoverflow.com/questions/18011816/has-native-android-ble-gatt-implementation-synchronous-nature?rq=1我不确定,如果你的假设正确,那writeDescriptor()需要在readCharacteristic之前完成.也许你的解决方案(队列)只是照顾同步性并以这种方式修复你的问题.在写入描述符之前,我实际上是在读写特性. (4认同)

小智 11

将值设置为描述符而不是put descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE),put descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE).现在调用onCharacteristicChanged的回调.

  • 这取决于蓝牙设备内部的蓝牙配置文件实现.它使用"通知"或"指示"来发布更新.所以你必须找出你的设备使用哪一个. (2认同)

小智 6

早期版本的Android接收通知(已注册的指示)中经验丰富的问题​​,之后总是有一个奇怪的断开连接事件.事实证明,这是因为我们注册了五个特征的通知.

在LogCat中发现的错误是:

02-05 16:14:24.990    1271-1601/? E/bt-btif? Max Notification Reached, registration failed.
Run Code Online (Sandbox Code Playgroud)

在4.4.2之前,注册数量上限为4!4.4.2将此限制增加到7.

通过减少早期版本中的注册数量,我们能够绕过这一限制.

  • 上限为4?言语无法描述这是多么可悲.即使是7也是荒唐可笑的. (4认同)

Mar*_*och 6

我假设(你没有提供你的源代码)你没有像Google想要的那样实现它:

(1)

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
Run Code Online (Sandbox Code Playgroud)

然后

(2)

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
Run Code Online (Sandbox Code Playgroud)

我想2失踪了.在这种情况下,我相信将触发低级别通知,但它们永远不会报告给应用程序层.