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

MTU change does not change packet size

Setup:

periheral: RF52840, S140 softdevice (SDK 15.3)

central: for test/development: laptop with Linux and BLE adapter build in, and/or Raspberry Pi, using Python testing script, later phone and other nRF device

We are using the NUS peripheral example as a base for our project, and we change the MTU.

I can use my test to change the MTU (btmon also confirms change), and the firmware in the nRF confirms by debug log: MTU changed to 247.

But when I send a packet of 67 bytes from the peripheral, NUS commits 67 bytes into soft device (confirmed by debug logging), and it gets a SUCCESS response, only 20 Bytes get received by central (confirmed by btmon).

What I do see in btmon log: Directly after connecting, the peripheral tries to change the MTU, but gets central sends 'Request not supported'. Next command is the central sending a MTU change request. That succeeds.

  1. In what sequence should we try toi exchange MTU updates?
  2. Where does the first MTU update originate? It is send during connection setup, originating from peripheral.

We've done an MTU change with the Raspberry on an other project before (using Nordic chip, but with Laird smartbasic) so it should be possible.

Note:
Latpop: Python 3, BluePy BLE library (1.3.0), Bluez (5.50)
Raspberry: Python 3, BluePY (1.3.0), Bluez (5.43)

Parents
  • Hi,

    Could you upload the btmon log ?

    Do you have a spare nRF5x-DK ? If yes, then a nRF sniffer v2 log would also be useful.

  • No, at the moment I only have a custom board with a Nordic chip.

    Here is the log: btmon log (available for few days only)

    2 transfers: 1st is OK (log entry 59: less then 20 bytes), second transfer is not OK (line 62, should be 67 Bytes)

  • Update: I've tried with my phone, sending a bogus command and I do get more that 20 Bytes. Same setup, running with my laptop: only 20 Bytes. So it seems to be an issue with the bluetooth stack in the laptop.

  • What is the value of m_ble_nus_max_data_len when you send the 67 bytes? 

    Did ble_nus_data_send() return NRF_SUCCESS ?

    When the SD starts the ATT MTU update exchange, the peer/laptop reports that it does not support the request... but then after service discovery, it starts the update exchange.

    Does the behavior change if SD does not start the ATT MTU update exchange ? In order to test this, try modifying on_connected_evt() function in nrf_ble_gatt.c to this:

    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) && !defined(S312)
        p_link->data_length_desired = p_gatt->data_length;
    #endif // !defined (S112) && !defined(S312)
    
        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) && !defined(S312)
            case BLE_GAP_ROLE_CENTRAL:
                p_link->att_mtu_desired = p_gatt->att_mtu_desired_central;
                break;
    #endif // !defined (S112) && !defined(S312)
    
            default:
                // Ignore.
                break;
        }
    
        // Begin an ATT MTU exchange if necessary.
        bool start_att_mtu_exchange_on_connect = false; 
        if (p_link->att_mtu_desired > p_link->att_mtu_effective && start_att_mtu_exchange_on_connect)
        {
            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) && !defined(S312)
        // 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) && !defined(S312)
    }

Reply
  • What is the value of m_ble_nus_max_data_len when you send the 67 bytes? 

    Did ble_nus_data_send() return NRF_SUCCESS ?

    When the SD starts the ATT MTU update exchange, the peer/laptop reports that it does not support the request... but then after service discovery, it starts the update exchange.

    Does the behavior change if SD does not start the ATT MTU update exchange ? In order to test this, try modifying on_connected_evt() function in nrf_ble_gatt.c to this:

    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) && !defined(S312)
        p_link->data_length_desired = p_gatt->data_length;
    #endif // !defined (S112) && !defined(S312)
    
        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) && !defined(S312)
            case BLE_GAP_ROLE_CENTRAL:
                p_link->att_mtu_desired = p_gatt->att_mtu_desired_central;
                break;
    #endif // !defined (S112) && !defined(S312)
    
            default:
                // Ignore.
                break;
        }
    
        // Begin an ATT MTU exchange if necessary.
        bool start_att_mtu_exchange_on_connect = false; 
        if (p_link->att_mtu_desired > p_link->att_mtu_effective && start_att_mtu_exchange_on_connect)
        {
            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) && !defined(S312)
        // 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) && !defined(S312)
    }

Children
  • Sigurd - I've been messing around with this exact problem for a couple of days now.  I have a product with an NRF52840 built into it, and Im using an RPI 4. 

    I think the problem has something to do with bluepy, and the weird way it directly includes code from the bluez stack, even though most of us have bluez already running as a service in our system.  I found that it isn't my RPI4 that can't handle the data length change, because if I manually activate my NUS service with bluetoothctl, it streams "big" packets just fine.

    However, when using bluepy, I get the same problem described above.  In fact, my data gets truncated at 20 bytes as described in a handful of posts elsewhere on the internet.

    When I modified my code as you described, however, it all seems to work, so your intuition seemed correct.  I'm not sure I really understand why though... in my code, I use the setMTU command to start an MTU negotiation from the Pi after connection.

    I'd really prefer not to modify the Nordic SDK if possible... is there another way to do this?  I'm guessing not.  I'm also curious what the implications for other connections would be if the stack isnt initiating MTU transfer?

  • Mike said:
    I'd really prefer not to modify the Nordic SDK if possible... is there another way to do this?

     If you are using the BLE GATT library, you would need to make a tiny change to get the desired behavior.

    Mike said:
    what the implications for other connections would be if the stack isnt initiating MTU transfer?

     You would be using the default ATT MTU until either master or slave starts an ATT MTU exchange update. If you want to start the exchange later, you could call the sd_ble_gattc_exchange_mtu_request() function.

    This post here is quite old. If you have more questions about this, please create a new post: https://devzone.nordicsemi.com/support/add

  • I had a similar issue wiht nrf52805. This solution worked perfectly, thanks a lot.

Related