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

ble_nus_data_send() limited to five packets if executed from nus_data_handler() interrupt handler

SDK: v15
SoftDevice: S132
Chip: nRF52832 on BL652 Module

I have a command->response type system. The Central (Android phone) sends a command, and the Peripheral (nRF52 device) responds.

The command sent by central is interpreted by nus_data_handler() handler, and calls another function that needs to send back packets with ble_nus_data_send().

The issue is that I can only send five packets from my function, even with VERY long delays (1 sec) between each send. After five I get error NRF_ERROR_RESOURCES for each later attempt. The one second delay is obviously much longer than the packet interval.

I searched the forum extensively and implemented a flag to track when BLE_GATTS_EVT_HVN_TX_COMPLETE is raised, but I noticed that this event does not happen after every packet is sent. Also, for the packets after the initial five, BLE_GATTS_EVT_HVN_TX_COMPLETE event will not be raised until the function is finished.

I think this has something to do with interrupt priorities but I don't know how to look into that. Can ble_evt_handler() priority be greater than nus_data_handler()?

<info> app: UART_SERVICE_SUBCOMMAND_SEND_ALLEVENTS
<info> app: event_id: 1
<info> app: Sending data for event: 1
<info> app: Sent data for ble. Success.
<info> app: event_id: 2
<info> app: Sending data for event: 2
<info> app: Sent data for ble. Success.
<info> app: event_id: 3
<info> app: Sending data for event: 3
<info> app: Sent data for ble. Success.
<info> app: event_id: 4
<info> app: Sending data for event: 4
<info> app: Sent data for ble. Success.
<info> app: event_id: 5
<info> app: Sending data for event: 5
<info> app: Sent data for ble. Success.
<info> app: event_id: 6
<info> app: Sending data for event: 6
<info> app: Sent data for ble. Err code: 19
<info> app: event_id: 7
<info> app: Sending data for event: 7
<info> app: Sent data for ble. Err code: 19

// This is the last event it tried to send (no retry in this code)

// After the last transfer attempt (#7), only then did the TX_COMPLETE event get raised

<info> app: BLE_GATTS_EVT_HVN_TX_COMPLETE event
<info> app: Connected
<info> app: BLE_GATTS_EVT_HVN_TX_COMPLETE event
<info> app: Connected
<info> app: BLE_GATTS_EVT_HVN_TX_COMPLETE event

  • Found a related post: No preemption in application

    I tried changing NRF_SDH_BLE_OBSERVER priority from 3 to 2 but it did not help.

    Any other options except to move the send function to main?

  • Hello,

    I believe the issue is that you are calling ble_nus_data_send from within the BLE event handler, which has the same priority as the TX_COMPLETE event, so you will not be able to get this interrupt. Your BLE events need to finish before you can get the other ones.

    You need to use your nus_data_handler() to set a flag which can execute your ble_nus_data_send from an application level interrupt priority. nus_data_handler() and ble_evt_handler() can not have different priorities. 

    So you need to use the nus event to set a flag which can be checked in your main loop, and execute a function if the flag is set.

    Best regards,

    Edvin

  • Thanks for your reply. That makes sense. I thought I was out of the context of the interrupt but obviously I am not.

    Are there any example projects that use this flag method that I can reference?

  • Not that I am aware of. But it could look something like this (pseudo code):

    #include "..."
    ...
    
    volatiole bool m_send_flag = false;
    volatiole bool m_space_in_buffer = true;
    
    static void nus_data_handler(ble_nus_evt_t * p_evt)
    {
        if (p_evt->type == BLE_NUS_EVT_RX_DATA)
        {
            m_send_flag = true;
        }
        ...
    }
    
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void *p_context)
    {
        ...
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                m_space_in_buffer = true;
                break;
        }
        ...
    }
    
    static void my_send_nus_data_function(void)
    {
        while (still_more_data_to_send)
        {
            ...
            err_code = ble_nus_data_send(...);
            if (err_code == NRF_ERROR_RESOURCES)
            {
                m_space_in_buffer = false;
                // exit function. m_send_flag is still true. 
                // we are not done sending, but there is no point in staying here until we get a TX_COMPLETE event. Let the CPU go to sleep until the next BLE event.
                return;
            }
        }
        // still_more_data_to_send = false, so we can set the m_send_flag to false)
        m_send_flag = false;
    }
    
    int main(void)
    {
        ...
        for(;;)
        {
            if (m_send_flag && m_space_in_buffer)
            {
                my_send_nus_data_function();
            }
            idle_state_handle();
        }
    }

  • Hello Edvin!

    I just found this thread very similar to my question I posted here.

    I tried with the lines suggested by you, yet I am not receiving all the string packets. Please refer to my code below. nus_data_handler and ble_evt_handler are kept same as you mentioned.

    volatile bool m_send_flag = false;
    volatile bool m_space_in_buffer = true;
    volatile bool m_tx_in_process = false;
    
    uint32_t err_code; int i=0,j=0; uint16_t len = 50; uint8_t data_arr[50];
    char* main_buf = {"..."};  //1000 bytes of string data
    	
    void send_data_over_ble(void)
    {
     	for (j=0;j<1000;j++)
    		{
    			data_arr[i] = main_buf[j];
    			i++;
    			if(i==len)
    				{
    					i=0;
    					err_code = ble_nus_string_send(&m_nus, data_arr, &len);
    					if (err_code == NRF_ERROR_RESOURCES)
    						{
    						m_space_in_buffer = false;
    						return;
    						}
    				}
    				m_tx_in_process = true;
    		}
    		m_send_flag = false;
    		m_tx_in_process = false;
    }
    
    int main(void)
    {
        ...
    		for(;;)
        {
            if (m_send_flag && m_space_in_buffer && !m_tx_in_process)
            {
                send_data_over_ble();
    		}
    	}
    }

    Please express your views in this regard.

Related