Android BLE API: уведомление GATT не получено

Устройство, использованное для тестирования: Nexus 4, Android 4.3.

Соединение работает нормально, но onCharacteristicChangedMethod моего обратного вызова никогда не вызывается. Однако я регистрируюсь для получения уведомлений, используя setCharacteristicNotification(char, true) внутри onServicesDiscovered, и эта функция даже возвращает true.

Журнал устройства (на самом деле нет сообщений вообще, когда уведомления должны появляться / отправляться через устройство Bluetooth):

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

Уведомления GATT отлично работают с iOS, и приложение в основном работает так же, как и на Android (регистрация для получения уведомлений и т. Д.).

Кто-нибудь еще испытал это с возможным решением?


person Boni2k    schedule 28.07.2013    source источник


Ответы (10)


Похоже, вы забыли написать дескриптор, который сообщает вашему устройству 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? 
}
person OneWorld    schedule 02.08.2013
comment
Я только что попробовал код из примеров Android 4.3, но не смог уведомить. Не могли бы вы поделиться фрагментом кодов, связанных с уведомлением? Спасибо. - person ZhangChn; 06.08.2013
comment
Я тоже это сделал, даже с вашим методом (который возвращает true). По-прежнему не повезло. Я использую UUID дескриптора (descUuid), который я нашел в logcat в строке D / BtGatt.GattService: onGetDescriptor () - address = C9: 79: 25: 34: 19: 6C, status = 0, descUuid = 00002902-0000 -1000-8000-00805f9b34fb - person Boni2k; 06.08.2013
comment
Найденный вами UUID совпадает с тем, который я использую. На самом деле это UUID в стандарте BLE. Проверьте, получаете ли вы коды состояния ошибки (кроме 0) в ваших обратных вызовах. Например. connect (), onDescriptorWrite () - person OneWorld; 06.08.2013
comment
@ Boni2k Это кажется глупым вопросом, но действительно ли вы подтвердили, что характеристика имеет дескриптор 0x2902 (здесь для удобства используется 16-битный) - person reTs; 07.08.2013
comment
@reTs Ваша характеристика должна предлагать этот дескриптор (0x2902) для включения уведомлений об обновлении характеристик. - person OneWorld; 07.08.2013
comment
@OneWorld Я знаю, я просто прошу ОП подтвердить, что этот дескриптор действительно существует в характеристике. - person reTs; 07.08.2013
comment
@OneWorld: Спасибо за подсказку. Действительно, я получаю статус 128 внутри onDescriptorWrite, что указывает на ошибку. Хотя я понятия не имею, что может означать этот код состояния ... - person Boni2k; 19.08.2013
comment
@ Boni2k GATT_NO_RESOURCES = -128 или 128; GATT_INTERNAL_ERROR = -127 или 129; GATT_ERROR = -123 или 133; GATT_ALREADY_OPEN = -115 или 141. Получил их от ребят из Samsung BLE Sdk. Эти коды состояния кажутся равными Android BLE SDK, за исключением того, что в качестве типа используется подписанный байт. Полный список находится здесь: img-developer.samsung.com/onlinedocs/samsung_ble_docs_200/ - person OneWorld; 19.08.2013
comment
Спасибо. FWIW, вот глупая ошибка, которую я сделал: ENABLE_INDICATION_VALUE нужно было использовать вместо ENABLE_NOTIFCATION_VALUE ... - person Boni2k; 24.09.2013
comment
хммм, я пробовал это, но все еще никаких обновлений, независимо от того, установил ли я ENABLE_NOTIFICATION_VALUE или ENABLE_INDICATION_VALUE. Вы знаете, как это делает iOS? Мое устройство отлично работает в iOS, и вам не нужно писать какие-либо дескрипторы, поэтому мне интересно, делают ли они это в фоновом режиме или что-то в этом роде? - person phreakhead; 14.12.2013
comment
@OneWorld Вы знаете, где на самом деле найти этот UUID в стандарте BLE? Не могли бы вы предоставить нам URL-адрес? Заранее спасибо! - person Brabbeldas; 03.12.2014
comment
@Brabbeldas Извините, я просто могу помочь вам, указав правильные условия поиска: UUID дескриптора - это 16-битный UUID, который также можно назвать 2902. Вы найдете множество документов в формате PDF на bluetooth.org/en-us/specification/adopted-specifications и доступный для просмотра HTML-контент на developer.bluetooth.org/Pages/default.aspx - person OneWorld; 04.12.2014
comment
Я вижу, что мне нужно вызвать задержку (например, Thread.sleep или Handler.post) между setCharacteristicNotification и writeDescriptor. Если я этого не сделаю, я никогда не получу onCharacteristicChanged. Может ли кто-нибудь подтвердить, что они видят нечто подобное? Обратите внимание, что пример на developer.android.com/guide/topics / connectivity / имеет тонкий ... между двумя вызовами, который, как я вижу, указывает на то, что вы не можете надежно всегда выполнять эти два вызова сразу друг за другом. - person swooby; 07.04.2015
comment
Если мой предыдущий комментарий верен: я не вижу, чтобы у самого setCharacteristicNotification был какой-либо обратный вызов, поэтому может ли кто-нибудь придумать другой способ, кроме задержки перед вызовом writeDescriptor, который может сделать это более надежным? - person swooby; 07.04.2015
comment
Я думаю, это может не иметь ничего общего с задержкой между командами; Я думаю, что это просто задержка в целом до того, как любая попытка установить уведомление даже начнется. Глядя на код для setCharacteristicNotification, кажется, что он не имеет единственного запроса gatt с ограничением по времени. - person swooby; 07.04.2015
comment
Это не ошибка, это ошибка API. Весь смысл setCharacteristicNotification состоит в том, чтобы включить уведомления - он должен это устанавливать. - person Glenn Maynard; 18.05.2015
comment
Эти дополнительные необходимые шаги действительно должны быть задокументированы. Существует слишком много и слишком серьезных недокументированных предостережений, о которых разработчики должны знать при реализации связи BLE на Android. - person Lars Blumberg; 20.11.2015
comment
Мы тесно сотрудничаем с Google над чем-то похожим ... не совсем уверены, что это идентично вашей проблеме, но мы обнаружили, что, когда ОС Google выполняет gatt.WriteDescriptor (), они отправляют его как WRITE_CMD, а не как WRITE_REQUEST. Я считаю, хотя могу ошибаться, что они планируют изменить это так, чтобы это произошло ОБА. Тем временем мы работали над этой проблемой, отвечая либо на CMD, либо на REQUEST для этого сообщения, однако это требует от нас обновления прошивки на нашем устройстве ... и нашим клиентам нужно что-то другое, кроме Android, чтобы сделать это через BLE или USB-кабель. - person Jason Nelson; 05.12.2015
comment
Ничего себе, я не могу поверить, что возвращаюсь к этому вопросу более 9 месяцев спустя. Я недавно видел и иногда видел в прошлом, что параметр feature.getDescriptor (CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID) возвращает значение null. если дескриптор равен нулю, данная последовательность непригодна для использования. Может ли кто-нибудь подумать о том, из-за чего это произошло, или есть ли какое-то обходное решение? - person swooby; 27.01.2016
comment
@swooby Может быть, попробуйте снова просканировать сервисы, когда это произойдет. - person racs; 08.05.2016
comment
Что вы имеете в виду под mGattInstances, как мне получить этот экземпляр? - person mesopotamia; 15.06.2016

@ Boni2k - у меня такие же проблемы. В моем случае у меня есть 3 характеристики уведомления и несколько характеристик чтения / записи.

Я обнаружил, что существует некоторая зависимость между writeGattDescriptor и readCharacteristic. Все дескрипторы writeGattDescriptors должны быть впереди и завершены до того, как будут выполнены какие-либо вызовы readCharacteristic.

Вот мое решение с использованием Queues. Теперь я получаю уведомления, а все остальное работает нормально:

Создайте две такие очереди:

private Queue<BluetoothGattDescriptor> descriptorWriteQueue = new LinkedList<BluetoothGattDescriptor>();
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();

Затем запишите все свои дескрипторы сразу после обнаружения с помощью этого метода:

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);      
    }
}

и этот обратный вызов:

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());
    };

Обычно метод считывания характеристики выглядит следующим образом:

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);              
}

и мой обратный вызов чтения:

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());
    }
person miznick    schedule 13.08.2013
comment
Позаботьтесь о синхронном характере реализации BLE в Android. Вы легко можете отменить запросы по собственной воле, если не используете такие очереди, как вы. См. stackoverflow.com/questions/18011816/ Я не уверен, если вы правы в своем предположении, что writeDescriptor () необходимо выполнить до readCharacteristic. Возможно, ваше решение (очереди) просто заботится о синхронном характере и таким образом решает вашу проблему. Я действительно читаю и записываю характеристики перед написанием дескриптора. - person OneWorld; 19.08.2013
comment
Я верю, что вы правы. Транзакции синхронны, и порядок не имеет значения. Любая транзакция должна завершить обратный вызов до того, как будет выпущена другая транзакция, и все будет работать правильно. Возможно, имеет смысл использовать одну очередь для всех транзакций и просто пометить каждую из них типом (дескриптор записи, характеристика чтения, характеристика записи и т. Д.) - person miznick; 21.08.2013
comment
ваш код готов, но здесь у меня возникла проблема с его использованием, это некоторые проблемы, с которыми я столкнулся, потому что я новичок в BLE. Имеется в виду, что на readCharacteristicQueue и readQueue я получаю ошибки. Приложите полный код, чтобы избавиться от этой ошибки. Спасибо - person Deepak; 30.01.2015
comment
Теоретически код выглядит нормально, но иногда я не получаю обратный вызов onCharacteristicWrite, поэтому ... - person Thomas; 06.02.2015
comment
Nordic написала класс для обработки этого, который называется GattManager. См. github.com/NordicSemiconductor/puck-central-android. - person Timmmm; 20.03.2015
comment
Этот довольно хороший северный код GattManager делает странный Thread.sleep (1000) в своем коде GattSetNotificationOperation (github.com/NordicSemiconductor/puck-central-android/blob/master/ ). Действительно ли этот сон необходим? Я вижу, что если я закомментирую сон, я не получу onCharacteristicChanged. - person swooby; 07.04.2015
comment
Я думаю, это может не иметь ничего общего с задержкой между командами; Я думаю, что это просто задержка в целом до того, как любая попытка установить уведомление даже начнется. Глядя на код для setCharacteristicNotification, кажется, что он не имеет единственного запроса gatt с ограничением по времени. - person swooby; 07.04.2015
comment
Я обнаружил, что после обнаружения службы к характеристике будет прикреплен дескриптор уведомления 0x2902, как и ожидалось. Однако, если я сначала напишу какую-нибудь другую характеристику, этот дескриптор больше не появится в списке getDescriptors() и вернет ноль. Однако, если я найду notify BluetoothGattDescriptor в onServicesDiscovered() и сохраню его на потом, я смогу вернуться к нему позже, установить его значение для включения уведомлений и записать дескриптор. - person Tom K; 26.02.2019
comment
Разве нет случая, когда у вас есть 2 элемента в вашей readqeue, и пока он выполняет чтение, мы добавляем элемент в очередь writeDescriptor, а затем мы записываем в дескриптор одновременно с чтением из очереди чтения? - person Chase Roberts; 27.02.2019

При установке значения в дескриптор вместо descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) поместите descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE). Теперь вызываются обратные вызовы для onCharacteristicChanged.

person user2926265    schedule 27.10.2013
comment
Можете уточнить, что должна делать индикация? Основываясь на документации, я был уверен, что в этом случае нужно установить правильный флаг ENABLE_NOTIFICATION_VALUE, но теперь вы второй человек на этой странице, который предположил, что ENABLE_INDICATION_VALUE - это правильный флаг. В чем разница и какой вариант использования для каждого? - person jkraybill; 28.10.2013
comment
Это зависит от реализации профиля Bluetooth внутри устройства Bluetooth. Он использует либо уведомление, либо индикацию для публикации обновлений. Поэтому вам нужно выяснить, какой из них использует ваше устройство. - person Boni2k; 29.10.2013
comment
Как сказал Boni2k, это зависит от профиля Bluetooth. Если ваш сервис соответствует стандартным профилям, вы сможете найти эту информацию [здесь] (developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx). Если вы хотите узнать это программно, вы можете вызвать метод getProperties () для своей характеристики и выполнить побитовое И для свойства, которое вы хотите проверить, отличается ли оно от 0, тогда оно поддерживает это действие. - person user2926265; 01.11.2013
comment
Что меня беспокоит, так это то, что в документации Android указано, что setCharacteristicNotifications работает ЛИБО для уведомлений или индикаций. Поэтому я предполагаю, что они проверяют свойства внутри и записывают правильные флаги в характеристику. В своем примере они также используют метод, который работал с некоторыми мониторами HeartRate. Вы знаете, ошибочен ли метод? Насколько я понимаю, нельзя установить ОБА флажка индикации / уведомления, хотя некоторые устройства могут это принять. ЕСЛИ они так поступают, я бы сказал, что их метод НЕПРАВИЛЬНЫЙ. - person Brian Reinhold; 02.04.2014

Я предполагаю (вы не предоставили свой исходный код), что вы не реализовали его как Google разыскивал:

(1)

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

а потом

(2)

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

Полагаю, 2 отсутствует. В этом случае я считаю, что уведомления низкого уровня будут срабатывать, но они никогда не будут отправлены на уровень приложения.

person Marian Paździoch    schedule 12.07.2016

Возникли проблемы с получением уведомлений в более ранних версиях Android (указание, что было зарегистрировано) и после этого всегда возникало странное событие отключения. Как оказалось, это произошло потому, что мы зарегистрировались для получения уведомлений по пяти характеристикам.

Ошибка, обнаруженная в LogCat:

02-05 16:14:24.990    1271-1601/? E/bt-btif﹕ Max Notification Reached, registration failed.

До версии 4.4.2 количество регистраций ограничивалось 4! 4.4.2 увеличил этот лимит до 7.

Уменьшая количество регистраций в более ранних версиях, мы смогли обойти это ограничение.

person Dan Wuensch    schedule 05.02.2015
comment
Максимум 4? Невозможно описать словами, насколько это жалко. Даже 7 - это смехотворно. - person Gregory Higley; 14.09.2015

Что ж, это имя API наверняка приведет в замешательство разработчика приложений, если он / она не был фоновым программистом Bluetooth.

С точки зрения базовой спецификации Bluetooth, процитируйте базовую спецификацию 4.2, том 3, часть G, раздел 3.3.3.3 «Конфигурация характеристик клиента»:

Значение характеристического дескриптора - это битовое поле. Когда бит установлен, это действие должно быть разрешено, в противном случае оно не будет использоваться.

и раздел 4.10

Уведомления можно настроить с помощью дескриптора конфигурации характеристик клиента (см. Раздел 3.3.3.3).

в котором четко указано, что если клиент хочет получить уведомление (или индикацию, требующую ответа) от сервера, он должен записать бит «Уведомление» в 1 (бит «Индикация» также в 1 в противном случае).

Однако название «setCharacteristicNotification» подсказывает нам, что если мы установим параметры этого API как TURE, клиент будет получать уведомления; к сожалению, этот API устанавливает только локальный бит, чтобы разрешить отправку уведомлений приложениям в случае получения удаленного уведомления. Смотрите код из Bluedroid:

    /*******************************************************************************
    **
    ** Function         BTA_GATTC_RegisterForNotifications
    **
    ** Description      This function is called to register for notification of a service.
    **
    ** Parameters       client_if - client interface.
    **                  bda - target GATT server.
    **                  p_char_id - pointer to GATT characteristic ID.
    **
    ** Returns          OK if registration succeed, otherwise failed.
    **
    *******************************************************************************/

    tBTA_GATT_STATUS BTA_GATTC_RegisterForNotifications (tBTA_GATTC_IF client_if,
                                                         BD_ADDR bda,
                                                         tBTA_GATTC_CHAR_ID *p_char_id)

{
    tBTA_GATTC_RCB      *p_clreg;
    tBTA_GATT_STATUS    status = BTA_GATT_ILLEGAL_PARAMETER;
    UINT8               i;

    if (!p_char_id)
    {
        APPL_TRACE_ERROR("deregistration failed, unknow char id");
        return status;
    }

    if ((p_clreg = bta_gattc_cl_get_regcb(client_if)) != NULL)
    {
        for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++)
        {
            if ( p_clreg->notif_reg[i].in_use &&
                 !memcmp(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN) &&
                  bta_gattc_charid_compare(&p_clreg->notif_reg[i].char_id, p_char_id))
            {
                APPL_TRACE_WARNING("notification already registered");
                status = BTA_GATT_OK;
                break;
            }
        }
        if (status != BTA_GATT_OK)
        {
            for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++)
            {
                if (!p_clreg->notif_reg[i].in_use)
                {
                    memset((void *)&p_clreg->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG));

                    p_clreg->notif_reg[i].in_use = TRUE;
                    memcpy(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN);

                    p_clreg->notif_reg[i].char_id.srvc_id.is_primary = p_char_id->srvc_id.is_primary;
                    bta_gattc_cpygattid(&p_clreg->notif_reg[i].char_id.srvc_id.id, &p_char_id->srvc_id.id);
                    bta_gattc_cpygattid(&p_clreg->notif_reg[i].char_id.char_id, &p_char_id->char_id);

                    status = BTA_GATT_OK;
                    break;
                }
            }
            if (i == BTA_GATTC_NOTIF_REG_MAX)
            {
                status = BTA_GATT_NO_RESOURCES;
                APPL_TRACE_ERROR("Max Notification Reached, registration failed.");
            }
        }
    }
    else
    {
        APPL_TRACE_ERROR("Client_if: %d Not Registered", client_if);
    }

    return status;
}'

так что важно было действие записи дескриптора.

person Guo Xingmin    schedule 19.07.2016

Вот простой способ сделать это, но дайте мне знать, если вы заметите какие-либо недостатки.

Шаг 1. Объявление логических переменных.

private boolean char_1_subscribed = false;
private boolean char
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
    } else {
        Log.w(TAG, "onServicesDiscovered received: " + status);
    }
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if(!char_1_subscribed)
        subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_1)); char_1_subscribed = true;
}
subscribed = false; private boolean char
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
                                    BluetoothGattCharacteristic characteristic) {
    if(UUID_CHAR_1.equals(characteristic.getUuid()))
    {
        if(!char_1_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_2)); char_2_subscribed = true;
    }
    if(UUID_CHAR_2.equals(characteristic.getUuid()))
    {
        if(!char_3_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_3)); char_3_subscribed = true;
    }
}
subscribed = false;

Шаг 2 подпишитесь на первую характеристику обратного вызова onServicesDiscovered:

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
    } else {
        Log.w(TAG, "onServicesDiscovered received: " + status);
    }
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if(!char_1_subscribed)
        subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_1)); char_1_subscribed = true;
}

Шаг 3

Подпишитесь на всех остальных после срабатывания обратного вызова onCharacteristicChanged

@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
                                    BluetoothGattCharacteristic characteristic) {
    if(UUID_CHAR_1.equals(characteristic.getUuid()))
    {
        if(!char_1_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_2)); char_2_subscribed = true;
    }
    if(UUID_CHAR_2.equals(characteristic.getUuid()))
    {
        if(!char_3_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_3)); char_3_subscribed = true;
    }
}
person JBaczuk    schedule 13.07.2015

Это работает для меня:

чтобы уведомить главное устройство об изменении некоторых характеристик, вызовите эту функцию на своем периферийном устройстве:

private BluetoothGattServer server;
//init....

//on BluetoothGattServerCallback...

//call this after change the characteristic
server.notifyCharacteristicChanged(device, characteristic, false);

на вашем главном устройстве: включите setCharacteristicNotification после обнаружения службы:

@Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        services = mGatt.getServices();
        for(BluetoothGattService service : services){
            if( service.getUuid().equals(SERVICE_UUID)) {
                characteristicData = service.getCharacteristic(CHAR_UUID);
                for (BluetoothGattDescriptor descriptor : characteristicData.getDescriptors()) {
                    descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                    mGatt.writeDescriptor(descriptor);
                }
                gatt.setCharacteristicNotification(characteristicData, true);
            }
        }
        if (dialog.isShowing()){
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    dialog.hide();
                }
            });
        }
   }

теперь вы можете проверить, что значение вашей характеристики изменилось, например, функция onCharacteristicRead (она также работает с функцией onCharacteristicChanged):

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Log.i("onCharacteristicRead", characteristic.toString());
        byte[] value=characteristic.getValue();
        String v = new String(value);
        Log.i("onCharacteristicRead", "Value: " + v);
}
person Doni    schedule 18.12.2016

У меня была еще одна причина, которую я хотел бы добавить, поскольку она сводила меня с ума весь день:

На моем Samsung Note 3 я не получал уведомлений об изменении значений, в то время как тот же код работал на любом другом устройстве, с которым я тестировал.

Перезагрузка устройства решила все проблемы. Очевидно, но когда вы сталкиваетесь с проблемой, вы забываете о ней думать.

person Christian    schedule 07.05.2015

У меня тоже были проблемы с уведомлениями для BLE на Android. Однако есть полностью рабочая демонстрация, которая включает оболочку bluetooth вокруг BluetoothAdapter. Оболочка называется BleWrapper и поставляется с демонстрационным приложением под названием BLEDemo, содержащимся в пакете Application Accelerator. Загрузите здесь: https://developer.bluetooth.org/Pages/Bluetooth-Android-Developers.aspx. Перед загрузкой вам необходимо зарегистрироваться, указав свой адрес электронной почты вверху справа. Лицензия проекта позволяет бесплатное использование, изменение кода и публикацию.

По моему опыту, демонстрационное приложение для Android очень хорошо обрабатывает подписки на уведомления BLE. Я еще не слишком углублялся в код, чтобы увидеть, как на самом деле оборачивается оболочка.

В Play Store доступно приложение для Android, которое представляет собой настройку демонстрации Application accelerator. Поскольку пользовательский интерфейс выглядит почти так же, я полагаю, что он также использует BleWrapper. Загрузите приложение здесь: https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner

person Lars Blumberg    schedule 05.08.2015