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

How to increase notification throughput on nRF52840?

Hello there,

I'm developing a C++ application on the BLE654 development bord which shall be able to generate and send 100+ BLE notifications per second on a single BLE connection.

On my first try it managed 5.

After some digging, I found that the soft device (s140, Ver 6.1.0) got a BLE_GAP_EVT_CONN_PARAM_UPDATE event, which set the minimum and maximum connection interval to 200 ms, wich was the defined MAX_CONN_INTERVAL (the code burrowed from the blinky example). After setting this to 20 ms I could get (close to) 50 notifications per second, but further decreasing the value again reduced the throughput (maybe the Android on the 'other side' of the BLE connection prohibited a further decrease).

I read somewere that soft devices from s120 up were able to process up to 6 notifications in a 7.5 ms connectipon interval, but here it looks as is the sd only does one notification per interval. What can/must be done to further increase the througput to reach at least 100 notifications per second?

Notes:

-in our application, we send the notifications with sd_ble_gatts_hvx(). I don't have exact numbers, but the internal queue of the soft device seemd rather small and we got a NRF_ERROR_RESOURCES error quickly, after wich we wait for a BLE_GATTS_EVT_HVN_TX_COMPLETE until we call sd_ble_gatts_hvx() again. I am certain the soft device at all time had notifications in its queue.

-as recipient of the notifications we used two tablets with Android 6.0.1 and 7.0, respectively. Both showed similar throughputs, and the final application shall also run on an Android device.

Regards,

     Lasse

Parents
  • There is no functionality in the SoftDevice to put together small packets to larger packets, unfortunately. You will have to do this in the application. So you have to collect the data that you want to send in larger packs, before you send them to the softdevice. The limit of the size of the pack is defined by the MTU size of the connection. 

    I don't know what SDK version you use, but at least in the later SDKs, v15.0.0 and later, the ble_app_uart example is initialized with maximum MTU. The actual MTU, though, is dependent on the connected device. If it is a fairly new mobile phone, you will usually get an MTU of about 180B, but if it is connected to another nRF (with the ble_app_uart_c example) you will get the maximum possible MTU of 247.

    You can see the actual MTU in the gatt_evt_handler(), which will update the m_ble_nus_max_data_len in the ble_app_uart example.

    If you change the application to send messages based on UART input, but instead send packets from the main-loop, you can test the throughput.

    In this example, you can use ble_nus_data_send() with arrays of size m_ble_nus_max_data_len, and send them until you get the return value NRF_ERROR_RESOURCES. This means that your queue is full. Then you must wait for the BLE_GATTS_EVT_HVN_TX_COMPLETE event (you must add this to the ble_evt_handler() ), which means that a packet has been ACKed, and you can queue another.

    Best regards,

    Edvin

  • Hi Edvin,

    I'm using SDK 15.2.0.

    I've tried to integrate the nus from the ble_uart_example into my code. However it didn't work as expected, since the notifications I send were truncated after 20 Byte. This is the same behaviour i got with a normal notification and the sd_ble_gatts_hvx() function. After digging through the nus code, I saw that it essentially boils down to calling the sd_ble_gatts_hvx(), so this is not a big surprise after all.
    I may have overlooked something, but at this point I cannot see why I should need the nus - I could as well hand the array in which I accumulated the data to sd_ble_gatts_hvx() directly.

    Either way, the 20 byte border kills this approach.

    These are the parameters I've used:

    #define NRF_SDH_BLE_GAP_DATA_LENGTH 251
    #define NRF_SDH_BLE_GATT_MAX_MTU_SIZE 247
    #define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 14080

    After negotiation, the mtu size was still 247 byte.

    Any more Ideas?

    Best regards,

         Lasse

  • Hello Lasse,

    You are correct. ble_nus_data_send will only use the sd_ble_gatts_hvx() function, in addition to doing some checks first, e.g. that notifications is enabled and that the conn_handle is correct, and so on.

    Can you check whether you get the gatt_evt_handler() callback function? If you do, what is the att_mtu_effective from the event?

    In the ble_app_uart example, the gatt_evt_handler() looks like this:

    void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
    {
        if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
        {
            m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
            NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);
        }
        NRF_LOG_DEBUG("ATT MTU exchange completed. central 0x%x peripheral 0x%x",
                      p_gatt->att_mtu_desired_central,
                      p_gatt->att_mtu_desired_periph);
    }

    What is the m_ble_nus_max data_len after this call?

    You can log it like it is done in this function:

    NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);

    Best regards,

    Edvin

  • Hello Edvin,

    the  gatt_evt_handler() isn't called.

    I've logged the m_ble_nus_max_data_len value prior to a call to ble_nus_data_send(); it was an unchanged 247.

    Best redargs,

         Lasse

Reply Children
  • Hello Lasse,

    I am a bit confused. Do you use the unmodified example, ble_app_uart from SDK15.2.0, or did you pull some parts of it into your application? The reason that I ask is that the unmodified example should ask for a higher MTU (247), but most phones don't allow this, and typically gives a bit less.

    Is it possible to do a sniffer trace of your connection? If possible, the nRF Sniffer would be sufficient.

    When you said, a couple of posts ago, "the 20 byte border kills this approach"

    Can you try to debug to see what "kills" the application? It is probably an APP_ERROR_CHECK that receives an err_code != NRF_SUCCESS. Can you see where it comes from?

    Most likely it is ble_nus_data_send() if that is the only thing you change, but can you also debug to check whether it is the sd_ble_gatts_hvx(), or one of the checks before? And what value does it return?

    I tested just now with the unmodified example from SDK15.2.0, and the log output is like this when I connect with an iPhone 8:

    I then modified the example to send a dummy array of size 182 with some random numbers whenever the uart handler triggered, and it was sent with the return value NRF_SUCCESS, and I received this array on my phone when notifications were enabled.

    Can you check the log output when you run your application. I don't know what your pcb looks like, but if you are able to program the chip, you should be able to view the RTT log using the same application as in the screenshot above.

    BR,

    Edvin

  • Hello Edvin,

    > Do you use the unmodified example, ble_app_uart from SDK15.2.0, or did you pull some parts of it into your application?


    I did both, each with at least two different android devices. In neither case the "Data len is set to ..." line was called (only "ATT MTU exchange completed. central 0xF7 peripheral 0xF7").


    Regarding the "kill": I didn't want to say that the application ran into a hard fault or similar problems. It runs fine and stable. What I meant was that it is pointless for me to increase the MTU and/or use the nus as long as the 20 byte restriction still is in place (whatever the reason may be).

    Sorry for the misunderstanding.

    No, I don't have a sniffer trace. I keep trying.

    Best regards,

         Lasse

  • Try to program the attached app on your device, and check the RTT log. What does it say?

    test_app.hex

    NB:

    The app is an application AND the softdevice, so no need to program the softdevice first.

    Just use:

    "nrfjprog -e && nrfjprog --program test_app.hex && nrfjprog --reset"

    Then connect to it with your android devices, and check the log. The app should advertise as Nordic_UART.

    BR,

    Edvin

  • Hello Edvin,

    the app doesn't say anything. It seems that it isn't running at all (or at least, it isn't running very long).

    I think the problem may be the soft device clock configuration. We are using a DVK-BL654 development kit which doesn't feed the XTAL clock. Since most examples do use the XTAL clock source I think your code does too, in wich case it will stall as soon as the soft device is initialized.

    Here are our soft device clock settings:

    #define NRF_SDH_CLOCK_LF_SRC 0
    #define NRF_SDH_CLOCK_LF_RC_CTIV 16
    #define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2
    #define NRF_SDH_CLOCK_LF_ACCURACY 1

    Best regards,

         Lasse

  • I'm sorry. I forgot. Try this one, then:

    test_app_2.hex

    (this .hex file also includes the softdevice)

Related