Peripheral UART - Incomplete Data Transmission via BLE

Dear Nordic DevZone community,

I am currently grappling with a challenge involving the transmission of data from an NRF52 device to the NRF Toolbox app through BLE.

To provide some context, I am generating and storing data that I intend to transmit via BLE to the NRF Toolbox app. One of the functions I've created repetitively calls `ble_nus_data_send()` for this purpose. However, I've noticed intermittent issues, particularly when transmitting a significant amount of information. In some instances, only one out of two messages is successfully sent through `ble_nus_data_send()`.

Upon thorough debugging, I have verified that the message generation is accurate. However, it appears there might be a hitch in the transmission or reception process through the app, especially when dealing with larger volumes of data. Typically, this results in a missed packet during transmission.

I am reaching out to seek any insights or suggestions from the community regarding potential causes of this issue. If anyone has faced a similar problem or has recommendations for debugging strategies, your expertise would be greatly appreciated.

Thank you in advance for your assistance.

Best regards,
Lara

  • Hi,

    Are you checking the return code from ble_nus_data_send()? Does it always return success?

    Can you check with a BLE sniffer if all generated data is sent from the peripheral? Can you provide the sniffer log along with the device log from the peripheral and the log from the phone application?

    Best regards,
    Jørgen

  • Hi Jørgen,

    I apologize for the delay in my response.

    I have been checking the return code from ble_nus_data_send() and it consistently returns success. Although I do not have a BLE sniffer, I am using Segger to debug while sending data from my phone so I can confirm if all data is being sent correctly. However, it appears that when I am sending too much data, the phone doesn't follow.

    I have addressed the initial issue of missing data by adding a buffer that waits for the data to be sent before sending more data. 


            case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                 // Event when packet has been sent
                 NRF_LOG_INFO("Sent");
                 // Remove item from the buffer
                 items_in_ble_buffer -= 1;
                 break;

    static void ble_send_data(data_to_send_t dataToSend)
    {
        if (phone_connected == true)
        {
            /* Wait for the buffer to have less than 5 items before proceeding. */ 
            while(items_in_ble_buffer > 5) //The value 5 was empirically determined as optimal during testing, so might be adjusted in the future.
            {
                // nrf_drv_wdt_channel_feed(m_channel_id); // feed WDT
                power_manage();
            }
    
            ble_nus_data_send(&m_nus, (uint8_t *)dataToSend.data, &dataToSend.length, m_conn_handle);
            items_in_ble_buffer ++;
        }

    Now, when I send a large amount of data, the nrf Toolbox freezes on the phone, but I can confirm that all data has been sent from the central thanks to the debug on Segger.

    To provide more details, I have been using an iPhone 6s with iOS15 and an iPhone12. I have changed the MTU size, and for the iPhone 6, the MTU is limited to 185 while I can go up to 247 with the iPhone 12. With the iPhone 12, I can send more data before the app freezes.

    I am using NRF52 dongle to connect between my connected object and my iPhone. I use SDK17.1.0 on the s132.

    Is there a way to resolve this freezing issue?

    I am uncertain if it is related, but I have also noticed that sometimes the phone disconnects (disconnection 19, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION 0x13) before sending all data. This problem appears to be unique to iOS, as Android does not have this issue. This disconnection does not seem to happen always at the same time and not every time.

    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(15, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (15 ms), Connection interval uses 1.25 ms units. */
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(30, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */
    #define SLAVE_LATENCY                   0                                           /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)             /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                       /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                      /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3   

    // <i> Requested BLE GAP data length to be negotiated.
    
    #ifndef NRF_SDH_BLE_GAP_DATA_LENGTH
    #define NRF_SDH_BLE_GAP_DATA_LENGTH 251
    #endif
    
    // <o> NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - Maximum number of peripheral links. 
    #ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT
    #define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 1
    #endif
    
    // <o> NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links. 
    #ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT
    #define NRF_SDH_BLE_CENTRAL_LINK_COUNT 0
    #endif
    
    // <o> NRF_SDH_BLE_TOTAL_LINK_COUNT - Total link count. 
    // <i> Maximum number of total concurrent connections using the default configuration.
    
    #ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT
    #define NRF_SDH_BLE_TOTAL_LINK_COUNT 1
    #endif
    
    // <o> NRF_SDH_BLE_GAP_EVENT_LENGTH - GAP event length. 
    // <i> The time set aside for this connection on every connection interval in 1.25 ms units.
    
    #ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH
    #define NRF_SDH_BLE_GAP_EVENT_LENGTH 6
    #endif
    
    // <o> NRF_SDH_BLE_GATT_MAX_MTU_SIZE - Static maximum MTU size. 
    #ifndef NRF_SDH_BLE_GATT_MAX_MTU_SIZE
    #define NRF_SDH_BLE_GATT_MAX_MTU_SIZE 247
    #endif

    Regarding the disconnection issue, I have looked into it and while it could be related to a bad connection parameter negotiation, I do not believe that to be the cause. I have also considered the possibility that it is related to BLE_GATTC_EVT_TIMEOUT, but I have followed the examples and do not think that is the issue. Here is the BLE event handler that I am currently using:

    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        uint32_t err_code;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected");
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
                err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                phone_connected = true;
                items_in_ble_buffer = 0;
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected");
                NRF_LOG_INFO("Disconnected reason: %d.", p_ble_evt->evt.gap_evt.params.disconnected.reason);
                m_conn_handle = BLE_CONN_HANDLE_INVALID;
    
                /* if advertising should be paused, don't restart advertising */
                if (advertising_paused == true)
                {
                    phone_connected = false;
                    break;
                }
    
                /* immediately restart advertising */
                advertising_start();
                phone_connected = false;
                break;
    
               
    
            case SD_BLE_GAP_CONN_PARAM_UPDATE:
                NRF_LOG_DEBUG("Connection parameters update.")
                ble_gap_evt_t const * const p_gap = &p_ble_evt->evt.gap_evt;
                m_conn_handle = p_gap->conn_handle;
                err_code = sd_ble_gap_conn_param_update(m_conn_handle, &p_gap->params.conn_param_update_request.conn_params);
    
                APP_ERROR_CHECK(err_code);
                break; 
        
            
            case BLE_GAP_EVT_PHY_UPDATE:
            {
                NRF_LOG_DEBUG("PHY update.");
                phy=p_ble_evt->evt.gap_evt.params.phy_update;
                phyTx=phy.tx_phy;
                phyRx=phy.rx_phy;
                NRF_LOG_INFO("PHY update status: %d\nPHY update TX:%d\nPHY update RX:%d.", phy.status,phyTx,phyRx) ;
                break;
            }
    
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
                // Pairing not supported
                err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_SYS_ATTR_MISSING:
                // No system attributes have been stored.
                err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("CENTRAL: GATT Server Time-out.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                 // Event when packet has been sent
                 NRF_LOG_INFO("Sent");
                 // Remove item from the buffer
                 items_in_ble_buffer -= 1;
                 break;
    
            default:
                // No implementation needed.
                break;
        }
    }

    I would appreciate any idea to improve the the BLE transmission of data (improve the speed also if possible) and get rid of this disconnection issue. 

    Best regards,

    Lara 

Related