This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

MTU negotiation problem

I'm facing a rather strange problem with an nRF52832 board I'm developing for. I'm using SDK 15.2 and S132 v6.0.0.

Sometimes, during the connection/negotiation phase, my device seems to get stuck in a weird state and can't send notifications. I've examined my logs and found out that when it works, I receive BLE_GATTC_EVT_EXCHANGE_MTU_RSP, and when it gets stuck, there is no such event received.

This is an example of a failed connection attempt:

<info> app: BLE_GAP_EVT_CONNECTED, conn handle: 1, role: 1
<debug> app: max_conn_interval: 11 ms
<debug> app: min_conn_interval: 11 ms
<debug> app: slave_latency: 0 connection events
<debug> app: conn_sup_timeout: 2000 ms
<debug> nrf_ble_gatt: Data length updated to 64 on connection 0x1.
<debug> nrf_ble_gatt: max_rx_octets: 64
<debug> nrf_ble_gatt: max_tx_octets: 64
<debug> nrf_ble_gatt: max_rx_time: 2120
<debug> nrf_ble_gatt: max_tx_time: 2120
<debug> app: BLE EVENT: 0x0024
<debug> nrf_ble_gatt: Peer on connection 0x1 requested an ATT MTU of 515 bytes.
<debug> nrf_ble_gatt: Updating ATT MTU to 64 bytes (desired: 64) on connection 0x1.
<debug> app: BLE EVENT: 0x0055
<debug> app: BLE EVENT: 0x0050
<debug> app: BLE_GATTS_EVT_WRITE, handle: 0x0017, len: 2
<error> app: Failed to notify, error: NRF_ERROR_INVALID_STATE (0x0008)

The central I'm connecting to is a Mac. This happens in the logs on the central:

default	12:11:12.512164+0000	bluetoothd	Sending MTU Request - MTU:515 Transport:2
default	12:11:12.555573+0000	bluetoothd	Received MTU Response - Response:64 Requested:515 Transport:2

This is an example of a successful attempt:

<info> app: BLE_GAP_EVT_CONNECTED, conn handle: 1, role: 1
<debug> app: max_conn_interval: 11 ms
<debug> app: min_conn_interval: 11 ms
<debug> app: slave_latency: 0 connection events
<debug> app: conn_sup_timeout: 2000 ms
<debug> nrf_ble_gatt: Data length updated to 64 on connection 0x1.
<debug> nrf_ble_gatt: max_rx_octets: 64
<debug> nrf_ble_gatt: max_tx_octets: 64
<debug> nrf_ble_gatt: max_rx_time: 2120
<debug> nrf_ble_gatt: max_tx_time: 2120
<debug> app: BLE EVENT: 0x0024
<debug> nrf_ble_gatt: Peer on connection 0x1 requested an ATT MTU of 515 bytes.
<debug> nrf_ble_gatt: Updating ATT MTU to 64 bytes (desired: 64) on connection 0x1.
<debug> app: BLE EVENT: 0x0055
<debug> nrf_ble_gatt: ATT MTU updated to 64 bytes on connection 0x1 (response).
<debug> app: BLE EVENT: 0x003A
<debug> app: BLE EVENT: 0x0050

And the central:

default	12:14:35.229238+0000	bluetoothd	Sending MTU Request - MTU:515 Transport:2
default	12:14:35.260755+0000	bluetoothd	Received MTU Request - MTU:64 Transport:2
default	12:14:35.264938+0000	bluetoothd	Received MTU Response - Response:64 Requested:515 Transport:2

Is there anything obvious that could be missing in some parameters/negotiation steps?

Parents
  • Please ensure that the nRF52 waits for the MTU response from the central device before moving on in the application.

    Best regards,

    Simon

  • Even if I'm waiting for the MTU response (0x3A) in my nRF52 code before doing anything, sometimes I do not get the event.

    Also, like you can see in the macOS logs I provided in the original question, when it succeeds, the Mac sends a MTU request (515), then my nRF52 sends an MTU request (64), and then the Mac receives an MTU response (64).

    My question is:

    a) Does the nRF52 have to send an MTU request as a peripheral?
    b) If so, why could it be that sometimes the nRF52 does not send an MTU request, then?

    It seems really logical that the event (0x3A) isn't received if the peripheral does not send a request.

    Edit: One more datapoint: If I simply just don't request the update from the nRF52 peripheral (return early from the on_connected_evt function), it seems to work fine every time. Could there be a timing issue between the nRF52 peripheral and the central sending simultaneous MTU update requests and the nRF52 get's in a confused state?

    From nrf_ble_gatt.c:

    /**@brief Handle a connected event.
     *
     * Begins an ATT MTU exchange procedure, followed by a data length update request as necessary.
     *
     * @param[in]   p_gatt      GATT structure.
     * @param[in]   p_ble_evt   Event received from the BLE stack.
     */
    static void on_connected_evt(nrf_ble_gatt_t * p_gatt, ble_evt_t const * p_ble_evt)
    {
        ret_code_t            err_code;
        uint16_t              conn_handle = p_ble_evt->evt.common_evt.conn_handle;
        nrf_ble_gatt_link_t * p_link      = &p_gatt->links[conn_handle];
    
        // Update the link desired settings to reflect the current global settings.
    #if !defined (S112)
        p_link->data_length_desired = p_gatt->data_length;
    #endif // !defined (S112)
    
        switch (p_ble_evt->evt.gap_evt.params.connected.role)
        {
            case BLE_GAP_ROLE_PERIPH:
                p_link->att_mtu_desired = p_gatt->att_mtu_desired_periph;
                break;
    
    #if !defined (S112)
            case BLE_GAP_ROLE_CENTRAL:
                p_link->att_mtu_desired = p_gatt->att_mtu_desired_central;
                break;
    #endif // !defined (S112)
    
            default:
                // Ignore.
                break;
        }
    
        return;
    
        // Begin an ATT MTU exchange if necessary.
        if (p_link->att_mtu_desired > p_link->att_mtu_effective)
        {
            NRF_LOG_DEBUG("Requesting to update ATT MTU to %u bytes on connection 0x%x.",
                          p_link->att_mtu_desired, conn_handle);
    
            err_code = sd_ble_gattc_exchange_mtu_request(conn_handle, p_link->att_mtu_desired);
    
            if (err_code == NRF_SUCCESS)
            {
                p_link->att_mtu_exchange_requested = true;
            }
            else if (err_code == NRF_ERROR_BUSY)
            {
                p_link->att_mtu_exchange_pending = true;
                NRF_LOG_DEBUG("sd_ble_gattc_exchange_mtu_request()"
                              " on connection 0x%x returned busy, will retry.", conn_handle);
            }
            else
            {
                NRF_LOG_ERROR("sd_ble_gattc_exchange_mtu_request() returned %s.",
                              nrf_strerror_get(err_code));
            }
        }
    
    #if !defined (S112)
        // Send a data length update request if necessary.
        if (p_link->data_length_desired > p_link->data_length_effective)
        {
            (void) data_length_update(conn_handle, p_link->data_length_desired);
        }
    #endif // !defined (S112)
    }
    

    Or is there a standard way to delay the peripheral MTU request, if none is received from the central?

Reply
  • Even if I'm waiting for the MTU response (0x3A) in my nRF52 code before doing anything, sometimes I do not get the event.

    Also, like you can see in the macOS logs I provided in the original question, when it succeeds, the Mac sends a MTU request (515), then my nRF52 sends an MTU request (64), and then the Mac receives an MTU response (64).

    My question is:

    a) Does the nRF52 have to send an MTU request as a peripheral?
    b) If so, why could it be that sometimes the nRF52 does not send an MTU request, then?

    It seems really logical that the event (0x3A) isn't received if the peripheral does not send a request.

    Edit: One more datapoint: If I simply just don't request the update from the nRF52 peripheral (return early from the on_connected_evt function), it seems to work fine every time. Could there be a timing issue between the nRF52 peripheral and the central sending simultaneous MTU update requests and the nRF52 get's in a confused state?

    From nrf_ble_gatt.c:

    /**@brief Handle a connected event.
     *
     * Begins an ATT MTU exchange procedure, followed by a data length update request as necessary.
     *
     * @param[in]   p_gatt      GATT structure.
     * @param[in]   p_ble_evt   Event received from the BLE stack.
     */
    static void on_connected_evt(nrf_ble_gatt_t * p_gatt, ble_evt_t const * p_ble_evt)
    {
        ret_code_t            err_code;
        uint16_t              conn_handle = p_ble_evt->evt.common_evt.conn_handle;
        nrf_ble_gatt_link_t * p_link      = &p_gatt->links[conn_handle];
    
        // Update the link desired settings to reflect the current global settings.
    #if !defined (S112)
        p_link->data_length_desired = p_gatt->data_length;
    #endif // !defined (S112)
    
        switch (p_ble_evt->evt.gap_evt.params.connected.role)
        {
            case BLE_GAP_ROLE_PERIPH:
                p_link->att_mtu_desired = p_gatt->att_mtu_desired_periph;
                break;
    
    #if !defined (S112)
            case BLE_GAP_ROLE_CENTRAL:
                p_link->att_mtu_desired = p_gatt->att_mtu_desired_central;
                break;
    #endif // !defined (S112)
    
            default:
                // Ignore.
                break;
        }
    
        return;
    
        // Begin an ATT MTU exchange if necessary.
        if (p_link->att_mtu_desired > p_link->att_mtu_effective)
        {
            NRF_LOG_DEBUG("Requesting to update ATT MTU to %u bytes on connection 0x%x.",
                          p_link->att_mtu_desired, conn_handle);
    
            err_code = sd_ble_gattc_exchange_mtu_request(conn_handle, p_link->att_mtu_desired);
    
            if (err_code == NRF_SUCCESS)
            {
                p_link->att_mtu_exchange_requested = true;
            }
            else if (err_code == NRF_ERROR_BUSY)
            {
                p_link->att_mtu_exchange_pending = true;
                NRF_LOG_DEBUG("sd_ble_gattc_exchange_mtu_request()"
                              " on connection 0x%x returned busy, will retry.", conn_handle);
            }
            else
            {
                NRF_LOG_ERROR("sd_ble_gattc_exchange_mtu_request() returned %s.",
                              nrf_strerror_get(err_code));
            }
        }
    
    #if !defined (S112)
        // Send a data length update request if necessary.
        if (p_link->data_length_desired > p_link->data_length_effective)
        {
            (void) data_length_update(conn_handle, p_link->data_length_desired);
        }
    #endif // !defined (S112)
    }
    

    Or is there a standard way to delay the peripheral MTU request, if none is received from the central?

Children
Related