h2zero / NimBLE-Arduino

A fork of the NimBLE library structured for compilation with Arduino, for use with ESP32, nRF5x.
https://h2zero.github.io/NimBLE-Arduino/
Apache License 2.0
670 stars 138 forks source link

Question on how a notify 0x2902 descriptor is set up in the server code #570

Open paulhamsh opened 11 months ago

paulhamsh commented 11 months ago

Hi I am trying to work out how Notify works in the NimBLE-Arudino code but just can't find the right bit. What I think I know:

What I can't work out is where the code creates the 0x2902 descriptor or adds to the subscription list when it is written to. I can find where it blocks you from creating another one!

I was thinking it would either be in NimBLEService in the start function, or in NimBLECharacteristic in the the creation of a characteristic. And that a 0x2902 descriptor would be created and a onWrite callback created for it to add or remove the client from the subscription list. Either that or it is buried in the NimBLE code - there are references in ble_gatts.c in nimble/host/src but not sure that is where I should be looking? Can you point me to the right files / functions to work out how this is done? Thank you!

h2zero commented 11 months ago

This is all taken care of by the library. No need to do anything with the 2902 descriptors here.

paulhamsh commented 11 months ago

Yes - but how, and where in the code? I want to understand how it does it. Thanks

paulhamsh commented 11 months ago

I think I have found where the 0x2902 is created - not in NimBLE-Arduino but in the nimble code itself.
In \src\nimble\nimble\host\src\ble_gatts.c there is the function ble_gatts_register_chr and in there it creates the CCCD if one is needed (calling ble_gatts_register_clt_cfg_dsc)

h2zero commented 11 months ago

Yes, that is where it is created.

paulhamsh commented 11 months ago

So I've traced this through the code, and worked out how the CCCD updates are managed in nimble and NimBLE-Arduino.
It seems that both the nimble library and NimBLE-Arduino cache which clients / centrals are connected and subscribed for notifications - why is this done twice, does NimBLE-Arduino do something additional to nimble?
I made the changes below to NimBLECharacteristic.cpp and it works for notifications - I know that it will break some things because of the code commented out but I just wanted to test that nimble cached the subscribe and could notify.
In summary, I commented out some lines in handleGapEvent to not require desc (between /* PAUL and PAUL END */ and edit of notify to just call ble_gatts_chr_updated(m_handle);

Line 260: handleGapEvent:

int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
                             struct ble_gatt_access_ctxt *ctxt,
                             void *arg)
{
    const ble_uuid_t *uuid;
    int rc;
    struct ble_gap_conn_desc desc;
    NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;

    NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
                                    ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write");

    uuid = ctxt->chr->uuid;
    if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
        switch(ctxt->op) {
            case BLE_GATT_ACCESS_OP_READ_CHR: {
                rc = ble_gap_conn_find(conn_handle, &desc);

/* PAUL
                assert(rc == 0);

                 // If the packet header is only 8 bytes this is a follow up of a long read
                 // so we don't want to call the onRead() callback again.
                if(ctxt->om->om_pkthdr_len > 8 ||
                   pCharacteristic->m_value.size() <= (ble_att_mtu(desc.conn_handle) - 3)) {
                    pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
                    pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc);
                }

PAUL END */  
                ble_npl_hw_enter_critical();
                rc = os_mbuf_append(ctxt->om, pCharacteristic->m_value.data(), pCharacteristic->m_value.size());
                ble_npl_hw_exit_critical(0);
                return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
            }

Line 442: notify

void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification) {
    NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length);

    void ble_gatts_chr_updated(uint16_t chr_val_handle);
    ble_gatts_chr_updated(m_handle);

    NIMBLE_LOGD(LOG_TAG, "@@@  notify: handle: %d", m_handle);
    NIMBLE_LOGD(LOG_TAG, "<< notify");
} // Notify
h2zero commented 11 months ago

Your code works, but I don't understand what you are trying to achieve here?

why is this done twice, does NimBLE-Arduino do something additional to nimble?

This was done for legacy reasons, in the past the notification function would notify all connected clients even though they were not subscribed. This may be a point for optimization now though, thanks.

paulhamsh commented 11 months ago

The code was just to show that nimble also notifies only after a subscribe event. Just a test. And that was because it looked like both nimble and NimBLE-Arduino cached those subscriptions and I wondered why - but if nimble functionality has changed underneath NimBLE-Arduino that makes sense. Thank you - it's clear to me now.