it-swarm.dev

Android BLE API: لم يتم تلقي إخطار GATT

الجهاز المستخدم للاختبار: Nexus 4 ، Android 4.3

يعمل الاتصال بشكل جيد ولكن لم يتم استدعاء onCharacteristicChangedMethod الخاص برد الاتصال. ومع ذلك ، فأنا أسجل للحصول على إشعارات باستخدام setCharacteristicNotification(char, true) داخل onServicesDiscovered وتعود تلك الوظيفة إلى حقيقة.

سجل الجهاز (هناك بالفعل لا رسائل على الإطلاق عندما الإخطارات يجب تظهر/يتم إرسالها عبر جهاز 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 (التسجيل للإشعار وما إلى ذلك).

وقد أي شخص آخر من ذوي الخبرة هذا مع حل ممكن؟

67
Boni2k

يبدو أنك نسيت كتابة الواصف الذي يخبر جهاز بليه الخاص بك بالذهاب في هذا الوضع. راجع سطور التعليمات البرمجية التي تتعامل مع واصف في 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? 
}
79
OneWorld

@ Boni2k - لدي نفس المشاكل. في حالتي ، لديّ 3 خصائص للإشعار وحفنة من خصائص القراءة/الكتابة.

ما وجدته هو أن هناك بعض التبعية بين writeGattDescriptor و readCharacteristic. الكل من writeGattDescriptors must تأتي أولاً و كاملة قبل إصدار أي مكالمات 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());
    }
40
miznick

عند تعيين القيمة على واصف بدلاً من وضع descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) ، ضع descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE). تسمى عمليات الاسترجاعات الخاصة بـ onCharacteristicChanged الآن.

11
user2926265

المشكلات التي تمت تجربتها في الإصدارات السابقة من 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.

بتقليل عدد التسجيلات في الإصدارات السابقة ، تمكنا من تجاوز هذا القيد.

6
D. Wescotty

أفترض (أنك لم تقدم شفرة المصدر الخاصة بك) أنك لم تنفذها كـ أرادت 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 مفقود. في هذه الحالة ، أعتقد أنه سيتم تشغيل الإشعارات ذات المستوى المنخفض ، لكن لن يتم الإبلاغ عنها مطلقًا لطبقة التطبيق.

6
Marian Paździoch

حسنًا ، يؤدي اسم API هذا بالتأكيد إلى حدوث بعض الالتباسات لمطور التطبيق إذا لم يكن هو/هي مبرمج خلفية Bluetooth.

من منظور مواصفات Bluetooth الأساسية ، اقتبس من المواصفات الأساسية 4.2 المجلد 3 ، الجزء G الجزء 3.3.3.3 "تكوين خصائص العميل":

قيمة واصف مميزة هو حقل بت. عند تعيين بعض الشيء ، يجب تمكين هذا الإجراء ، وإلا فلن يتم استخدامه.

والقسم 4.10

يمكن تكوين الإشعارات باستخدام واصف توصيف خصائص العميل (انظر القسم 3.3.3.3).

وهو ما ينص بوضوح على أنه إذا أراد العميل استلام الإشعار (أو الإشارة ، التي تحتاج إلى استجابة) من الخادم ، فيجب أن يكتب بت "الإخطار" إلى 1 (بت "الإشارة" أيضًا إلى 1 خلاف ذلك).

ومع ذلك ، فإن اسم "setCharacteristicNotification" يعطينا تلميحًا هو أنه إذا قمنا بتعيين معلمات واجهة برمجة التطبيقات هذه على أنها TURE ، فسيحصل العميل على إعلامات ؛ لسوء الحظ ، يقوم واجهة برمجة التطبيقات هذه فقط بتعيين البت المحلي للسماح للإشعار المرسلة إلى التطبيقات في حالة ظهور إشعار عن بُعد. انظر الكود من 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;
}'

فما يهم هو عمل الكتابة الواصف.

4
Guo Xingmin

إليك طريقة بسيطة للقيام بذلك ، ولكن اسمحوا لي أن أعرف إذا كنت ترى أي عيوب.

الخطوة 1 قم بتعريف المتغيرات المنطقية

private boolean char_1_subscribed = false;
private boolean char_2_subscribed = false;
private boolean char_3_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;
    }
}
1
JBaczuk

كان لدي سبب آخر أود إضافته لأنه دفعني إلى الجنون طوال اليوم:

في Samsung Note 3 ، لم أتلق إعلامات بالقيم التي تم تغييرها أثناء عمل نفس الكود على أي جهاز آخر اختبرته.

إعادة تشغيل الجهاز حل جميع المشاكل. واضح ، لكن عندما تكون في مشكلة ، تنسى التفكير في الأمر.

0
Christian

هذا واحد يعمل بالنسبة لي:

لإشعار الجهاز الرئيسي بأن هناك خاصية ما تتغير ، اتصل بهذه الوظيفة على جهاز الكمبيوتر الخاص بك:

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