Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Speeding up application to send 24bit I2S data at 16 kHz via BLE

Hi,

I'm trying to send I2S data (~ 16 kHz, 24 bits) via BLE using the nRF52 DK and the nRF5 SDK 17.1.0. After I get the data from the I2S driver (by specification, they are in 32bit format), I add them to very simple ring buffer. The problem is, that this ring buffer becomes full too fast, overwriting older data (which has not been sent yet), which subsequently results in data loss.

Is there a way to speed up my application to avoid this or give higher priority to the application to be able to process the data faster (I guess the main problem is my loop which is casting the 32bit data into 24bit samples)?

Here is my applications logic together with the respective code:

global variables:

/*----------------- I2S -----------------*/
#define I2S_DATA_BLOCK_WORDS    32

static bool i2s_active = false;
static bool i2s_transfer = false;

/*----------------- BUFFERS -----------------*/
#define RNG_BUF_SIZE            (I2S_DATA_BLOCK_WORDS*4*60)
static uint32_t rng_buffer[RNG_BUF_SIZE];
static uint16_t rb_write_ptr = 0;
static uint16_t rb_read_ptr = 0;

static uint8_t m_array[243] = {0};

main():

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

    // Initialize.
    log_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();

    // Initialize TWI/ I2C & Setup ADC
    twi_adc_configuration();

    // Initialize i2s
    err_code = i2s_init();
    if (err_code == NRF_SUCCESS)
    {
        NRF_LOG_INFO("I2S successfully initialized.");
    }
    else
    {
        NRF_LOG_INFO("Error initializing I2S.");
    }

    // Initialize BLE
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init();
    conn_params_init();
    peer_manager_init();

    // Start advertising
    advertising_start(erase_bonds);

    for (;;)
    {
        if (i2s_transfer)
        {
            if (i2s_active != true)
            {
                err_code = start_i2s();

                if (err_code == NRF_SUCCESS)
                {
                    i2s_active = true;
                    NRF_LOG_INFO("I2S started.\n");
                } 
                else
                {
                    NRF_LOG_INFO("Error starting I2S.\n");
                }
            }

            // set back err_code
            err_code = NULL;
            if (rb_read_ptr < rb_write_ptr)
            {
                // fill up an array, which is later sent via BLE
                for (int i = 0; i <= 243; i+=3) 
                {
                    if (rb_read_ptr > RNG_BUF_SIZE)
                    {
                        rb_read_ptr = 0;
                        read_cnt++;
                        NRF_LOG_INFO("reading finished %d", read_cnt);
                    }

                    uint32_t const * p_word = NULL;
                    uint8_t sample[3];
      
                    // cast 32bit sample to 24bit sample
                    p_word = &rng_buffer[rb_read_ptr];
                    sample[0] = ((uint8_t const *)p_word)[2];
                    sample[1] = ((uint8_t const *)p_word)[1];
                    sample[2] = ((uint8_t const *)p_word)[0];

                    rb_read_ptr++;

                    copy 24bit sample to the array
                    memcpy(&m_array[i], &sample, sizeof(sample));
                }

                // when m_array is full, send data via BLE
                err_code = ble_aas_value_update(&m_aas, &m_array);
                if (err_code == NRF_SUCCESS)
                {
                    pkg_cnt++;
                    sample_no+=81;
                }
                if ((err_code != NRF_SUCCESS) &&
                    (err_code != NRF_ERROR_INVALID_STATE) &&
                    (err_code != NRF_ERROR_RESOURCES) &&
                    (err_code != NRF_ERROR_BUSY) &&
                    (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                   )
                {
                    APP_ERROR_CHECK(err_code);
                }
                else if (err_code == NRF_ERROR_RESOURCES)
                {
                    // if ble stack is full, wait until more space is available
                    // ble_ready will become true, when BLE_GATTS_EVT_HVN_TX_COMPLETE event is received
                    ble_ready = false;
                    while (!ble_ready)
                    {
                        idle_state_handle();
                    }
                    
                    // retry
                    err_code = ble_aas_value_update(&m_aas, &m_array);
                    if (err_code == NRF_SUCCESS)
                    {
                        pkg_cnt++;
                        sample_no+=81;
                    }
                    if ((err_code != NRF_SUCCESS) &&
                        (err_code != NRF_ERROR_INVALID_STATE) &&
                        (err_code != NRF_ERROR_RESOURCES) &&
                        (err_code != NRF_ERROR_BUSY) &&
                        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                       )
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                }
            }
        }

        if ((!i2s_transfer) &&
            (i2s_active)
           )
        {
            nrf_drv_i2s_stop();
            i2s_active = false;
            NRF_LOG_INFO("package added to ble stack: %d", pkg_cn);
            NRF_LOG_INFO("samples sent: %d", sample_no);
        }

        idle_state_handle();
        NRF_LOG_FLUSH();
    }

    bsp_board_leds_off();
}

the i2s data handler and the function which adds the received 32bit samples to the ring buffer:

static void write_rng_buffer(uint32_t const * p_block)
{
    if ((rb_write_ptr+I2S_DATA_BLOCK_WORDS) > RNG_BUF_SIZE)
    {
        rb_write_ptr = 0;
        write_cnt++;
        NRF_LOG_INFO("writing finished %d", write_cnt);
    }

    memcpy(&rng_buffer[rb_write_ptr], p_block, (I2S_DATA_BLOCK_WORDS*4));
    rb_write_ptr+=I2S_DATA_BLOCK_WORDS;
}

static void data_handler(nrf_drv_i2s_buffers_t const * p_released,
                         uint32_t                      status)
{
    // 'nrf_drv_i2s_next_buffers_set' is called directly from the handler
    // each time next buffers are requested, so data corruption is not
    // expected.
    ASSERT(p_released);

    // When the handler is called after the transfer has been stopped
    // (no next buffers are needed, only the used buffers are to be
    // released), there is nothing to do.
    if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
    {
        return;
    }

    // First call of this handler occurs right after the transfer is started.
    // No data has been transferred yet at this point, so there is nothing to
    // check. Only the buffers for the next part of the transfer should be
    // provided.
    if (!p_released->p_rx_buffer)
    {
        // .p_tx_buffer = m_buffer_tx[1] changed to .p_tx_buffer = NULL, since we only receive data
        nrf_drv_i2s_buffers_t const next_buffers = {
            .p_rx_buffer = m_buffer_rx[1],
            .p_tx_buffer = NULL,
        };
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));

    }
    else
    {
        write_rng_buffer(p_released->p_rx_buffer);

        // The driver has just finished accessing the buffers pointed by
        // 'p_released'. They can be used for the next part of the transfer
        // that will be scheduled now.
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
    }
}

any help and hint is appreciated!

  • I was checking the CPU usage by enabling NRF_PWR_MGMT_CONFIG_LOG_ENABLED and NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED. The output in the debug window can be seen below. It seems the cpu usage never exceeds 4 %, so  I guess there has to be a way to speed this process up?

    <info> app_timer: RTC: initialized.
    <info> pwr_mgmt: Init
    TWI device detected at address 0x18.
     Accessing Page no : 00 
     Error in register 1. Write = 01 but Read = 00
     Accessing Page no : 01 
     Accessing Page no : 00 <info> I2S: Initialized.
    <info> app: I2S successfully initialized.
    <info> app: Fast advertising.
    <info> pwr_mgmt: CPU Usage: 47%
    <debug> nrf_ble_gatt: Requesting to update ATT MTU to 247 bytes on connection 0x0.
    <debug> nrf_ble_gatt: Updating data length to 251 on connection 0x0.
    <info> app: PHY update requested.
    <info> app: Connected.
    <info> pwr_mgmt: CPU Usage: 0%
    <debug> nrf_ble_gatt: ATT MTU updated to 247 bytes on connection 0x0 (response).
    <debug> nrf_ble_gatt: Data length updated to 251 on connection 0x0.
    <debug> nrf_ble_gatt: max_rx_octets: 251
    <debug> nrf_ble_gatt: max_tx_octets: 251
    <debug> nrf_ble_gatt: max_rx_time: 2120
    <debug> nrf_ble_gatt: max_tx_time: 2120
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> I2S: Started.
    <info> app: I2S started.
    
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 5%
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 3%
    <info> pwr_mgmt: CPU Usage: 5%
    <info> pwr_mgmt: CPU Usage: 3%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> I2S: Stopped.
    <info> app: package added to ble stack: 281
    <info> app: samples sent: 60408
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%

  • Hello,

    I don't think the bottleneck is the 24->32 bit casting.

    I didn't look too much at the logic in your casting, and my I2S is a bit rusty, since there is no sound HW on the DKs. But is it correct that the p_released->p_rx_buffer in the data_handler() callback has samples of 32 bits, where the last 8 bits are blank (0x00 or 0xFF)? I.e. 0xAAAAAA00, and the next sample has 0xBBBBBB00, and 0xCCCCCC00 and so on?

    If so, after you cast it to 32 bytes, are you left with this in the buffer?

    0xAAAAAABB 0xBBBBCCCC 0xCCDDDDDD 0x... and so on? Or what does it look like after you cast it?

    I assume that your bottleneck is currently the BLE, and not the CPU or I2S. 

    I see that some parts of the application are missing, such as the implementation of ble_aas_value_update(). What does it look like? Is this always sending a large buffer? The trick in BLE is to always send large buffers (to reduce the overhead/payload ratio). Then, you need to keep the buffers full at all times for maximum throughput. 

    If this is satisfied, the throughput is up to the rest of your connection parameters. This isn't straight forward, but you need an MTU that is large enough (looks good from your log). What is your connection interval, connection event length, and PHY?

    To play around with the connection parameters, I suggest you take a look at the SDK\examples\ble_central_and_peripheral\experimental\ble_app_att_mtu_throughput

    I believe maxing the event length, using around 50ms connection interval, and using 2MBPS PHY will give the highest throughput.

    Best regards,

    Edvin

  • Hi Edvin,

    thanks for youor answer.

    Yes, the i2s buffer always provides 32 bit samples: "The size of the buffers is specified in a number of 32-bit words. Such a 32-bit memory word can either contain four 8-bit samples, two 16-bit samples or one right-aligned 24-bit sample sign extended to 32 bit." (from here). I'm quite sure that the casting works, since I'm expecting a sine wave to be sent and I'm receiving this (verified with nRF Connect for Desktop). 

    I tried to reduce the amount of code in my initial post, that's why I didn't post it. Here is the full BLE implementation including ble_aas_value_update(), which is based on the ble_app_template from the SDK and the nRF52-Bluetooth-Course (as is the overall project).

    //  ble_aas.c
    
    #include "sdk_common.h"
    #include "ble_srv_common.h"
    #include "ble_aas.h"
    #include <string.h>
    #include "nrf_gpio.h"
    #include "boards.h"
    #include "nrf_log.h"
    
    uint32_t ble_aas_value_update(ble_aas_t * p_aas, uint8_t *value){
        
        if (p_aas == NULL)
        {
            return NRF_ERROR_NULL;
        }
    
        // Update value in the GATT table with the value that is passed to this function
        uint32_t err_code = NRF_SUCCESS;
        ble_gatts_value_t gatts_value;
    
        // Initialize value struct.
        memset(&gatts_value, 0, sizeof(gatts_value));
    
        //gatts_value.len     = sizeof(value);
        gatts_value.len = 243;
        gatts_value.offset  = 0;
        gatts_value.p_value = value;
        //gatts_value.p_value = m_array;
    
        // Update database.
        err_code = sd_ble_gatts_value_set(p_aas->conn_handle,
                                            p_aas->aas_value_handle.value_handle,
                                            &gatts_value);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        // Notify peer that value of the charactersitc has changed
    
        // Send value if connected and notifying.
        // 1. check if connection handle is valid
        if ((p_aas->conn_handle != BLE_CONN_HANDLE_INVALID)) 
        {
            ble_gatts_hvx_params_t hvx_params;
    
            memset(&hvx_params, 0, sizeof(hvx_params));
    
            hvx_params.handle = p_aas->aas_value_handle.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = gatts_value.offset;
            //hvx_params.offset = 0;
            hvx_params.p_len  = &gatts_value.len;
            //hvx_params.p_len  = 244;
            hvx_params.p_data = gatts_value.p_value;
            //hvx_params.p_data = &value;
    
            err_code = sd_ble_gatts_hvx(p_aas->conn_handle, &hvx_params);
        }
        else
        {
            err_code = NRF_ERROR_INVALID_STATE;
        }
    
        return err_code;
    }
    
    /**@brief Function for handling the Write event.
     *
     * @param[in]   p_aas       AAS Service structure.
     * @param[in]   p_ble_evt   Event received from the BLE stack.
     */
    static void on_write(ble_aas_t * p_aas, ble_evt_t const * p_ble_evt)
    {
        ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        
        // Check if the handle passed with the event matches the Custom Value Characteristic handle.
        if (p_evt_write->handle == p_aas->aas_value_handle.value_handle)
        {
            // Put specific task here.
            #ifdef BOARD_PCA10040
                nrf_gpio_pin_toggle(LED_4);
            #endif
        }
    
        // Check if the Custom value CCCD is written to and that the value is the appropriate length, i.e 2 bytes.
        if ((p_evt_write->handle == p_aas->aas_value_handle.cccd_handle)
            && (p_evt_write->len == 2)
           )
        {
    
            // CCCD written, call application event handler
            if (p_aas->evt_handler != NULL)
            {
                ble_aas_evt_t evt;
    
                if (ble_srv_is_notification_enabled(p_evt_write->data))
                {
                    evt.evt_type = BLE_AAS_EVT_NOTIFICATION_ENABLED;
                }
                else
                {
                    evt.evt_type = BLE_AAS_EVT_NOTIFICATION_DISABLED;
                }
                // Call the application event handler.
                p_aas->evt_handler(p_aas, &evt);
            }
    
        }
    }
    
    /**@brief Function for handling the Connect event.
     *
     * @param[in]   p_aas       AAS Service structure.
     * @param[in]   p_ble_evt   Event received from the BLE stack.
     */
    static void on_connect(ble_aas_t * p_aas, ble_evt_t const * p_ble_evt)
    {
        p_aas->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    
        ble_aas_evt_t evt;
    
        evt.evt_type = BLE_AAS_EVT_CONNECTED;
    
        p_aas->evt_handler(p_aas, &evt);
       
        // Request PHY update
        NRF_LOG_INFO("PHY update requested.");
        ble_gap_phys_t const phys =
        {
            .rx_phys = BLE_GAP_PHY_2MBPS,
            .tx_phys = BLE_GAP_PHY_2MBPS
        };
    
        ret_code_t err_code;
        err_code = sd_ble_gap_phy_update(p_aas->conn_handle, &phys);
        APP_ERROR_CHECK(err_code);
    
        // switch on LED 1
        nrf_gpio_pin_toggle(LED_1);
    
    }
    
    /**@brief Function for handling the Disconnect event.
     *
     * @param[in]   p_aas       AAS Service structure.
     * @param[in]   p_ble_evt   Event received from the BLE stack.
     */
    static void on_disconnect(ble_aas_t * p_aas, ble_evt_t const * p_ble_evt)
    {
        UNUSED_PARAMETER(p_ble_evt);
        p_aas->conn_handle = BLE_CONN_HANDLE_INVALID;
    
        // switch off LED 1
        nrf_gpio_pin_toggle(LED_1);
    }
    
    void on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ble_aas_t * p_aas = (ble_aas_t *) p_context;
        
        if (p_aas == NULL || p_ble_evt == NULL)
        {
            return;
        }
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                on_connect(p_aas, p_ble_evt);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                on_disconnect(p_aas, p_ble_evt);
                break;
    
            case BLE_GATTS_EVT_WRITE:
                on_write(p_aas, p_ble_evt);
            default:
                // No implementation needed.
                break;
        }
    }
    
    /**@brief Function for adding the characteristics.
     *
     * @param[in]   p_aas        AAS Service structure.
     * @param[in]   p_aas_init   Information needed to initialize the service.
     *
     * @return      NRF_SUCCESS on success, otherwise an error code.
     */
    static uint32_t add_characteristic(ble_aas_t * p_aas, const ble_aas_init_t * p_aas_init)
    {
        uint32_t            err_code;
        ble_gatts_char_md_t char_md;
        ble_gatts_attr_md_t cccd_md;
        ble_gatts_attr_t    attr_char_value;
        ble_uuid_t          ble_uuid;
        ble_gatts_attr_md_t attr_md;
    
        memset(&cccd_md, 0, sizeof(cccd_md));
    
        //  Read  operation on CCCD should be possible without authentication.
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
        
        cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    
        memset(&char_md, 0, sizeof(char_md));
    
        char_md.char_props.read   = 1;
        char_md.char_props.write  = 1;
        char_md.char_props.notify = 1; 
        char_md.p_char_user_desc  = NULL;
        char_md.p_char_pf         = NULL;
        char_md.p_user_desc_md    = NULL;
        char_md.p_cccd_md         = &cccd_md; 
        char_md.p_sccd_md         = NULL;
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_aas_init->initial_value_char_attr_md.read_perm;
        attr_md.write_perm = p_aas_init->initial_value_char_attr_md.write_perm;
        attr_md.vloc       = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth    = 0;
        attr_md.wr_auth    = 0;
        attr_md.vlen       = 0;
    
        ble_uuid.type = p_aas->uuid_type;
        ble_uuid.uuid = AUDIO_CHAR_UUID;
    
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        // set initial size of the attribute
        // attr_char_value.init_len  = sizeof(uint8_t);
        attr_char_value.init_len = 243;
        attr_char_value.init_offs = 0;
        // set max size of attribute 
        // attr_char_value.max_len   = sizeof(uint8_t);
        attr_char_value.max_len = 243;
    
        err_code = sd_ble_gatts_characteristic_add(p_aas->service_handle, &char_md,
                                                   &attr_char_value,
                                                   &p_aas->aas_value_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        return NRF_SUCCESS;
    }
    
    uint32_t ble_aas_init(ble_aas_t * p_aas, const ble_aas_init_t * p_aas_init)
    {
        if (p_aas == NULL || p_aas_init == NULL)
        {
            return NRF_ERROR_NULL;
        }
    
        uint32_t   err_code;
        ble_uuid_t ble_uuid;
    
        // Initialize service structure
        p_aas->evt_handler = p_aas_init->evt_handler;
        p_aas->conn_handle = BLE_CONN_HANDLE_INVALID;
    
        // Add AAS Service UUID
        ble_uuid128_t base_uuid = {AAS_SERVICE_UUID_BASE};
        err_code = sd_ble_uuid_vs_add(&base_uuid, &p_aas->uuid_type);
        VERIFY_SUCCESS(err_code);
    
        ble_uuid.type = p_aas->uuid_type;
        ble_uuid.uuid = AAS_SERVICE_UUID;
    
        // Add the AAS Service
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_aas->service_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
        
        return add_characteristic(p_aas, p_aas_init);

    And the BLE related definitions from my main.c file:

    #define APP_ADV_INTERVAL                300                                     /**< The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms). */
    #define APP_ADV_DURATION                18000                                   /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    #define APP_BLE_OBSERVER_PRIO           3                                       /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    #define APP_BLE_CONN_CFG_TAG            1                                       /**< A tag identifying the SoftDevice BLE configuration. */
    
    #define DATA_LENGTH_DEFAULT             27
    #define DATA_LENGTH_MAX                 251
    
    #define CONN_INTERVAL_DEFAULT           (uint16_t)(MSEC_TO_UNITS(7.5, UNIT_1_25_MS)) 
    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.1 seconds). */
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(50, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (0.2 second). */
    #define SLAVE_LATENCY                   0                                       /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory timeout (4 seconds). */
    
    #define BLE_PHY_DEFAULT                 BLE_GAP_PHY_2MBPS
    
    #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 SEC_PARAM_BOND                  1                                       /**< Perform bonding. */
    #define SEC_PARAM_MITM                  0                                       /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC                  0                                       /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS              0                                       /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
    #define SEC_PARAM_OOB                   0                                       /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE          7                                       /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE          16                                      /**< Maximum encryption key size. */

    From my understanding, and looking at the log above, the connection parameters should be fine. The theoretisch BLE throughput should also be fine to send the amount of data I require (16.000 Hz x 24 bits). This is why I came the conclusion that the BLE should not be the bottleneck. The receiving peer is btw a nrf52840 Dongle, so I'm also not expecting any problems there. However, I will have a look at nRF for Desktop logfiles and share that with you in a minute.

    If you need more details on the implementation, I can share my project/ all files here as well.

     

  • Good morning,

    after my answer from yesterday I checked again my connection parameters and, in fact, I was using 7.5 ms as event length. I changed that now to 50 ms by changing

    #define CONN_INTERVAL_DEFAULT           (uint16_t)(MSEC_TO_UNITS(7.5, UNIT_1_25_MS)) 
    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(50, UNIT_1_25_MS) 

    to

    #define CONN_INTERVAL_DEFAULT           (uint16_t)(MSEC_TO_UNITS(50, UNIT_1_25_MS)) 
    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(50, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.1 seconds). */
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(50, UNIT_1_25_MS) 

    and according to nRF Connect for Desktop, my connection parameters should now allow maximum throughput (2 M PHY and optimal connection parameters). I'm attaching my log file from nRF Connect.2022-08-17T06_13_35.488Z-log.txt

    However, this did not improve the overall process so much - I'm still receiving less samples than I would expect when recording, e.g., 3 seconds (~ 48.000 samples expected). So I looked again at the maximum throughput example. One significant difference in the example's sdk_config.h is

    // <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 400
    #endif

    this value is 6 in my application. I tried setting the value first to 400 and then to 40 (to achieve 50 ms), but I got the following warnings:

    <warning> nrf_sdh_ble: Insufficient RAM allocated for the SoftDevice.
    <warning> nrf_sdh_ble: Change the RAM start location from 0x20002AD8 to 0x20002BE0.
    <warning> nrf_sdh_ble: Maximum RAM size for application is 0xD420.
    <error> nrf_sdh_ble: sd_ble_enable() returned NRF_ERROR_NO_MEM.
    

    I changed the RAM start location as I was told, but since then my application stops working after roughly 1 second without a clear error message. I can only see an arrow in the 'Disassembly' window of Segger emStudio pointing to

    00000A60    4B01    ldr r3,
    

    I haven't been able to find out what that means and how to address this issue. Can you help me out?

  • Moritz_S said:
    I haven't been able to find out what that means and how to address this issue. Can you help me out?

    That is a hardfault. These are a bit tricky to debug. 

    The log (from nRF Connect) only states that the peripheral disappeared, and hence you got a timeout disconnection.

    If you sent the appliication you are using, will I be able to replicate the issue (hardfault) without an external microphone attached?

    In fact, I think you should try to reduce the application to only send dummy data, so that we can exclude the I2S operations for now. Just to see whether the I2S + the data processing is slowing down things at all.

    If you strip it down, and send it so that I can try to run it on a DK, I can see what your connection parameters looks like. 

    In case you are interrested, I'll attach a couple of modified examples (ble_app_uart and ble_app_uart_c) which will just send dummy data from one device to the other based on some button presses. 

    0825.nus_throughput.zip

    It runs in SDK16.0.0, so please download and test it in SDK16.0.0. Just unzip it next to the ble_app_uart example in the folder structure. Flash one DK with the peripheral example and another with the central, and press button 1 (I think) on one of them, and monitor the RTT log output. 

    Best regards,

    Edvin

Related