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

sd_ble_gatts_hvx returns incorrect data size on MTU > 23 on nrf52840 DK

I have configured the server with max mtu size of 512 and changed the start location of the app to allow for a larger SoftDevice. I have done this both for an mtu of 512 and 247 and 24.

From the Android I request 512 bytes and the negotiation from the server returns the correct value 512 or 247 or 24 depending upon the pre-set max MTU size.

Now its time to do a notification. I am trying to send 85 bytes. With an MTU of 23 I had to split it and send several notifications until all was sent. That worked. Now I expect I should be able to send the 85-byte packet in one try for the 512 and 247 case. However, I get an error 0x000C on the sd_ble_gatts_hvx() call in all cases which translates to 'incorrect data size'. This is followed by the server disconnecting, There is no documentation that explains what might cause this. In spite of everything LOOKING like I should work with the new MTU size, SoftDevice is still acting like it's set to an MTU of 23.

I have asked in another post what the maximum MTU size is per platform and per SoftDevice; information which should be easy to find but nothing is easy to find with the search engine provided by the documentation web app.

What could be causing that error? I assume it must be due to the MTU size since MTU = 23 works.

Does it have something to do with NRF_SDH_BLE_GAP_DATA_LENGTH? How do NRF_SDH_BLE_GAP_DATA_LENGTH and NRF_SDH_BLE_GATT_MAX_MTU_SIZE on the peripheral relate? Can the peripheral request an MTU change?

Parents
  • Hi Brian, 

    Correct, I think it has something to do with NRF_SDH_BLE_GAP_DATA_LENGTH.

    So there are 2 different configuration on different layer here. 
    The ATT_MTU is data size on Attribute layer when the DATA_LENGTH is data size on the link layer. You can have a look at this case

    There is something special about notification is that it can't be split into multiple packets like other command for example read or write command. So the NRF_SDH_BLE_GAP_DATA_LENGTH will decide the maximum notification length (I believe it's NRF_SDH_BLE_GAP_DATA_LENGTH - 4) 

    So in your case you would need to request longer NRF_SDH_BLE_GAP_DATA_LENGTH (maximum 251)

  • Hung,

    At the moment I need to notify and indicate data packets that can be far longer than 251. So I fragment them and each hunk is MTU - 2. So to make bigger hunks I have the peer set the mtU to 512. Setting the MTU size seems to work but that alone is not enough.

    So what you seem to be saying is that I could keep the MTU at 23 and increase the data length to 251 (if accepted by the peer) and then send notifications that are NRF_SDH_BLE_GAP_DATA_LENGTH_MAX - 4 - 2. When data length max is 27, I can only send 20 bytes per hunk so I assume there is more overhead that you did not account for above,

    It seems strange to me that all the SDK examples give the size of the indication/notification hunk as dependent upon the mtu and NOT the data packet length. Is that because of historical reasons from 4.0?

    Is there any advantage of setting the mtu to 512 when the maximum data length is 251?

  • Hi again Brian, 

    There're some relation between them. If you set the ATT_MTU smaller than the data length, the size of the packet transmitted over the radio will be limited by the ATT_MTU. So to utilize the large payload size you need to set ATT_MTU to at least 247 bytes (this includes 3 bytes header of ATT). 

    The advantage of setting MTU to 512 when maximum data length is 251 is that you don't have to manually split the payload if it's over 251 (only applied for read or write command , not notification) as it's done automatically in the ATT layer and link layer. 
    The notification is limited by both the ATT_MTU and the Data length as it can only be sent in only one radio packet. 

  • OKay, I am basically trying to do that (I have only long notifications/indications). But I cannot figure out how to set SoftDevice to use my NRF_SDH_BLE_GAP_DATA_LENGTH. (Note I am only using SoftDevice). I assumed the method sd_ble_gap_data_length_update() would do this but no. After I call that method in response to the client update request where I set my server parameters to the client parameters, I get no error but the BLE_GAP_EVT_DATA_LENGTH_UPDATE event shows my settings were ignored.

    Here is what I do:

    case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
            {
                ble_gap_data_length_limitation_t limitation;
                ble_gap_data_length_params_t server;
                NRF_LOG_DEBUG("Data length update request: max_rx_octets %d, max_rx_time %d, "
                    "max_tx_octets %d, max_tx_time %d",
                    p_ble_evt->evt.gap_evt.params.data_length_update_request.peer_params.max_rx_octets,
                    p_ble_evt->evt.gap_evt.params.data_length_update_request.peer_params.max_rx_time_us,
                    p_ble_evt->evt.gap_evt.params.data_length_update_request.peer_params.max_tx_octets,
                    p_ble_evt->evt.gap_evt.params.data_length_update_request.peer_params.max_tx_time_us);
                server.max_rx_octets = p_ble_evt->evt.gap_evt.params.data_length_update_request.peer_params.max_tx_octets;
                server.max_rx_time_us = BLE_GAP_DATA_LENGTH_AUTO;
                server.max_tx_octets = p_ble_evt->evt.gap_evt.params.data_length_update_request.peer_params.max_rx_octets;
                server.max_tx_time_us = BLE_GAP_DATA_LENGTH_AUTO;
                uint32_t result = sd_ble_gap_data_length_update(
                    p_ble_evt->evt.gap_evt.conn_handle,
                    &server,
                    &limitation);
                NRF_LOG_DEBUG("Result %d", result);
                if (result != NRF_SUCCESS)
                {
                    NRF_LOG_DEBUG("RX limit %d  tx limit %d", limitation.rx_payload_limited_octets, limitation.tx_payload_limited_octets);
                }
                used = true;
            }
            break;
    
            case BLE_GAP_EVT_DATA_LENGTH_UPDATE:
            {
                NRF_LOG_DEBUG("Data length update: max_rx_octets %d, max_rx_time %d, "
                    "max_tx_octets %d, max_tx_time %d",
                    p_ble_evt->evt.gap_evt.params.data_length_update.effective_params.max_rx_octets,
                    p_ble_evt->evt.gap_evt.params.data_length_update.effective_params.max_rx_time_us,
                    p_ble_evt->evt.gap_evt.params.data_length_update.effective_params.max_tx_octets,
                    p_ble_evt->evt.gap_evt.params.data_length_update.effective_params.max_tx_time_us);
            }
            used = true;
            break;
    

  • Please follow the message sequence chart: https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s132.api.v7.2.0/group___b_l_e___g_a_p___d_a_t_a___l_e_n_g_t_h___u_p_d_a_t_e___p_r_o_c_e_d_u_r_e___m_s_c.html?cp=4_7_3_1_2_1_5_6

    You would need to call sd_ble_gap_data_length_update() before or after you get connected, not only when you receive BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST event. This event is only applied when the peer actually request new data length (which might not happens all the time)

    Please try capture sniffer trace when you test. 

Reply Children
  • Yes I have looked at that diagram which makes your comment "You would need to call sd_ble_gap_data_length_update() before or after you get connected," make no sense. How can I send this to the peer when the peer is not connected? I need a connection handle!!!

    I agree that the peer may not always send such requests but when it does and I do the update and the BLE_GAP_EVT_DATA_LENGTH_UPDATE event shows the server tx size is 251, SoftDevice is STILL limiting me to 20 bytes on the notification. There is something missing BEFORE I even thing about playing with these sd_..._update actions.

    here is a log. Everything goes well until I try and send a packet greater than 20 bytes.

    <debug> app: Button press done to start advertising
    <debug> app: Started advertising at time 91801
    <debug> app: Connection event received at time 92992.
    <debug> app: Failed updating persistent sys attr info. Error code: 0x0B
    <debug> app: Data length update request: max_rx_octets 251, max_rx_time 2120, max_tx_octets 27, max_tx_time 328
    <debug> app: Result 0
    <debug> app: Data length update: max_rx_octets 27, max_rx_time 328, max_tx_octets 251, max_tx_time 2120
    <debug> app: Physical update request: rx phys 3, tx phys 3
    <debug> app: Result 0
    <debug> app: Physical update update:rx phys 0, tx phys 3,
    <debug> app: Physical update update:rx phys 2, tx phys 2,
    <debug> app: Connection parameters update at time 93711: min: 6 max: 6 supervision timeout: 500 slave latency: 0
    <debug> app: New MTU size requested 512
    <debug> app: Requested MTU size exceeds maximum; reset to maximum size of 253
    <debug> app: Enabling control point
    <debug> app: Enabling CP CCCD with 2 at time 93801
    <debug> app: Enabling GHS response characteristic
    <debug> app: Enabling Response CCCD with 1 at time 93808
    <debug> app: Command 9 written on CP at time 93816
    <debug> app: Calling command handler
    <debug> app: ====> Received 'is Pairing required' command
    <debug> app: Response field non NULL: value 0x1
    <debug> app: Connection parameters update at time 93816: min: 36 max: 36 supervision timeout: 500 slave latency: 0
    <debug> app: =====> Sending 6 bytes of data at time 93816:
     09 00 00 00 01 00      |       .....
    <debug> app: =====> Send # 1: Send 6 bytes of 6 total from offset 0 at time 93816
    <debug> app: =====> Entire package sent
    <debug> app: Data length update request: max_rx_octets 251, max_rx_time 2120, max_tx_octets 251, max_tx_time 2120
    <debug> app: Result 0
    <debug> app: ----> Indications complete at time 93868, connection handle 0x0000
    <debug> app: Data length update: max_rx_octets 251, max_rx_time 2120, max_tx_octets 251, max_tx_time 2120
    <debug> app: Pairing request requested
    <debug> app: GHS is paired with some device but not the one that has connected!
    <debug> app: Encryption established
    <debug> app: Pairing completed
    <debug> app: Command 12 written on CP at time 94521
    <debug> app: Calling command handler
    <debug> app: ====> Received Get Current Time Info command at time 94521.
    <debug> app: ====> Sending Current Time Info at time 94521.
     0C 00 04 00 0E 00 A8 46|.......F
     A2 05 00 00 00 00 E8 03|........
     00 00 00 1F            |....
    <debug> app: =====> Sending 20 bytes of data at time 94523:
     0C 00 04 00 0E 00 A8 46|.......F
     A2 05 00 00 00 00 E8 03|........
     00 00 00 1F            |....
    <debug> app: =====> Send # 1: Send 20 bytes of 20 total from offset 0 at time 94523
    <debug> app: =====> Entire package sent
    <debug> app: ----> Notification TX done event received. Packets sent and not evented 0
    <debug> app: ----> Notification(s) complete at time 94566, connection handle 0x0000
    <debug> app: =====> Sending 4 bytes of data at time 94566:
     0C 00 00 00            |....
    <debug> app: =====> Send # 1: Send 4 bytes of 4 total from offset 0 at time 94566
    <debug> app: =====> Entire package sent
    <debug> app: ----> Indications complete at time 94611, connection handle 0x0000
    <debug> app: Command 10 written on CP at time 94633
    <debug> app: Calling command handler
    <debug> app: ====> Received Get System Info command at time 94633.
    <debug> app: =====> Sending 85 bytes of data at time 94633:
     0A 00 1F 00 4F 00 02 F1|....O...
     02 FF FF E3 4D AA 01 07|....M...
     10 02 00 12 47 48 53 20|....GHS
     42 6C 6F 6F 64 20 50 72|Blood Pr
     65 73 73 75 72 65 0B 47|essure.G
     48 53 2D 42 50 2D 32 33|HS-BP-23
     34 72 00 80 08 73 6E 58|4r...snX
     2D 34 39 35 36 06 66 77|-4956.fw
     39 30 2E 39 07 73 77 30|90.9.sw0
     2E 30 2E 35 08 68 77 35|.0.5.hw5
    <debug> app: =====> Send # 1: Send 85 bytes of 85 total from offset 0 at time 94633
    <debug> app: =====> Failed doing the indication. Error code: 0x0C
    <debug> app: Sending disconnect
    <debug> app: Disconnected at time 97355
    

  • Sorry about the mistake , I had an impression that if you call sd_ble_gap_data_length_update() before the connection with NULL as parameter it will be automatically applied the default value to all connections. But you are right, you need to wait after the connection and call the function with correct connection handle. 

    We would need to look into the way you declare the characteristic. If the max length of the characteristic is smaller than the data size you want to do indication you would receive an error.

    Please try capture sniffer trace so we can be sure what's actually configured in the connection. 

    Even if you don't use our SDK, you still can try to test the example in the SDK and have a look on how it's handled in the SDK. If it works in the SDK , most likely the same sequence should work in your case. I would suggest to test the ble_app_uart example. 

  • The missing link was the maximum length in the definition of the characteristic. It had been so long since I looked at that I forgot it even existed. I am developing a generic health device service and it involves no client reads so there really is no need for a data base at all with respect to a value. The client writes only commands and the server indicates/notifies.

    So in the end there are a whole host of things one needs to do to change the size of an indicated/notified data hunk.

    1. There is really no need to change the MTU size at all (though you may want to for through put)
    2. You have to negotiate data length with the client ASAP
    3. You have to create your characteristic with a default length that is the maximum possible data length (I suppose the only thing you waste is space if your negotiated data length is less than the characteristic value length - there is no way to dynamically change the characteristic value length or not specify one at all if you do not need to read it from the client.)

    I still have not tried initiating the data length myself - so far my Android 11 is doing it. My Android 6 does not so there I will need to initiate it - and it might not support it..

    I should probably add that I am using SoftDevice since it illustrates the Bluetooth functionality one needs to use for this generic standard. If you use the SDK (which does not support this profile of course) many of the Bluetooth functions are hidden and that would defeat the purpose of this example code.

Related