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

LIBUARTE+BLE_APP_UART (ble_nus_data_send seems can't handle too many data or fast enough that comes from libuarte - app just hangs)

Hi Nordic Support Team,

My current setup is : Android Phone <--BLE--> nRF52840 <--LIBUARTE--> External STM32 MCU

I started the example with this folder in the link provided (pca10056 with S140):

https://devzone.nordicsemi.com/f/nordic-q-a/71665/nrf-52832-uart-high-speed-communication/294686#294686

It works fine when I am sending command from my phone to nRF and send to the external MCU.

However, when I tried fetching data from the nRF to the phone through ble_nus_data_send(), the nRF will only send some data (outputted to my phone serial terminal) and then hangs, even when I am in debug mode, the application doesn't even stop. (maybe it it stuck in a while loop or something). Note that nRF and external MCU is exchanging a lot of data at baud rate of 115200.

Also, by "hangs" means that the device doesn't react to my command request from the phone and the debug terminal doesn't output anything after.

When I commented out the ble_nus_data_send() part, the uart is exchanging data smoothly between nRF and External MCU.

I have looked through some of the threads relating to this implementation, but I don't what I am doing wrong here. I think these important pieces of code will be able to show you :

// <q> NRF_LIBUARTE_ASYNC_WITH_APP_TIMER  - nrf_libuarte_async - libUARTE_async library
 

#ifndef NRF_LIBUARTE_ASYNC_WITH_APP_TIMER
#define NRF_LIBUARTE_ASYNC_WITH_APP_TIMER 1
#endif

#define APP_BLE_CONN_CFG_TAG            1                                           /**< A tag identifying the SoftDevice BLE configuration. */

#define DEVICE_NAME                     "nRF_libUARTE"                               /**< Name of device. Will be included in the advertising data. */
#define NUS_SERVICE_UUID_TYPE           BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */

#define APP_BLE_OBSERVER_PRIO           3                                           /**< Application's BLE observer priority. You shouldn't need to modify this value. */

#define APP_ADV_INTERVAL                64                                          /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */

#define APP_ADV_DURATION                18000                                       /**< The advertising duration (180 seconds) in units of 10 milliseconds. */

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(75, 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                                           /**< Number of attempts before giving up the connection parameter negotiation. */

#define DEAD_BEEF                       0xDEADBEEF                                  /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 2, 2, NRF_LIBUARTE_PERIPHERAL_NOT_USED, 255, 3);

BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);                                   /**< BLE NUS service instance. */
NRF_BLE_GATT_DEF(m_gatt);                                                           /**< GATT module instance. */
NRF_BLE_QWR_DEF(m_qwr);                                                             /**< Context for the Queued Write module.*/
BLE_ADVERTISING_DEF(m_advertising);                                                 /**< Advertising module instance. */

static uint16_t   m_conn_handle          = BLE_CONN_HANDLE_INVALID;                 /**< Handle of the current connection. */
static uint16_t   m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;            /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
static ble_uuid_t m_adv_uuids[]          =                                          /**< Universally unique service identifier. */
{
    {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}
};

static void nus_data_handler(ble_nus_evt_t * p_evt)
{

    if (p_evt->type == BLE_NUS_EVT_RX_DATA)
    {
        uint32_t err_code;

        NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
        NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);

        uint8_t uart_string[BLE_NUS_MAX_DATA_LEN+2] = "";
        memcpy(uart_string, p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
        uart_string[p_evt->params.rx_data.length] = '\r';
        uart_string[p_evt->params.rx_data.length+1] = '\n';

        err_code = nrf_libuarte_async_tx(&libuarte,uart_string, p_evt->params.rx_data.length + 2);
        // APP_ERROR_CHECK(err_code);
    }

    else if (p_evt->type == BLE_NUS_EVT_TX_RDY)
    {
        flag_transmitted = true;
        NRF_LOG_DEBUG("TX IS READY");
    }
    else if (p_evt->type == BLE_NUS_EVT_COMM_STARTED)
    {
        NRF_LOG_DEBUG("NOTIFICATION IS STARTED!");
    }
}

/**@brief   Function for handling app_uart events.
 *
 * @details This function will receive a single character from the app_uart module and append it to
 *          a string. The string will be be sent over BLE when the last character received was a
 *          'new line' '\n' (hex 0x0A) or if the string has reached the maximum data length.
 */
/**@snippet [Handling the data received over UART] */
void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
{
    nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
    ret_code_t     ret;
    uint16_t index = 0;

    switch (p_evt->type)
    {
        case NRF_LIBUARTE_ASYNC_EVT_ERROR:
            break;
        case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
            NRF_LOG_DEBUG("NRF_LIBUARTE_ASYNC_EVT_RX_DATA : Received data from libuarte. Writing data on UART.");
            NRF_LOG_HEXDUMP_DEBUG(p_evt->data.rxtx.p_data,  p_evt->data.rxtx.length);
            // NRF_LOG_DEBUG("Flag Transmitted : %d.", flag_transmitted);
            
            while (index < p_evt->data.rxtx.length)
            {
                NRF_LOG_INFO("data rxtx loop");
                uint16_t length = (uint16_t)(p_evt->data.rxtx.length - index > BLE_NUS_MAX_DATA_LEN) ? BLE_NUS_MAX_DATA_LEN : p_evt->data.rxtx.length - index;
                do
                {
                    ret = ble_nus_data_send(&m_nus, p_evt->data.rxtx.p_data + index, &length, m_conn_handle);
                    if ((ret != NRF_ERROR_INVALID_STATE) &&
                        (ret != NRF_ERROR_RESOURCES) &&
                        (ret != NRF_ERROR_NOT_FOUND))
                    {
                        APP_ERROR_CHECK(ret);
                    }
                } while (ret == NRF_ERROR_RESOURCES);
                index += length;

             }

             if (p_evt->data.rxtx.p_data == NULL){

             }
             else {
                nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
             }
            
            break;

        case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
            // NRF_LOG_DEBUG("NRF_LIBUARTE_ASYNC_EVT_TX_DONE : Received data from libuarte. Writing data on UART.");
            // NRF_LOG_HEXDUMP_DEBUG(p_evt->data.rxtx.p_data,  p_evt->data.rxtx.length);

            break;
        default:
            break;
    }
}
/**@snippet [Handling the data received over UART] */


/**@brief  Function for initializing the UART module.
 */
/**@snippet [UART Initialization] */
static void uart_init(void)
{
    uint32_t                     err_code;

    nrf_libuarte_async_config_t nrf_libuarte_async_config = {
            .tx_pin     = TX_PIN_NUMBER,
            .rx_pin     = RX_PIN_NUMBER,
            .baudrate   = NRF_UARTE_BAUDRATE_115200,
            .parity     = NRF_UARTE_PARITY_EXCLUDED,
            .hwfc       = NRF_UARTE_HWFC_DISABLED,
            .timeout_us = 100,
            .int_prio   = APP_IRQ_PRIORITY_LOW_MID
            
    };

    err_code = nrf_libuarte_async_init(&libuarte, &nrf_libuarte_async_config, uart_event_handler, (void *)&libuarte);

    APP_ERROR_CHECK(err_code);

    nrf_libuarte_async_enable(&libuarte);

    static uint8_t text[] = "ble_app_libUARTE example started.\r\n";
    static uint8_t text_size = sizeof(text);

    err_code = nrf_libuarte_async_tx(&libuarte, text, text_size);
    APP_ERROR_CHECK(err_code);
}

Parents
  • Hi Kenneth, ret = 19 so it is NRF_ERROR_RESOURCES

     * @retval ::NRF_ERROR_RESOURCES Too many notifications queued.
     *                               Wait for a @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event and retry.

    I found that it in some occasion if it sends everything successfully, it will return NRF_SUCCESS then everything will be displayed in my phone serial monitor.

    If it doesn't, then it will display some partial data into the phone serial monitor, then the ret keeps returning NRF_ERROR_RESOURCES which makes it stuck in the while loop in the example code.

  • Then, in this case you may need to change the implementation such that you do as described for the error code:
    "Wait for a @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event and retry."

    You may then need to buffer the data so you can call it on BLE_GATTS_EVT_HVN_TX_COMPLETE  event.

    Kenneth

  • Ok, I will try this. And also, for anyone looking, this thread is really useful.

    devzone.nordicsemi.com/.../ble-lost-messages

  • Hi Kenneth,

    So I made these adjustment on the code :

    case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
                // NRF_LOG_DEBUG("NRF_LIBUARTE_ASYNC_EVT_RX_DATA : Received data from libuarte. Writing data on UART.");
                // NRF_LOG_HEXDUMP_DEBUG(p_evt->data.rxtx.p_data,  p_evt->data.rxtx.length);
                
                while (index < p_evt->data.rxtx.length)
                {
                    uint16_t length = (uint16_t)(p_evt->data.rxtx.length - index > BLE_NUS_MAX_DATA_LEN) ? BLE_NUS_MAX_DATA_LEN : p_evt->data.rxtx.length - index;
                    do
                    {
                        if (tx_buffer_free == true){
    
                            ret = ble_nus_data_send(&m_nus, p_evt->data.rxtx.p_data + index, &length, m_conn_handle);
    
                            if ((ret != NRF_ERROR_INVALID_STATE) &&
                                (ret != NRF_ERROR_RESOURCES) &&
                                (ret != NRF_ERROR_NOT_FOUND))
                            {
                                APP_ERROR_CHECK(ret);
                            }
    
                            if (ret == NRF_ERROR_RESOURCES) {
                                tx_buffer_free = false;
                            }
                        }
                        NRF_LOG_INFO("RET | TX_BUFFER_FREE : %d | %d", ret, tx_buffer_free);
                    } while (ret == NRF_ERROR_RESOURCES);
                    index += length;
                }
                nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
                break;
    

    And under ble_evt_handler, I added this case 

    case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                tx_buffer_free = true;
                NRF_LOG_INFO("NOTIFICATION TX COMPLETE!");
                
                break;

    So I set a tx_buffer_free as a global variable, basically what I think it does is the while loop will always be executed until the BLE_GATTS_EVT_HVN_TX_COMPLETE is executed by the SoftDevice Interrupt and changing the tx_buffer_free variable to break from the while loop.

    In reality, I always got the ret code as 19 and tx_buffer_free is always 0. Seems like it never goes into BLE_GATTS_EVT_HVN_TX_COMPLETE, but on other cases where there aren't too much packets coming in. It will print out the "NOTIFICATION_TX_COMPLETE". 

    Can you point out what I am doing wrong here?

  • Is it possible that your while loop is running in a interrupt context, and this prevent the softdevice callback to be serviced (an interrupt can't pre-empt another interrupt of higher or same priority).

    Kenneth

  • Do you know which specific priority I should change? I have always been using default value from the example.

    UART Priority is 6, NRF_SDH_BLE Priority is 3, BLE_NUS_BLE_OBSERVER_PRIO is 2.

    There are like a lot of priority configurations in the "sdk_config.h".

  • Hi Kenneth,

    I didn't get as much NRF_ERROR_RESOURCES after following the solution from these threads though it still exists and I need to resolve it :

    https://devzone.nordicsemi.com/f/nordic-q-a/66265/ble_nus_data_send-is-returning-nrf_error_resources/272007#272007

    https://devzone.nordicsemi.com/f/nordic-q-a/62498/sd_ble_gatts_hvx-in-softdevice-event

    By following this snippet : 

    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        ble_cfg_t ble_cfg;
        memset(&ble_cfg, 0, sizeof ble_cfg);
        ble_cfg.conn_cfg.conn_cfg_tag = APP_BLE_CONN_CFG_TAG;
        ble_cfg.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = 50;
        err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &ble_cfg, ram_start);
        APP_ERROR_CHECK(err_code);
    
    
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Register a handler for BLE events.
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
    }

    Basically changes so far that I have made from the example code are : 

    1. Change MIN_CONN_INTERVAL and MAX_CONN_INTERVAL to 50ms.

    2. Change NRF_SDH_BLE_GAP_EVENT_LENGTH to 40.

    3. Adding the snippets above.

Reply
  • Hi Kenneth,

    I didn't get as much NRF_ERROR_RESOURCES after following the solution from these threads though it still exists and I need to resolve it :

    https://devzone.nordicsemi.com/f/nordic-q-a/66265/ble_nus_data_send-is-returning-nrf_error_resources/272007#272007

    https://devzone.nordicsemi.com/f/nordic-q-a/62498/sd_ble_gatts_hvx-in-softdevice-event

    By following this snippet : 

    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        ble_cfg_t ble_cfg;
        memset(&ble_cfg, 0, sizeof ble_cfg);
        ble_cfg.conn_cfg.conn_cfg_tag = APP_BLE_CONN_CFG_TAG;
        ble_cfg.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = 50;
        err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &ble_cfg, ram_start);
        APP_ERROR_CHECK(err_code);
    
    
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Register a handler for BLE events.
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
    }

    Basically changes so far that I have made from the example code are : 

    1. Change MIN_CONN_INTERVAL and MAX_CONN_INTERVAL to 50ms.

    2. Change NRF_SDH_BLE_GAP_EVENT_LENGTH to 40.

    3. Adding the snippets above.

Children
Related