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

Enable notifications for NUS and another service in BLE Central device avoiding NRF_ERROR_BUSY

Hello everyone,

I'm developing an application for boards with NRF52832, Softdevice S132 3.00 and SDK 12.3.0.

The network architecture consists of:

  • one peripheral board (slave) with NUS and BAS services
  • one central plus peripheral board (master) with central plus peripheral NUS and BAS services
  • an android device which would connect to the central device (master)

The peripheral/slave application is working properly, has been tried directly connecting to the android device. It's based on the UART peripheral example of the SDK.

The central/master application is based on the ble_central_and_peripheral ble_app_hrs_rscs_relay example. I changed the HRS and RSCS services for NUS service and set it up for only one peripheral link. When connected to the Android device and a peripheral device with only NUS, it works properly. But when I added the BAS service to the slave, the error appeared on the master.

And thus, when the master device discovers the two UUIDs of NUS and BAS services in the advertising data, it tries to enable the notification and register the CCCD with the softdevice. This works for the first service (BAS in this case), but the second time it doesn't work because the softdevice returns NRF_ERROR_BUSY.

As I've learned, this is normal when I don't give enough time to the softdevice to do the ATT write request to the peer (as in this diagram). I've seen some other threads in the devzone with the same problem, but I can't find a workaround.

What I've tried so far is to create a flag which waits for the softdevice to answer with a BLE_GATTC_EVT_WRITE_RSP event after I register the first service. Then, I wait for the flag to be activated until try to register the second service. But the problem is that the while I write to wait for the flag turns out to be an endless loop. Could it be a problem of interrupt priorities?

This is the relevant code I added to the application:

Flag declaration:

static volatile uint8_t sd_gattc_write_rsp_rx = 1;

BLE_GATTC_EVT_WRITE_RSP in on_ble_central_evt function:

case BLE_GATTC_EVT_WRITE_RSP:
	// Response from SD
	NRF_LOG_DEBUG("**** GATT Client Write Response.\r\n");
	sd_gattc_write_rsp_rx = 1;
	break; // BLE_GATTC_EVT_WRITE_RSP

db_disc_handler function:

static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
{
	while(!sd_gattc_write_rsp_rx)
	{
	        // The program stays here forever
			ret_code_t err_code = sd_app_evt_wait();
			APP_ERROR_CHECK(err_code);
	};
	sd_gattc_write_rsp_rx = 0;
    ble_bas_on_db_disc_evt(&m_ble_bas_c, p_evt);
	ble_nus_c_on_db_disc_evt(&m_ble_nus_c, p_evt);
}

Here is where the program crashes if I don't wait for the softdevice response:

static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, const ble_nus_c_evt_t * p_ble_nus_evt)
{
    uint32_t err_code;
    switch (p_ble_nus_evt->evt_type)
    {
        case BLE_NUS_C_EVT_DISCOVERY_COMPLETE:
            err_code = ble_nus_c_handles_assign(p_ble_nus_c, p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);
            APP_ERROR_CHECK(err_code);

            err_code = ble_nus_c_rx_notif_enable(p_ble_nus_c);
            APP_ERROR_CHECK(err_code); // ERROR 0x11 (17 - NRF_ERROR_BUSY)
            NRF_LOG_INFO("Nordic UART service discovered on conn_handle 0x%x\r\n", p_ble_nus_evt->conn_handle);
            break;

        case BLE_NUS_C_EVT_NUS_RX_EVT:
			// Handle data
            break;

        case BLE_NUS_C_EVT_DISCONNECTED:
            NRF_LOG_INFO("Nordic UART service disconnected\r\n");
            scan_start();
            break;
    }
}

How should I properly manage the BLE_GATTC_EVT_WRITE_RSP of the softdevice?

Anyone has an example of a central device connecting to a peripheral device with two services?

Thank you very much in advance.

  • Hi,

    So if I understand you correctly the problem is that you can't enable two CCCDs back-to-back because you get the busy error when you write to the second CCCD attribute?

    I advise you to have a look at the BLE Heart Rate Collector Example. It shows how a central can enable CCCDs on two or more characteristics one after another. Shortly explained, the example sets up a buffer of "messages" to be written and then processes this buffer on every successive BLE_GATTC_EVT_WRITE_RSP event. 

  • Thank you for your answer, Martin. You understood it correctly.

    I've taken a look at the example you mention, although for SDK 12.3.0. I've seen that for the two services (HRS and BAS) it uses a buffer that waits for the BLE_GATTC_EVT_WRITE_RSP response from the SD. It is programmed independently inside the central-service c-files (ble_bas_c.c and ble_hrs_c.c). It looks great and it's independent of the configuration of main.c.

    It seems that my problem lays on the NUS central service. Its structure differs from the one of HRS, BAS and the rest of standard service and doesn't implement the buffer to enable CCCDs. Do you know if there is any reason for this?

    For example, the cccd_configure functions of NUS...

    /**@brief Function for creating a message for writing to the CCCD.
     */
    static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable)
    {
        uint8_t buf[BLE_CCCD_VALUE_LEN];
    
        buf[0] = enable ? BLE_GATT_HVX_NOTIFICATION : 0;
        buf[1] = 0;
    
        const ble_gattc_write_params_t write_params = {
            .write_op = BLE_GATT_OP_WRITE_REQ,
            .flags    = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
            .handle   = cccd_handle,
            .offset   = 0,
            .len      = sizeof(buf),
            .p_value  = buf
        };
    
        return sd_ble_gattc_write(conn_handle, &write_params);
    }

    ...and BAS...

    /**@brief Function for creating a message for writing to the CCCD.
     */
    static uint32_t cccd_configure(uint16_t conn_handle, uint16_t handle_cccd, bool notification_enable)
    {
        NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d\r\n",
                                                                handle_cccd,conn_handle);
    
        tx_message_t * p_msg;
        uint16_t       cccd_val = notification_enable ? BLE_GATT_HVX_NOTIFICATION : 0;
    
        p_msg              = &m_tx_buffer[m_tx_insert_index++];
        m_tx_insert_index &= TX_BUFFER_MASK;
    
        p_msg->req.write_req.gattc_params.handle   = handle_cccd;
        p_msg->req.write_req.gattc_params.len      = WRITE_MESSAGE_LENGTH;
        p_msg->req.write_req.gattc_params.p_value  = p_msg->req.write_req.gattc_value;
        p_msg->req.write_req.gattc_params.offset   = 0;
        p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ;
        p_msg->req.write_req.gattc_value[0]        = LSB_16(cccd_val);
        p_msg->req.write_req.gattc_value[1]        = MSB_16(cccd_val);
        p_msg->conn_handle                         = conn_handle;
        p_msg->type                                = WRITE_REQ;
    
        tx_buffer_process();
        return NRF_SUCCESS;
    }

    What do you recommend? I can't alter the SDK files in order to keep other projects working.

    Should I create my own version of ble_nus_c.c using the structure and behavior of BAS and the other services? Would that suffice for this purpose of enabling two CCCDs?

  • You might be on to something here. It seems like there is a chance of GATTC write collisions in the NUS client implementation. 

    I would recommend implementing your own version with a buffer.

     

  • I think that might be it. I replaced ble_nus_c.c from the SDK with a modified copy which includes the message-buffer technique used in the other services. So far, it works, with no undesired effect.

    I attach the code to this message, for anyone having the same problem. I think it would be reasonable to patch it as well in the SDK if no one reports any side-effect.

    /**
     * Copyright (c) 2012 - 2017, Nordic Semiconductor ASA
     * 
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     * 
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     * 
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     * 
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     * 
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     * 
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     * 
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * 
     */
    #include "sdk_common.h"
    #if NRF_MODULE_ENABLED(BLE_NUS_C)
    #include <stdlib.h> // definition of NULL
    
    #include "ble.h"
    #include "ble_nus_c.h"
    #include "ble_gattc.h"
    #include "ble_srv_common.h"
    #include "app_error.h"
    #define NRF_LOG_MODULE_NAME "BLE_NUS_C"
    #include "nrf_log.h"
    
    #define TX_BUFFER_MASK         0x07                  /**< TX Buffer mask, must be a mask of continuous zeroes, followed by continuous sequence of ones: 000...111. */
    #define TX_BUFFER_SIZE         (TX_BUFFER_MASK + 1)  /**< Size of send buffer, which is 1 higher than the mask. */
    
    #define WRITE_MESSAGE_LENGTH   BLE_CCCD_VALUE_LEN    /**< Length of the write message for CCCD. */
    
    typedef enum
    {
        READ_REQ,      /**< Type identifying that this tx_message is a read request. */
        WRITE_REQ      /**< Type identifying that this tx_message is a write request. */
    } tx_request_t;
    
    
    /**@brief Structure for writing a message to the peer, i.e. CCCD.
     */
    typedef struct
    {
        uint8_t                  gattc_value[WRITE_MESSAGE_LENGTH];  /**< The message to write. */
        ble_gattc_write_params_t gattc_params;                       /**< The GATTC parameters for this message. */
    } write_params_t;
    
    /**@brief Structure for holding the data that will be transmitted to the connected central.
     */
    typedef struct
    {
        uint16_t     conn_handle;  /**< Connection handle to be used when transmitting this message. */
        tx_request_t type;         /**< Type of message. (read or write). */
        union
        {
            uint16_t       read_handle;  /**< Read request handle. */
            write_params_t write_req;    /**< Write request message. */
        } req;
    } tx_message_t;
    
    
    static tx_message_t  m_tx_buffer[TX_BUFFER_SIZE];  /**< Transmit buffer for the messages that will be transmitted to the central. */
    static uint32_t      m_tx_insert_index = 0;        /**< Current index in the transmit buffer where the next message should be inserted. */
    static uint32_t      m_tx_index        = 0;        /**< Current index in the transmit buffer containing the next message to be transmitted. */
    
    /**@brief Function for passing any pending request from the buffer to the stack.
     */
    static void tx_buffer_process(void)
    {
        if (m_tx_index != m_tx_insert_index)
        {
            uint32_t err_code;
    
            if (m_tx_buffer[m_tx_index].type == READ_REQ)
            {
                err_code = sd_ble_gattc_read(m_tx_buffer[m_tx_index].conn_handle,
                                             m_tx_buffer[m_tx_index].req.read_handle,
                                             0);
            }
            else
            {
                err_code = sd_ble_gattc_write(m_tx_buffer[m_tx_index].conn_handle,
                                              &m_tx_buffer[m_tx_index].req.write_req.gattc_params);
            }
            if (err_code == NRF_SUCCESS)
            {
                NRF_LOG_DEBUG("SD Read/Write API returns Success..\r\n");
                m_tx_index++;
                m_tx_index &= TX_BUFFER_MASK;
            }
            else
            {
                NRF_LOG_DEBUG("SD Read/Write API returns error. This message sending will be "
                    "attempted again..\r\n");
            }
        }
    }
    
    
    /**@brief Function for handling write response events.
     *
     * @param[in] p_nus_c   Pointer to the Nordic UART Service Client Structure.
     * @param[in] p_ble_evt Pointer to the SoftDevice event.
     */
    static void on_write_rsp(ble_nus_c_t * p_nus_c, const ble_evt_t * p_ble_evt)
    {
        // Check if the event if on the link for this instance
        if (p_nus_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
        {
            return;
        }
        // Check if there is any message to be sent across to the peer and send it.
        tx_buffer_process();
    }
    
    
    /**@brief     Function for handling read response events.
     *
     * @details   This function will validate the read response and raise the appropriate
     *            event to the application.
     *
     * @param[in] p_nus_c   Pointer to the Nordic UART Service Client Structure.
     * @param[in] p_ble_evt Pointer to the SoftDevice event.
     */
    static void on_read_rsp(ble_nus_c_t * p_nus_c, const ble_evt_t * p_ble_evt)
    {
        const ble_gattc_evt_read_rsp_t * p_response;
    
        // Check if the event if on the link for this instance
        if (p_nus_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
        {
            return;
        }
    
        p_response = &p_ble_evt->evt.gattc_evt.params.read_rsp;
    
        if (p_response->handle == p_nus_c->handles.nus_rx_handle)
        {
            ble_nus_c_evt_t evt;
    
            evt.conn_handle = p_ble_evt->evt.gattc_evt.conn_handle;
            evt.evt_type = BLE_NUS_C_EVT_NUS_RX_EVT;
    
    				memcpy(evt.p_data,p_response->data,p_response->len);
    				evt.data_len = p_response->len;
    
            p_nus_c->evt_handler(p_nus_c, &evt);
        }
        // Check if there is any buffered transmissions and send them.
        tx_buffer_process();
    }
    
    void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt)
    {
        ble_nus_c_evt_t nus_c_evt;
        memset(&nus_c_evt,0,sizeof(ble_nus_c_evt_t));
    
        ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;
    
        // Check if the NUS was discovered.
        if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
            p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_NUS_SERVICE &&
            p_evt->params.discovered_db.srv_uuid.type == p_ble_nus_c->uuid_type)
        {
    
            uint32_t i;
    
            for (i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                switch (p_chars[i].characteristic.uuid.uuid)
                {
                    case BLE_UUID_NUS_TX_CHARACTERISTIC:
                        nus_c_evt.handles.nus_tx_handle = p_chars[i].characteristic.handle_value;
                        break;
    
                    case BLE_UUID_NUS_RX_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rx_handle = p_chars[i].characteristic.handle_value;
                        nus_c_evt.handles.nus_rx_cccd_handle = p_chars[i].cccd_handle;
                        break;
    
                    default:
                        break;
                }
            }
    
    				NRF_LOG_INFO("Nordic UART Service discovered at peer.\r\n");
    
            if (p_ble_nus_c->evt_handler != NULL)
            {
                nus_c_evt.conn_handle = p_evt->conn_handle;
                nus_c_evt.evt_type    = BLE_NUS_C_EVT_DISCOVERY_COMPLETE;
                p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
            }
        }
    }
    
    /**@brief     Function for handling Handle Value Notification received from the SoftDevice.
     *
     * @details   This function will uses the Handle Value Notification received from the SoftDevice
     *            and checks if it is a notification of the NUS RX characteristic from the peer. If
     *            it is, this function will decode the data and send it to the
     *            application.
     *
     * @param[in] p_ble_nus_c Pointer to the NUS Client structure.
     * @param[in] p_ble_evt   Pointer to the BLE event received.
     */
    static void on_hvx(ble_nus_c_t * p_ble_nus_c, const ble_evt_t * p_ble_evt)
    {
        // HVX can only occur from client sending.
        if ( (p_ble_nus_c->handles.nus_rx_handle != BLE_GATT_HANDLE_INVALID)
                && (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.nus_rx_handle)
                && (p_ble_nus_c->evt_handler != NULL)
            )
        {
            ble_nus_c_evt_t ble_nus_c_evt;
    
            ble_nus_c_evt.evt_type = BLE_NUS_C_EVT_NUS_RX_EVT;
            ble_nus_c_evt.p_data   = (uint8_t *)p_ble_evt->evt.gattc_evt.params.hvx.data;
            ble_nus_c_evt.data_len = p_ble_evt->evt.gattc_evt.params.hvx.len;
    
            p_ble_nus_c->evt_handler(p_ble_nus_c, &ble_nus_c_evt);
        }
    }
    
    uint32_t ble_nus_c_init(ble_nus_c_t * p_ble_nus_c, ble_nus_c_init_t * p_ble_nus_c_init)
    {
        uint32_t      err_code;
        ble_uuid_t    uart_uuid;
        ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;
    
        VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
        VERIFY_PARAM_NOT_NULL(p_ble_nus_c_init);
    
        err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_ble_nus_c->uuid_type);
        VERIFY_SUCCESS(err_code);
    
        uart_uuid.type = p_ble_nus_c->uuid_type;
        uart_uuid.uuid = BLE_UUID_NUS_SERVICE;
    
        p_ble_nus_c->conn_handle           = BLE_CONN_HANDLE_INVALID;
        p_ble_nus_c->evt_handler           = p_ble_nus_c_init->evt_handler;
        p_ble_nus_c->handles.nus_rx_handle = BLE_GATT_HANDLE_INVALID;
        p_ble_nus_c->handles.nus_tx_handle = BLE_GATT_HANDLE_INVALID;
    
        return ble_db_discovery_evt_register(&uart_uuid);
    }
    
    void ble_nus_c_on_ble_evt(ble_nus_c_t * p_ble_nus_c, const ble_evt_t * p_ble_evt)
    {
        if ((p_ble_nus_c == NULL) || (p_ble_evt == NULL))
        {
            return;
        }
    
        if ( (p_ble_nus_c->conn_handle != BLE_CONN_HANDLE_INVALID)
           &&(p_ble_nus_c->conn_handle != p_ble_evt->evt.gap_evt.conn_handle)
           )
        {
            return;
        }
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GATTC_EVT_HVX:
                on_hvx(p_ble_nus_c, p_ble_evt);
                break;
    
            case BLE_GATTC_EVT_WRITE_RSP:
                on_write_rsp(p_ble_nus_c, p_ble_evt);
                break;
    
            case BLE_GATTC_EVT_READ_RSP:
                on_read_rsp(p_ble_nus_c, p_ble_evt);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                if (p_ble_evt->evt.gap_evt.conn_handle == p_ble_nus_c->conn_handle
                        && p_ble_nus_c->evt_handler != NULL)
                {
                    ble_nus_c_evt_t nus_c_evt;
    
                    nus_c_evt.evt_type = BLE_NUS_C_EVT_DISCONNECTED;
    
                    p_ble_nus_c->conn_handle = BLE_CONN_HANDLE_INVALID;
                    p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
                }
                break;
        }
    }
    
    /**@brief Function for creating a message for writing to the CCCD.
     */
    static uint32_t cccd_configure(uint16_t conn_handle, uint16_t handle_cccd, bool notification_enable)
    {
        NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d\r\n",
                                                                handle_cccd,conn_handle);
    
        tx_message_t * p_msg;
        uint16_t       cccd_val = notification_enable ? BLE_GATT_HVX_NOTIFICATION : 0;
    
        p_msg              = &m_tx_buffer[m_tx_insert_index++];
        m_tx_insert_index &= TX_BUFFER_MASK;
    
        p_msg->req.write_req.gattc_params.handle   = handle_cccd;
        p_msg->req.write_req.gattc_params.len      = WRITE_MESSAGE_LENGTH;
        p_msg->req.write_req.gattc_params.p_value  = p_msg->req.write_req.gattc_value;
        p_msg->req.write_req.gattc_params.offset   = 0;
        p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ;
        p_msg->req.write_req.gattc_value[0]        = LSB_16(cccd_val);
        p_msg->req.write_req.gattc_value[1]        = MSB_16(cccd_val);
        p_msg->conn_handle                         = conn_handle;
        p_msg->type                                = WRITE_REQ;
    
        tx_buffer_process();
        return NRF_SUCCESS;
    }
    
    uint32_t ble_nus_c_rx_notif_enable(ble_nus_c_t * p_ble_nus_c)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
    
        if ( (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
           ||(p_ble_nus_c->handles.nus_rx_cccd_handle == BLE_GATT_HANDLE_INVALID)
           )
        {
            return NRF_ERROR_INVALID_STATE;
        }
        return cccd_configure(p_ble_nus_c->conn_handle,p_ble_nus_c->handles.nus_rx_cccd_handle, true);
    }
    
    uint32_t ble_nus_c_string_send(ble_nus_c_t * p_ble_nus_c, uint8_t * p_string, uint16_t length)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
    
        if (length > BLE_NUS_MAX_DATA_LEN)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
        if ( p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
    		tx_message_t * msg;
    
        msg                  = &m_tx_buffer[m_tx_insert_index++];
        m_tx_insert_index   &= TX_BUFFER_MASK;
    
    		msg->req.write_req.gattc_params.handle   = p_ble_nus_c->handles.nus_tx_handle;
        msg->req.write_req.gattc_params.len      = length;
        msg->req.write_req.gattc_params.p_value  = p_string;
        msg->req.write_req.gattc_params.offset   = 0;
        msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_CMD;
    
        msg->conn_handle     = p_ble_nus_c->conn_handle;
        msg->type            = WRITE_REQ;
    
        tx_buffer_process();
        return NRF_SUCCESS;
    }
    
    
    uint32_t ble_nus_c_handles_assign(ble_nus_c_t * p_ble_nus,
                                      const uint16_t conn_handle,
                                      const ble_nus_c_handles_t * p_peer_handles)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_nus);
    
        p_ble_nus->conn_handle = conn_handle;
        if (p_peer_handles != NULL)
        {
            p_ble_nus->handles.nus_rx_cccd_handle = p_peer_handles->nus_rx_cccd_handle;
            p_ble_nus->handles.nus_rx_handle      = p_peer_handles->nus_rx_handle;
            p_ble_nus->handles.nus_tx_handle      = p_peer_handles->nus_tx_handle;
        }
        return NRF_SUCCESS;
    }
    #endif // NRF_MODULE_ENABLED(BLE_NUS_C)
    

Related