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!

Parents
  • 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.

     

Reply
  • 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.

     

Children
No Data
Related