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

Data loss while sending large data by notifications with custom BLE service

Hello everyone,

A nRF52832 with SDK 15.0 was used for my application. I am trying to send a large amount of data via BLE with a custom BLE service. I extended MTU size so that it can meet my demand in both Client and Server. My Android app was developed to get the data. To check whether data sent by BLE device are lost or not, I added an index after a data package of 48-byte long. The following image is about index I got. 

There are 1, 2 or even 3 packages lost during data transmission. In this case, I tried to set the value of interval connection as low as possible (9ms). If I increased this interval as I have tested (24ms), there are more data packages lost.

Interestingly, if I use NUS BLE service instead and UART Android app to collect data,  this issue didn't happen and I can send up to 60 byte data per package without data loss. I double-checked my update data in custom BLE service, it is similar to that in NUS BLE service. 

Thus, are there any suggestion for me to solve this issue on the custom BLE service?

Thanks!

  • You missed one: NRF_ERROR_BUSY 

    	if (temp == noData)
    		{	
    			do 
    				{
    					err_code = ble_cus_custom_value_update(&m_cus, dataSendBLE, noData);
    					temp = 0;
    					if ( (err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_RESOURCES) &&
    							 (err_code != NRF_ERROR_NOT_FOUND) && (err_code ! NRF_ERROR_BUSY))
    					{
    							APP_ERROR_CHECK(err_code);
    					}
    				} while ((err_code == NRF_ERROR_RESOURCES) || (err_code == NRF_ERROR_BUSY) );
    			
    		}

    I tend to just use (err_code == NRF_SUCCESS) to know a packet was sent ok, rather than spinning in a loop, but it's better to be explicit.  Typically - though not required - you would use a queue or other buffer for the sensor packets, since sometimes BLE will be unavailable for significant periods and I assume data must not be lost. That way the code doesn't just spin but any packet that wasn't sent will be tried again on the next interval.

    Also I think you want temp = 0; to only happen on a successful transmit ..

  • The problem hasn't been fixed yet. Yeah, you're right, I need to use a better way ( data structure) to handle a large amount of data from sensor. I'll look at it.

    Btw, when I use same way collecting data from sensor with NUS BLE service, data loss never happens even I increase the length of package or interval connection. I am reading this post, in UART handler, what it did in the following code is similar to what you suggested to me.

    void uart_event_handle(app_uart_evt_t * p_event)
    {
        static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
        static uint8_t index = 0;
        uint32_t       err_code;
    
        switch (p_event->evt_type)
        {
            case APP_UART_DATA_READY:
                UNUSED_VARIABLE(app_uart_get(&data_array[index]));
                index++;
    
                if ((data_array[index - 1] == '\n') || (index >= (m_ble_nus_max_data_len)))
                {
                    NRF_LOG_DEBUG("Ready to send data over BLE NUS\r\n");
                    NRF_LOG_HEXDUMP_DEBUG(data_array, index);
    
                    do
                    {
                        err_code = ble_nus_string_send(&m_nus, data_array, index);
                        if ( (err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_BUSY) )
                        {
                            APP_ERROR_CHECK(err_code);
                        }
                    } while (err_code == NRF_ERROR_BUSY);
    
                    index = 0;
                }
                break;
    
            case APP_UART_COMMUNICATION_ERROR:
                APP_ERROR_HANDLER(p_event->data.error_communication);
                break;
    
            case APP_UART_FIFO_ERROR:
                APP_ERROR_HANDLER(p_event->data.error_code);
                break;
    
            default:
                break;
        }
    }

    Someone also suggested that "either call ble_nus_data_send() in a loop until the function returns NRF_SUCCESS, or wait for the BLE_GATTS_EVT_HVN_TX_COMPLETE and then send the packet". Should I try to use BLE_GATTS_EVT_HVN_TX_COMPLETE? 

  • Also I think you want temp = 0; to only happen on a successful transmit ..

    Yeah, you're right. For this purpose, I should put it outside the do..while loop then.

  • Ah-ha! You raise an interesting question. The Nordic Uart Service (NUS BLE Service) is not the same as the code you have above - the transfer of data from the Peripheral to the Central in NUS uses a Write Request, which requires an acknowledgement and therefore can only occur once every Connection Interval. The code you are using above uses a Write Command, which does not require an acknowledgement and can therefore be significantly faster with multiple packets possible in a Connection Interval. Note the NUS uses Write Command when the Central sends data to the Peripheral; Write Request is only Peripheral to Central in NUS.

    Does it matter? Well I didn't think so, as the handshake indicating packet success should occur at the LL level, but maybe that is not the case. In this post I asked about basically the same thing, and got a good response from martinbl which I recommend you read.

  • the transfer of data from the Peripheral to the Central in NUS uses a Write Request

    I don't think so as it uses notification to transmit data. I quoted what Martinbl answered in your post: The NUS peripheral example on the other hand, uses notifications. It is done with the function sd_ble_gatts_hvx() and happens in the function ble_nus_string_send() at the bottom of the file ble_nus.c.

    Btw, this post and this one mentioned about this term NRF_SDH_BLE_GAP_DATA_LENGTH which negotiates the BLE GAP data exchange. My project has been set this term with value of 27, I'll increase this and see what happens.

Related