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

Related