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

BLE data transfer problem

Hello,


I'm using a development board PCA10040 with sdk 16.0

I'm trying to organize a data streaming between two devices. Nrf52 board collect data (27 bytes with interval of 500us) from sensor board via SPI and send it via NUS.

At other side I have a dongle PCA10059 as a central with usbd_ble_uart.
It works fine with little data size and single send.

The problem is at BLE transfer. For first test I'm trying to send 18 packets (27*18 = 486 bytes).
Sensor board send packets all the time via SPI, I need only read

for(uint8_t idx = 0; idx < 18; ++idx)
{
    spi_xfer_done = false;
    memset(data, 0, sizeof(data));
    
    err_code = nrf_drv_spi_transfer(&spi, 0x00, 0, data, 27);
    APP_ERROR_CHECK(err_code);
    while (!spi_xfer_done) {__WFE();}

    uint16_t length = (uint16_t)sizeof(data);
    
    err_code = ble_nus_data_send(&m_nus, data, &length, m_conn_handle);
    APP_ERROR_CHECK(err_code);
}


But I receive only 5 arrays (27*5) and controller goes reboot.

1. Why this happend? RTT viewer shows nothing about this
But I found by debuger error code 13.

If I will take-off error check from ble_nus_data_send, I can receive 9 packets (243 bytes) without errors. But no more...
Ok, here looks like BLE buffer is full and was sent completely but only once.

2. Second test. I tried by other way. If I will form one 243 bytes array composed by 9 arrays with 27 bytes.

for(uint8_t idy = 0; idy < 10; ++idy)
{
    memset(data, 0, sizeof(data));
    for(uint8_t idx = 0; idx < 9; ++idx)
    {
        spi_xfer_done = false;
        memset(data_one, 0, sizeof(data_one));

        err_code = nrf_drv_spi_transfer(&spi, 0x00, 0, data_one, 27);
        APP_ERROR_CHECK(err_code);
        while (!spi_xfer_done) {__WFE();}  

        memcpy(data + idx*27, data_one, 27*sizeof(uint8_t));
    }
    uint16_t length = (uint16_t)sizeof(ECG_data);

    //err_code =
    ble_nus_data_send(&m_nus, ECG_data, &length, m_conn_handle);
    //APP_ERROR_CHECK(err_code);
}


The cycle idy happend 10 times but I receive only 3 times (3*243 = 729 bytes) without errors.
But it only works without app_error_check.
Ok, much better...

But why not 10 times?

3. Looks like I have some problem with connection, isn't it?
I tried to adjust some configuration but without success... Parameters are:
sdk_config.h
NRF_SDH_BLE_GAP_EVENT_LENGTH     6     //if change this parameter it give me reboot loop
NRF_SDH_BLE_GATT_MAX_MTU_SIZE   247

nrf_ble_gatt.c
BLE_GAP_DATA_LENGTH_DEFAULT     247  
BLE_GAP_DATA_LENGTH_MAX         251  

main.c
MIN_CONN_INTERVAL    20 //units
MAX_CONN_INTERVAL    75


Dongle configured by the same.

4. The final idea is to send and receive 54 kB at minimal time.

Sincerely,

Parents
  • I recommend to check out the ble_app_att_mtu_throughput example, which should negotiate and show the max throughput:
    https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.0.2/ble_sdk_app_att_mtu.html

    But here is a tip for you:

    If possible try to reduce the number of notifications sent between the peers. For instance instead of sending many small data packets, try to send larger packets combining more data into a single notification packet. To allow this you should exchange max supported MTU and DLE size between the two peers by include nrf_ble_gatt_init() in your application:

    void nrf_ble_gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
    {
    	if(p_evt->evt_id == NRF_BLE_GATT_EVT_DATA_LENGTH_UPDATED)
    	{
    		// p_evt->params.data_length is the max length on-air
    	}	
    	if(p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED)
    	{
    		// p_evt->params.att_mtu_effective is the max MTU size		
    	}	
    	
    	// Once both are updated, then you can for instance start sending notifications that are longer than 20bytes when calling sd_ble_gatts_hvx()/sd_ble_gatts_value_set().
    }
    
    
    /**@brief Function for initializing the GATT module.
     */
    static void gatt_init(void)
    {
        ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, nrf_ble_gatt_evt_handler);
        APP_ERROR_CHECK(err_code);
    }

    If you for instance set NRF_SDH_BLE_GATT_MAX_MTU_SIZE and NRF_SDH_BLE_GAP_DATA_LENGTH to 247, and then wait for nrf_ble_gatt_evt_handler() you can find the most efficient data length for your notification data to be 'p_evt->params.data_length-3'.

    Make sure that the characterstic can support this data length by setting add_char_params.max_len = 247 when init the characteristic. 

    After receiving the nrf_ble_gatt_evt_handler() you can now call sd_ble_gatts_hvx() with larger data than 20bytes (which is default), now you can allow packet of up to (p_evt->params.data_length-3) bytes. This will allow you to send less packets, and should overall make it less likely that you experience NRF_ERROR_RESOURCES and also increase throughput. (FYI: The '-3' is due to header in the packet).

    Best regards,
    Kenneth

  • Thank you for response,

    As you suggested, I added (found in some example)

    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("gatt_event: ATT MTU is set to 0x%X(%d)", data_length, data_length);
        }
        else if((m_conn_handle == p_evt->conn_handle) &&(p_evt->evt_id == NRF_BLE_GATT_EVT_DATA_LENGTH_UPDATED))
        {
            m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH - 4;
            NRF_LOG_INFO("gatt_event: Data len is set to 0x%X(%d)", data_length, data_length);
        }
        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);
    }

    I tryed to increase NRF_SDH_BLE_GAP_EVENT_LENGTH parameter but any value (diffent from the 6) give me "fatal error". 
    When I changed linker configuration, changing softdevice RAM I was able to change this parameter.
    So, now I can receive 4 packets (243 bytes each one) and return ble event with code 0x57. After this I have error 0x13(19). Is it mean that I have softdevice buffer full?
    Could you please give me example how to implement for waiting on BLE_GATTS_EVT_HVN_TX_COMPLETE ? Provided by ble_app_att_mtu_throughput example is not fully clear...
    As I use 2 boards, should the configuration of both devices be the same ?

Reply
  • Thank you for response,

    As you suggested, I added (found in some example)

    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("gatt_event: ATT MTU is set to 0x%X(%d)", data_length, data_length);
        }
        else if((m_conn_handle == p_evt->conn_handle) &&(p_evt->evt_id == NRF_BLE_GATT_EVT_DATA_LENGTH_UPDATED))
        {
            m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH - 4;
            NRF_LOG_INFO("gatt_event: Data len is set to 0x%X(%d)", data_length, data_length);
        }
        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);
    }

    I tryed to increase NRF_SDH_BLE_GAP_EVENT_LENGTH parameter but any value (diffent from the 6) give me "fatal error". 
    When I changed linker configuration, changing softdevice RAM I was able to change this parameter.
    So, now I can receive 4 packets (243 bytes each one) and return ble event with code 0x57. After this I have error 0x13(19). Is it mean that I have softdevice buffer full?
    Could you please give me example how to implement for waiting on BLE_GATTS_EVT_HVN_TX_COMPLETE ? Provided by ble_app_att_mtu_throughput example is not fully clear...
    As I use 2 boards, should the configuration of both devices be the same ?

Children
  • Sounds you have been able to get much better throughput now by larger notifications and more packets in each interval. 

    You can call sd_ble_gatts_hvx() until NRF_ERROR_RESOURCES (error 0x13 = 19). Then you can wait for a BLE_GATTS_EVT_HVN_TX_COMPLETE event (0x57) and retry sd_ble_gatts_hvx().

    Alternatively, instead of waiting for BLE_GATTS_EVT_HVN_TX_COMPLETE event you can just retry sd_ble_gatts_hvx() from a timer also.

    Best regards,
    Kenneth

Related