BT notifications throttled to 1 packet every 250ms

As the title suggests, we are having issues with BT notifications being throttled.

The function which does the notifications is below:

static void dump_imu_data(void)
{
    if (!current_conn) {
        LOG_WRN("No BLE connection, skipping IMU dump");
        return;
    }

    int ret;
    const flash_partition_t *partition = &flash_partition_map[PARTITION_ID_IMU];

    flash_partition_pointer_t read_pointer = {
        .id = PARTITION_ID_IMU,
        .current_sector = 0,
        .next_seq = 0};

    uint16_t next_seq_no = 0;
    ret = prepare_read_pointer(PARTITION_ID_IMU, &read_pointer, &next_seq_no);
    if (ret < 0) return;

    ble_payload_t ble_payload = {.seq_no = 0, .data = {0}, .crc16 = 0};

    while (read_pointer.next_seq < next_seq_no)
    {
        uint16_t sector_offset = 0;
        ble_payload.seq_no = 0;

        while (sector_offset < FLASH_SECTOR_SIZE)
        {
            size_t bytes_to_read = MIN(BLE_PAYLOAD_DATA_SIZE, FLASH_SECTOR_SIZE - sector_offset);
            memset(ble_payload.data, 0xFF, sizeof(ble_payload.data));

            ret = flash_sector_read(read_pointer.current_sector, sector_offset, bytes_to_read, ble_payload.data);
            if (ret < 0) {
                LOG_ERR("Failed to read flash: %d", ret);
                return;
            }

            ble_payload.crc16 = crc16_ccitt(0, (uint8_t *)&ble_payload, sizeof(ble_payload_t) - 2);

            // Wait until a tx slot is available
            k_sem_take(&tx_slots, K_FOREVER);
            LOG_INF("Sending packet");

            // Send BLE notification (non-blocking)
            nfy_params.data = &ble_payload;
            nfy_params.len = MAX_BLE_PAYLOAD_SIZE;
            nfy_params.func = notify_cb;

            int err = bt_gatt_notify_cb(current_conn, &nfy_params);
            if (err < 0) {
                LOG_ERR("Failed to send BLE payload: %d", err);
                k_sem_give(&tx_slots); // release slot if failed
                continue;
            }

            ble_payload.seq_no++;
            sector_offset += bytes_to_read;
        }

        read_pointer.next_seq++;
        read_pointer.current_sector++;
        if (read_pointer.current_sector >= partition->end_sector)
            read_pointer.current_sector = partition->start_sector;
    }

    // Wait for all in-flight packets to complete
    while (tx_in_flight > 0) {
        k_yield();
    }
}

We loop over sectors of flash memory, reading out chunks of bytes (only enough to fill a BLE packet) and sending them as fast as we can.
We have a semaphore which has 20 slots. The semaphore is refilled by the notify_cb function below:

static void notify_cb(struct bt_conn *conn, void *user_data)
{
    ARG_UNUSED(user_data);
    ARG_UNUSED(conn);
    for (int i = 0; i < IN_FLIGHT_TARGET; i++)
        k_sem_give(&tx_slots);
}

The log statement ("Sending packet") logs to console and gives us an approximate loop time of 250ms (with approx 1ms of variance).
However, the time between the first and second packet is usually approx 1ms, after that it immediately slows down to 250ms per packet.
I have also tested this loop without the call to bt_gatt_notify_cb, and the same log benchmarking strategy yields a loop time of approx 150us.
I also tested the loop with a dummy array of data rather than data read directly from flash (with bt_gatt_notify_cb enabled) and no difference was observed.
I have also confirmed that the average wait time for the semaphore slot is < 1ms.


Our relevant BT configs are:

# Enable Bluetooth
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_PHY_UPDATE=y
CONFIG_BT_USER_PHY_UPDATE=y
CONFIG_BT_SMP=y

# Bluetooth buffer configuration
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_ACL_RX_SIZE=251
CONFIG_BT_L2CAP_TX_BUF_COUNT=10
CONFIG_BT_MAX_PAIRED=1
CONFIG_BT_MAX_CONN=1
CONFIG_BT_ATT_PREPARE_COUNT=5
CONFIG_BT_L2CAP_TX_MTU=251
CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=7500

We configure the connection interval to be 7.5ms (have also tried with 15ms, same result), and this has been verified to be the connection interval that is used by the central machine (linux device running bluez stack, verified with `sudo btmon`).

If I have missed any major pieces of information please let me know, any help is appreciated.
Also there is still a possibility of this being a hardware issue of some sort, but the packets are being successfully sent and received albeit very slowly.
Below I have attached a screenshot of the HF crystal and antenna tuning circuit which is a combination of reference designs from Nordic and Antennova (so likely is not perfectly tuned):

Antennova, Mica antenna datasheet:
www.antenova.com/.../Mica-A5645H-PS-1.0.pdf

Parents Reply Children
No Data
Related