How to Resubscribe to Old BLE Handles Upon Reconnection

Hello Nordic Team,

For my application, I am using two nRF54L15 chips with one configured as a master and the other a slave. For the remainder of this post, I will be discussing my Bluetooth Master code. I setup the discovery process to search through all characteristics of the slave device and subscribe to the characteristics that I want notifying.

static struct bt_gatt_subscribe_params notifyStruct[NUM_OF_NOTIFY_CHARS] = {0};

// For initially subscribing to a notification
static void collectNotifyHandle(struct bt_conn *conn, const struct bt_gatt_attr *attr) {
    int err;
    //volatile struct bt_gatt_attr* attrTemp = attr;
    printk("      - - Descriptor Discovered. Val = 0x%x, Handle = %d\n", BT_UUID_16(attr->uuid)->val, attr->handle);

    // Check if Next Notify Characteristic is Set
    switch(nextNotifyChar) {
        //// Femur Sensor Notifies
        case NOTIFY_FS_STATUS:
            notifyStruct[NOTIFY_FS_STATUS].notify = notifyFSStatusCb;
            break;
        case NOTIFY_FS_QUAT:
            notifyStruct[NOTIFY_FS_QUAT].notify = notifyFSQuatCb;
            break;
        case NOTIFY_FS_LOG:
            notifyStruct[NOTIFY_FS_LOG].notify = notifyFSLogCb;
            break;
        case NOTIFY_FS_RAW:
            notifyStruct[NOTIFY_FS_RAW].notify = notifyFSRawCb;
            break;

        //// Glasses Notifies
        case NOTIFY_GL_VALUE:
            notifyStruct[NOTIFY_GL_VALUE].notify = notifyGLValueCb;
            break;

        //// Tablet Notifies
        case NOTIFY_TB_CMD:
            notifyStruct[NOTIFY_TB_CMD].notify = notifyTBCmdCb;
            break;
        case NOTIFY_FWU_INFO:
            notifyStruct[NOTIFY_FWU_INFO].notify = notifyFWUInfoCb;
            break;
        case NOTIFY_FWU_DATA:
            notifyStruct[NOTIFY_FWU_DATA].notify = notifyFWUDataCb;
            break;

        case NUM_OF_NOTIFY_CHARS:
        default:
            // Do Nothing
            break;
    }

    // If Next Notify Characteristic is Set, Grab Generic Subscribe Data
    if (nextNotifyChar < NUM_OF_NOTIFY_CHARS) {
        notifyStruct[nextNotifyChar].value = BT_GATT_CCC_NOTIFY;
        if (attr->uuid->type == BT_UUID_TYPE_128){
            notifyStruct[nextNotifyChar].ccc_handle = attr->handle + 1;
        }else{
            notifyStruct[nextNotifyChar].ccc_handle = attr->handle;
        }

        // Attempt Subscription
        err = bt_gatt_subscribe(conn, &notifyStruct[nextNotifyChar]);
        if (err && err != -EALREADY) {
            printk("[BLE] FAILURE: Subscribe failed (err %d)\n", err);
        } else {
            printk("      - - Subscribed to Handle = %d, Notify = %d\n", attr->handle,  nextNotifyChar);
        }

        // Reset Notify Structure
        nextNotifyChar = NUM_OF_NOTIFY_CHARS;
    }
}

// For receiving a subscribed notification
uint8_t genericNotifyCollect(volatile uint8_t* storageArr, volatile bool* storageFlag, void (*codeInject)(uint8_t*),
    struct bt_gatt_subscribe_params *params, const void *data) {
    int result = BT_GATT_ITER_STOP;

    if ((uint8_t*)data) {
        uint8_t* myData = (uint8_t*)data;

        // Optionally Collect the Data
        if (storageArr != NULL && storageFlag != NULL) {
            *storageFlag = true;
		    oa_memcpy((uint8_t*)storageArr, myData, VND_MAX_LEN);
        }

        // Optionally, Perform Code Injection
        // This drastically reduces the code for some files
        if (codeInject != NULL) {
            codeInject(myData);
        }

		// Continue Receiving Notification
		result = BT_GATT_ITER_CONTINUE;

    } else {
        printk("[BLE] UNSUBSCRIBING: Notify Returned Null: %d\n", params->value_handle);
		params->value_handle = 0U;
    }

	return result;
}


// For re-subscribing to stored handles on re-pair
void subscribeToStoredHandles(struct bt_conn *conn) {
    bool subscribe;

    for (NOTIFY_CHAR_HANDLES_T handle = NOTIFY_FS_STATUS; handle < NUM_OF_NOTIFY_CHARS; handle++) {
        // Reset Subscribe Status
        subscribe = false;

        // Check if Handle is Valid
        if (notifyStruct[handle].ccc_handle != 0) {
            // Determine if Current Connection Matches Handle
            switch(handle) {
                case NOTIFY_FS_STATUS:
                case NOTIFY_FS_QUAT:
                case NOTIFY_FS_LOG:
                case NOTIFY_FS_RAW:
                    if (conn == *ptrFSConn) {
                        subscribe = true;
                    }
                    break;
                case NOTIFY_GL_VALUE:
                    if (conn == *ptrGLConn) {
                        subscribe = true;
                    }
                    break;
                case NOTIFY_TB_CMD:
                case NOTIFY_FWU_INFO:
                case NOTIFY_FWU_DATA:
                    if (conn == *ptrTBConn) {
                        subscribe = true;
                    }
                    break;
                default:
                    break;
            }

            // Perform Subscribe
            if (subscribe) {
                int err = bt_gatt_subscribe(conn, &notifyStruct[handle]);
                if (err) {
                    printk("[BLE] FAILURE: Subscribe failed (err %d)\n", err);
                } else {
                    printk("      - - Subscribed to Handle = %d, Notify = %d\n", notifyStruct[handle].ccc_handle, handle);
                }
            }
        }
    }
}

It is common for my application that the two devices lose connection and have to re-pair. To skip the discover process, I maintain the structures used to write/read/subscribe with the my device and re-use them. Both of my devices will not be software updated during this disconnection, so I am assuming this is safe to do. 

Unfortunately, this tactic is not completely working. I have not thoroughly tested the write/read, but the notifies do not seem to re-subscribe correctly. Currently, two of them are failing to re-subscribe. It seems when the notifications unsubscribe during the disconnect, some of the notify structure variables are changed. I printed out the entire notify structure and found the node member is being set to 0 for the values that are failing to subscribe. My custom logs are shown below that hopefully provide some context. Is there a more proper way to accomplish what I am doing? Or am I missing a step to reset the connection/notifications properly?

[BLE] Primary Service Discovered. Val = 0x1855, Handle = 41, End Handle = 55
      - - Subscribed to value=1, value_handle=45, ccc_handle=46, notify=174433, subscribe=0, node=0,         flags=536904816, handle=0
      - - Subscribed to value=1, value_handle=48, ccc_handle=49, notify=174469, subscribe=0, node=536904820, flags=536904840, handle=1
      - - Subscribed to value=1, value_handle=51, ccc_handle=52, notify=174505, subscribe=0, node=536904844, flags=536904864, handle=2
      - - Subscribed to value=1, value_handle=54, ccc_handle=55, notify=174537, subscribe=0, node=536904868, flags=536904888, handle=3
[BLE] Bluetooth Discovery: Complete


[BLE] UNSUBSCRIBING: Notify Returned Null: 54
[BLE] UNSUBSCRIBING: Notify Returned Null: 51
[BLE] UNSUBSCRIBING: Notify Returned Null: 48
[BLE] UNSUBSCRIBING: Notify Returned Null: 45
[BLE] FS Status: Disconnected 0x8


[BLE] Device Status: Connected
[BLE] Using Cached Handles
      - - Subscribed to      value=1, value_handle=45, ccc_handle=46, notify=174433, subscribe=0, node=0,         flags=536904816, handle=0
      - - Subscribed to      value=1, value_handle=48, ccc_handle=49, notify=174469, subscribe=0, node=536904820, flags=536904840, handle=1
      - - Failed Subscribing value=1, value_handle=51, ccc_handle=52, notify=174505, subscribe=0, node=0,         flags=536904864, handle=2
      - - Failed Subscribing value=1, value_handle=54, ccc_handle=55, notify=174537, subscribe=0, node=0,         flags=536904888, handle=3

Thanks,

Levi

Parents
  • It looks like you are re-using the structures from the previous connection. Do not reuse bt_gatt_subscribe_params structures from a previous connection. Reinitialize these structures before resubscribing. Use bt_gatt_resubscribe() if you are bonded and the server supports persistent subscriptions, but still ensure the parameters are valid and reinitialized. Always set all required fields before calling bt_gatt_subscribe() or bt_gatt_resubscribe().

Reply
  • It looks like you are re-using the structures from the previous connection. Do not reuse bt_gatt_subscribe_params structures from a previous connection. Reinitialize these structures before resubscribing. Use bt_gatt_resubscribe() if you are bonded and the server supports persistent subscriptions, but still ensure the parameters are valid and reinitialized. Always set all required fields before calling bt_gatt_subscribe() or bt_gatt_resubscribe().

Children
  • Hi Susheel,

    I have tested your initial response for resetting the subscribe params and found this yields the same result as before. Additionally, I tried unsubscribing during the disconnected() callback and found that the unsubscribing fails, providing a (-128) socket not connected error. See updated code below with log output. Thoughts?

    Code:

    void subscribeToStoredHandles(struct bt_conn *conn, char* addr) {
        bool subscribe;
    
        for (NOTIFY_CHAR_HANDLES_T handle = NOTIFY_FS_STATUS; handle < NUM_OF_NOTIFY_CHARS; handle++) {
            // Reset Subscribe Status
            subscribe = false;
    
            // Check if Handle is Valid
            if (notifyStruct[handle].ccc_handle != 0) {
                // Determine if Current Connection Matches Handle
                switch(handle) {
                    case NOTIFY_FS_STATUS:
                    case NOTIFY_FS_QUAT:
                    case NOTIFY_FS_LOG:
                    case NOTIFY_FS_RAW:
                        if (conn == *ptrFSConn) {
                            subscribe = true;
                        }
                        break;
                    case NOTIFY_GL_VALUE:
                        if (conn == *ptrGLConn) {
                            subscribe = true;
                        }
                        break;
                    case NOTIFY_TB_CMD:
                    case NOTIFY_FWU_INFO:
                    case NOTIFY_FWU_DATA:
                        if (conn == *ptrTBConn) {
                            subscribe = true;
                        }
                        break;
                    default:
                        break;
                }
    
                // Perform Subscribe
                if (subscribe) {
                    // Create fresh structure for fast subscription
                    struct bt_gatt_subscribe_params fresh = {
                        .notify       = notifyStruct[handle].notify,
                        .value_handle = notifyStruct[handle].value_handle,
                        .ccc_handle   = notifyStruct[handle].ccc_handle,
                        .value        = BT_GATT_CCC_NOTIFY,
                    };
                    notifyStruct[handle] = fresh;
    
                    int err = bt_gatt_subscribe(conn, &notifyStruct[handle]);
                    // int err = bt_gatt_resubscribe(BT_ID_DEFAULT, addr, &notifyStruct[handle]);
                    if (err) {
                        printk("[BLE] FAILURE: Subscribe failed (err %d). value=%d, value_handle=%d, ccc_handle=%d, notify=%d, subscribe=%d, node=%d, flags=%d, handle=%d\n",
                            err,
                            notifyStruct[handle].value,
                            notifyStruct[handle].value_handle,
                            notifyStruct[handle].ccc_handle,
                            notifyStruct[handle].notify,
                            notifyStruct[handle].subscribe,
                            notifyStruct[handle].node,
                            notifyStruct[handle].flags,
                            handle);
                    } else {
                        printk("      - - Subscribed to value=%d, value_handle=%d, ccc_handle=%d, notify=%d, subscribe=%d, node=%d, flags=%d, handle=%d\n",
                            notifyStruct[handle].value,
                            notifyStruct[handle].value_handle,
                            notifyStruct[handle].ccc_handle,
                            notifyStruct[handle].notify,
                            notifyStruct[handle].subscribe,
                            notifyStruct[handle].node,
                            notifyStruct[handle].flags,
                            handle);
    
                    }
                }
            }
        }

    Log:

    00> [BLE] Primary Service Discovered. Val = 0x1855, Handle = 41, End Handle = 55
    00>       - 16-Bit Characteristic Discovered. Val = 0x2b26, Handle = 42
    00>       - 16-Bit Characteristic Discovered. Val = 0x2b29, Handle = 44
    00>       - - Looking for Notify Descriptor...
    00>       - - Descriptor Discovered. Val = 0x2902, Handle = 46
    00>       - - Subscribed to value=1, value_handle=45, ccc_handle=46, notify=176809, subscribe=0, node=0, flags=536904920, handle=0
    00>       - 16-Bit Characteristic Discovered. Val = 0x2b2a, Handle = 47
    00>       - - Looking for Notify Descriptor...
    00>       - - Descriptor Discovered. Val = 0x2902, Handle = 49
    00>       - - Subscribed to value=1, value_handle=48, ccc_handle=49, notify=176845, subscribe=0, node=536904924, flags=536904944, handle=1
    00>       - 16-Bit Characteristic Discovered. Val = 0x2b2c, Handle = 50
    00>       - - Looking for Notify Descriptor...
    00>       - - Descriptor Discovered. Val = 0x2902, Handle = 52
    00>       - - Subscribed to value=1, value_handle=51, ccc_handle=52, notify=176881, subscribe=0, node=536904948, flags=536904968, handle=2
    00>       - 16-Bit Characteristic Discovered. Val = 0x2b2d, Handle = 53
    00>       - - Looking for Notify Descriptor...
    00>       - - Descriptor Discovered. Val = 0x2902, Handle = 55
    00>       - - Subscribed to value=1, value_handle=54, ccc_handle=55, notify=176913, subscribe=0, node=536904972, flags=536904992, handle=3
    00> [BLE] Looking for Next Service...
    00> [BLE] Bluetooth Discovery: Complete
    
    
    00> [BLE] UNSUBSCRIBING: Notify Returned Null: 54
    00> [BLE] UNSUBSCRIBING: Notify Returned Null: 51
    00> [BLE] UNSUBSCRIBING: Notify Returned Null: 48
    00> [BLE] UNSUBSCRIBING: Notify Returned Null: 45
    00> [BLE] FAILURE: Unsubscribe failed (err -128). value=0, value_handle=45, ccc_handle=46, notify=176809, subscribe=0, node=0, flags=536904920, handle=0
    00> [BLE] FAILURE: Unsubscribe failed (err -128). value=0, value_handle=48, ccc_handle=49, notify=176845, subscribe=0, node=0, flags=536904944, handle=1
    00> [BLE] FAILURE: Unsubscribe failed (err -128). value=0, value_handle=51, ccc_handle=52, notify=176881, subscribe=0, node=0, flags=536904968, handle=2
    00> [BLE] FAILURE: Unsubscribe failed (err -128). value=0, value_handle=54, ccc_handle=55, notify=176913, subscribe=0, node=0, flags=536904992, handle=3
    00> [BLE] Device Status: Disconnected 0x8
    
    
    00> [BLE] Device Status: Connected
    00> [BLE] Using Cached Handles
    00>       - - Subscribed to value=1, value_handle=45, ccc_handle=46, notify=176809, subscribe=0, node=0, flags=536904920, handle=0
    00>       - - Subscribed to value=1, value_handle=48, ccc_handle=49, notify=176845, subscribe=0, node=536904924, flags=536904944, handle=1
    00>       - - Subscribed to value=1, value_handle=51, ccc_handle=52, notify=176881, subscribe=0, node=536904948, flags=536904968, handle=2
    00> [BLE] FAILURE: Subscribe failed (err -12). value=1, value_handle=54, ccc_handle=55, notify=176913, subscribe=0, node=0, flags=536904992, handle=3

    I am not bonded with my device, but I also tried using bt_gatt_resubscribe(). Predictably, this does not work either. I did some quick searching for the bonding API but did not find anything. What function must be called to initiate bonding with my peripheral? 

Related