This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Sending SAADC data via ble_nus_data_send

Hello,

I am trying to send data obtained from a SAADC port via BLE using the ble_nus_data_send function. This must happen periodically at frequencies within the Gigahertz range, ideally.

I started with the ble_app_uart example, from SDK16. Then, I initialized a timer using PPI to trigger an SAADC sampling event, such as in the saadc example.

Inside the saadc_callback function, I process the data and call the ble_nus_data_send function, as seen bellow:

void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;

        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);

        saadc2ble_convert();   
        uint16_t length = (uint16_t)UART_TX_BUF_SIZE;
        err_code = ble_nus_data_send(&m_nus, adc_output, &length, m_conn_handle);                  
        APP_ERROR_CHECK(err_code);         
    }
}

the saadc2ble_convert function simply converts the data from uint16_t to uint8_t and the length of the data transmitted via ble_nus_data_send function has not been modified from the initial example:

#define UART_TX_BUF_SIZE                256                                         /**< UART TX buffer size. */
#define SAMPLES_IN_BUFFER  UART_TX_BUF_SIZE/2
static nrf_saadc_value_t adc_buf[SAMPLES_IN_BUFFER];                   //!< Buffer used for storing ADC value.
static uint8_t          adc_output[UART_TX_BUF_SIZE]; //!< Current battery level.


void saadc2ble_convert(){

   int i;

   for(i=0; i<SAMPLES_IN_BUFFER; i++){
       adc_output[i*2] = adc_buf[i] & 0xff;
       adc_output[i*2 + 1] = (adc_buf[i] >> 8) & 0xff;
   }

}

I initialize all in the main function as seen in the next code block:

int main(void)
{
    bool erase_bonds;
    ret_code_t err_code;

    // Initialize.
    uart_init();
    log_init();
    timers_init();    
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();

    saadc_init();
    saadc_sampling_event_init();
    

    advertising_init();
    conn_params_init();

    // Start execution.
    printf("\r\nUART started.\r\n");
    NRF_LOG_INFO("Debug logging for UART over RTT started.");



    advertising_start();

    saadc_sampling_event_enable();



    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }
}

Debugging, I see that the SAADC is reading data well, however, I couldn't get any data at the smartphone.

I am getting the error shown on the image:

Could anyone help me to find a way to solve this problem?

  • Hello, Karl.

    Just to make things clear, I was getting both the errors I mentioned above, but after I made a few changes, it seems they ceased to appear. I think this ticket can be closed here since the initially proposed problem has been solved. I would like to thank you for your support. I think your main contribution was to help me to make better use of the debugging functions in Segger embedded studio, and it has helped me a lot not only with this situation but with future possible issues.

    Thanks!

  • Thiago said:
    I was getting a very slow data transfer so I made a small change to the code. I removed the ble_nus_data_send() function from the main loop and placed it in the saadc_callback(). After I did that, I left the device working to check for stability and it seems that I am not getting errors anymore. This is how my saadc_callback() function looks like now:

    Great, it sounds a lot better that you are doing this as part of your saadc_callback rather than the main context - I would expect that this change could alleviate both the NRF_ERROR_NO_MEM and especially the NRF_ERROR_RESOURCES error.

    Thiago said:
    The callback is called by a timer event, every 1us. I am getting the data, but I need to speed up the transmission.

    What do you mean when you say that the saadc_callback is called by a timer every 1us? Did you mean 1 ms, and that the callback is called every 1 ms because the buffer is filled every 1 ms?
    If not, please elaborate.

    Thiago said:
    I can see that not every time the ble_nus_data_transfer() function is called the data is successfully transmitted. Very often, it returns the error 0x13 (NRF_ERROR_RESOURCES).

    The reason for this error can be read about in the sd_ble_gatts_hvx API Reference documentation. In essence, you are queueing notifications faster than you are sending them.
    Could you tell me, how frequently do you queue a new notification, what is your connection interval, and what is your HVN TX queue size?

    Thiago said:
    What could I do to increase the success rate of the ble_nus_data_transfer() function, thus increasing the amount of transmitted data?

    We will need to make sure that you are not generating notifications faster than you are sending them, which looks to be the case.
    Fortunately, there are many ways to remedy this - depending on what is causing this overflow.
    For example, you might increase MTU size, and bundle more data into each notification.
    Alternatively, you could decrease your connection interval, so that notifications are sent faster.
    Or, if your issue is that you are sending multiple notifications on multiple characteristics, this error might be resolved by increasing your HVN TX queue size.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

  • What do you mean when you say that the saadc_callback is called by a timer every 1us? Did you mean 1 ms, and that the callback is called every 1 ms because the buffer is filled every 1 ms?
    If not, please elaborate.

    I configure a timer using the function nrf_drv_timer_us_to_ticks with 1 as the second input, so I assume that the timer will repeat every 1 microsecond. I did just like the saadc example, except that I replaced the function nrf_drv_timer_ms_to_ticks in the example with nrf_drv_timer_us_to_ticks. 

    Could you tell me, how frequently do you queue a new notification, what is your connection interval, and what is your HVN TX queue size?

    I am not sure, but I think I queue a notification every time I call the function ble_nus_data_send, if that is right, then it happens every time the timer triggers the saadc_callback functions, and as explained above, it seems to be configured to happen at every 1us.

    I believe this is how the connection interval is configured:

    #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. */

    But I don't know how can I see the HVN TX queue size. In what file would it be defined?

     

  • Thiago said:
    I configure a timer using the function nrf_drv_timer_us_to_ticks with 1 as the second input, so I assume that the timer will repeat every 1 microsecond. I did just like the saadc example, except that I replaced the function nrf_drv_timer_ms_to_ticks in the example with nrf_drv_timer_us_to_ticks. 

    Do you intend to sample the SAADC every us? The minimum Acquisition time for the SAADC is 3 usand the maximal sampling frequency is therefore 200 kHz.
    Have you configured your SAADC channel to use 3 us acquisition time?

    Thiago said:
    I believe this is how the connection interval is configured:

    This is the connection interval range that the peripheral will accept. It is the central that determines what connection interval will actually be used.
    But, lets say for the calculations sake that the central determines the shortest connection interval supported by the peripheral will be used - 20 ms.

    Thiago said:
    I am not sure, but I think I queue a notification every time I call the function ble_nus_data_send, if that is right, then it happens every time the timer triggers the saadc_callback functions, and as explained above, it seems to be configured to happen at every 1us.

    This depends on the size of the provided buffer. The DONE event will be generated every time the buffer is filled - how often this is depends on the size of the buffer. If you are using the SAADC example, the default buffer size is 5. Lets say for the sake of the calculations that you were producing 1 sample every 1 us.

    This would mean that you are trying to queue a notification ever 5th us, but you are only actually sending notifications every 20 ms. So, with these parameters, you will have attempted to queue 4000 notifications between every connection event. I would absolutely expect this to cause an NRF_ERROR_RESOURCES.

    So, this will not work for multiple reasons. Let us look at how we may resolve this situation.
    What is your applications requirements - how often do you need to sample the SAADC and how fast do you need to get these samples to your central device? 
    Is power consumption a part of the equation?

    Thiago said:
    But I don't know how can I see the HVN TX queue size. In what file would it be defined?

    This is set during the initialization of your BLE Nordic UART service.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

  • Hello, Karl.

    Do you intend to sample the SAADC every us? The minimum Acquisition time for the SAADC is 3 usand the maximal sampling frequency is therefore 200 kHz.
    Have you configured your SAADC channel to use 3 us acquisition time?

    I modified the saadc acquisition time to 3us as you said. I also changed the timer configuration to 5us, since transmitting data faster than the saadc can sample in pointless. 

    If you are using the SAADC example, the default buffer size is 5.

     Actually, I changed a bit the saadc example to fill a buffer with 64 samples. 

    What is your applications requirements - how often do you need to sample the SAADC and how fast do you need to get these samples to your central device? 
    Is power consumption a part of the equation?

    I want to sample signals with frequencies higher than 10kHz and faithfully reproduce the signal after BLE transmission. Power consumption is important, but the first requirement is the fidelity of the signal received in the central device. At the moment, I only can transmit and receive signals with frequencies up to 1Hz.

Related