Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Nus client and write to secure characteristic

Hi,

I have modified the nus service and added two new characteristics called txs and rxs which support read and write protection.

on the peripheral i have set BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM for txs and rxs

The service is working as expected from NRF connect for android.

Now I have modified the nus client service as well and implemented my own ble_nus_c_string_send_secure, which writes to the secure characteristic.

I have bonded the central device with nus client to peripheral with nus service.

the ble_nus_c_string_send command works and peripheral receives the string, but ble_nus_c_string_send_secure command returns NRF_SUCCESS but peripheral does not receive the string.

Am I missing something?

for writing to a secured characteristic, do I have to pass more to the sd_ble_gattc_write function?

Thanks,

Raghav

  • Hello,

    So, what does your implementation look like? What is the difference between ble_nus_c_string_send and ble_nus_c_string_send_secure? Do they write to the same characteristic, or does it write to the new characteristics, rxs? Is the rxs set up properly on the peripheral?

     

    It is a bit difficult to say what the issue may be only with a brief description of your implementation. Could you upload at least the implementation of ble_nus_c_string_send_secure()?

     

    Best regards,

    Edvin

  • Hi Edvin,

    I will attach files that I have changed and this is how I have configured the NUS service on the peripheral

    BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&nus_init.nus_rxs_attr_md.cccd_write_perm);
    BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&nus_init.nus_rxs_attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&nus_init.nus_rxs_attr_md.write_perm);
    
    BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&nus_init.nus_txs_attr_md.cccd_write_perm);
    BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&nus_init.nus_txs_attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&nus_init.nus_txs_attr_md.write_perm);


    Please check the function ble_nus_c_string_send_secure in the nus client
  • /**
     * Copyright (c) 2012 - 2018, 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)
    #include "ble.h"
    #include "ble_nus.h"
    #include "ble_srv_common.h"
    
    #define NRF_LOG_MODULE_NAME ble_nus
    #if BLE_NUS_CONFIG_LOG_ENABLED
    #define NRF_LOG_LEVEL       BLE_NUS_CONFIG_LOG_LEVEL
    #define NRF_LOG_INFO_COLOR  BLE_NUS_CONFIG_INFO_COLOR
    #define NRF_LOG_DEBUG_COLOR BLE_NUS_CONFIG_DEBUG_COLOR
    #else // BLE_NUS_CONFIG_LOG_ENABLED
    #define NRF_LOG_LEVEL       0
    #endif // BLE_NUS_CONFIG_LOG_ENABLED
    #include "nrf_log.h"
    NRF_LOG_MODULE_REGISTER();
    
    
    #define BLE_UUID_NUS_TX_CHARACTERISTIC 0x0003               /**< The UUID of the TX Characteristic. */
    #define BLE_UUID_NUS_RX_CHARACTERISTIC 0x0002               /**< The UUID of the RX Characteristic. */
    #define BLE_UUID_NUS_TXS_CHARACTERISTIC 0x0005               /**< The UUID of the TXS Characteristic. */
    #define BLE_UUID_NUS_RXS_CHARACTERISTIC 0x0004               /**< The UUID of the RXS Characteristic. */
    
    #define BLE_NUS_MAX_RX_CHAR_LEN        BLE_NUS_MAX_DATA_LEN /**< Maximum length of the RX Characteristic (in bytes). */
    #define BLE_NUS_MAX_TX_CHAR_LEN        BLE_NUS_MAX_DATA_LEN /**< Maximum length of the TX Characteristic (in bytes). */
    
    #define NUS_BASE_UUID                  {{0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}} /**< Used vendor specific UUID. */
    
    
    /**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the SoftDevice.
     *
     * @param[in] p_nus     Nordic UART Service structure.
     * @param[in] p_ble_evt Pointer to the event received from BLE stack.
     */
    static void on_connect(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
    {
        ret_code_t                 err_code;
        ble_nus_evt_t              evt;
        ble_gatts_value_t          gatts_val;
        uint8_t                    cccd_value[2];
        ble_nus_client_context_t * p_client = NULL;
    
        err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage,
                                     p_ble_evt->evt.gap_evt.conn_handle,
                                     (void *) &p_client);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                          p_ble_evt->evt.gap_evt.conn_handle);
        }
    
        uint8_t send_comm_started_event = 0;
    
        /* Check the hosts CCCD value to inform of readiness to send data using the RX characteristic */
        memset(&gatts_val, 0, sizeof(ble_gatts_value_t));
        gatts_val.p_value = cccd_value;
        gatts_val.len     = sizeof(cccd_value);
        gatts_val.offset  = 0;
    
        err_code = sd_ble_gatts_value_get(p_ble_evt->evt.gap_evt.conn_handle,
                                          p_nus->tx_handles.cccd_handle,
                                          &gatts_val);
    
        if ((err_code == NRF_SUCCESS)     &&
            (p_nus->data_handler != NULL) &&
            ble_srv_is_notification_enabled(gatts_val.p_value))
        {
            if (p_client != NULL)
            {
                p_client->is_notification_enabled = true;
            }
            send_comm_started_event = 1;
        }
    
        /* Check the hosts CCCD value to inform of readiness to send data using the RX characteristic */
        memset(&gatts_val, 0, sizeof(ble_gatts_value_t));
        gatts_val.p_value = cccd_value;
        gatts_val.len     = sizeof(cccd_value);
        gatts_val.offset  = 0;
    
        err_code = sd_ble_gatts_value_get(p_ble_evt->evt.gap_evt.conn_handle,
                                          p_nus->txs_handles.cccd_handle,
                                          &gatts_val);
    
        if ((err_code == NRF_SUCCESS)     &&
            (p_nus->data_handler != NULL) &&
            ble_srv_is_notification_enabled(gatts_val.p_value))
        {
            if (p_client != NULL)
            {
                p_client->is_secure_notification_enabled = true;
            }
            send_comm_started_event = 1;
        }
    
        if(send_comm_started_event) 
        {
            memset(&evt, 0, sizeof(ble_nus_evt_t));
            evt.type        = BLE_NUS_EVT_COMM_STARTED;
            evt.p_nus       = p_nus;
            evt.conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            evt.p_link_ctx  = p_client;
    
            p_nus->data_handler(&evt);
        }
    
    }
    
    
    /**@brief Function for handling the @ref BLE_GATTS_EVT_WRITE event from the SoftDevice.
     *
     * @param[in] p_nus     Nordic UART Service structure.
     * @param[in] p_ble_evt Pointer to the event received from BLE stack.
     */
    static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
    {
        ret_code_t                    err_code;
        ble_nus_evt_t                 evt;
        ble_nus_client_context_t    * p_client;
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    
        err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage,
                                     p_ble_evt->evt.gatts_evt.conn_handle,
                                     (void *) &p_client);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                          p_ble_evt->evt.gatts_evt.conn_handle);
        }
    
        memset(&evt, 0, sizeof(ble_nus_evt_t));
        evt.p_nus       = p_nus;
        evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
        evt.p_link_ctx  = p_client;
    
        if ((p_evt_write->handle == p_nus->tx_handles.cccd_handle || p_evt_write->handle == p_nus->txs_handles.cccd_handle) &&
            (p_evt_write->len == 2))
        {
            if (p_client != NULL)
            {
                if(p_evt_write->handle == p_nus->tx_handles.cccd_handle) {
                  if (ble_srv_is_notification_enabled(p_evt_write->data))
                  {
                      p_client->is_notification_enabled = true;
                      evt.type                          = BLE_NUS_EVT_COMM_STARTED;
                  }
                  else
                  {
                      p_client->is_notification_enabled = false;
                      evt.type                          = BLE_NUS_EVT_COMM_STOPPED;
                  }
                } else {
                  if (ble_srv_is_notification_enabled(p_evt_write->data))
                  {
                      p_client->is_secure_notification_enabled = true;
                      evt.type                          = BLE_NUS_EVT_COMM_STARTED;
                  }
                  else
                  {
                      p_client->is_secure_notification_enabled = false;
                      evt.type                          = BLE_NUS_EVT_COMM_STOPPED;
                  }
                }
                
                if (p_nus->data_handler != NULL)
                {
                    p_nus->data_handler(&evt);
                }
    
            }
        }
        else if ((p_evt_write->handle == p_nus->rx_handles.value_handle || p_evt_write->handle == p_nus->rxs_handles.value_handle) &&
                 (p_nus->data_handler != NULL))
        {
            if(p_evt_write->handle == p_nus->rxs_handles.value_handle) 
            {
              evt.type                = BLE_NUS_EVT_RXS_DATA;
            } 
            else 
            {
              evt.type                = BLE_NUS_EVT_RX_DATA;
            }
            evt.params.rx_data.p_data = p_evt_write->data;
            evt.params.rx_data.length = p_evt_write->len;
    
            p_nus->data_handler(&evt);
        }
        else
        {
            // Do Nothing. This event is not relevant for this service.
        }
    }
    
    
    /**@brief Function for handling the @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event from the SoftDevice.
     *
     * @param[in] p_nus     Nordic UART Service structure.
     * @param[in] p_ble_evt Pointer to the event received from BLE stack.
     */
    static void on_hvx_tx_complete(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
    {
        ret_code_t                 err_code;
        ble_nus_evt_t              evt;
        ble_nus_client_context_t * p_client;
    
        err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage,
                                     p_ble_evt->evt.gatts_evt.conn_handle,
                                     (void *) &p_client);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                          p_ble_evt->evt.gatts_evt.conn_handle);
            return;
        }
    
        if (p_client->is_notification_enabled)
        {
            memset(&evt, 0, sizeof(ble_nus_evt_t));
            evt.type        = BLE_NUS_EVT_TX_RDY;
            evt.p_nus       = p_nus;
            evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
            evt.p_link_ctx  = p_client;
    
            p_nus->data_handler(&evt);
        } else if (p_client->is_secure_notification_enabled)
        {
            memset(&evt, 0, sizeof(ble_nus_evt_t));
            evt.type        = BLE_NUS_EVT_TXS_RDY;
            evt.p_nus       = p_nus;
            evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
            evt.p_link_ctx  = p_client;
    
            p_nus->data_handler(&evt);
        }
    }
    
    
    /**@brief Function for adding TX characteristic.
     *
     * @param[in] p_nus       Nordic UART Service structure.
     * @param[in] p_nus_init  Information needed to initialize the service.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static uint32_t tx_char_add(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
    {
        /**@snippet [Adding proprietary characteristic to the SoftDevice] */
        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));
    
        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.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;
    
        ble_uuid.type = p_nus->uuid_type;
        ble_uuid.uuid = BLE_UUID_NUS_TX_CHARACTERISTIC;
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    
        attr_md.vloc    = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth = 0;
        attr_md.wr_auth = 0;
        attr_md.vlen    = 1;
    
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        attr_char_value.init_len  = sizeof(uint8_t);
        attr_char_value.init_offs = 0;
        attr_char_value.max_len   = BLE_NUS_MAX_TX_CHAR_LEN;
    
        return sd_ble_gatts_characteristic_add(p_nus->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_nus->tx_handles);
        /**@snippet [Adding proprietary characteristic to the SoftDevice] */
    }
    
    /**@brief Function for adding TXS characteristic.
     *
     * @param[in] p_nus       Nordic UART Service structure.
     * @param[in] p_nus_init  Information needed to initialize the service.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static uint32_t txs_char_add(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
    {
        /**@snippet [Adding proprietary characteristic to the SoftDevice] */
        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));
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        cccd_md.write_perm = p_nus_init->nus_txs_attr_md.cccd_write_perm;
    
        cccd_md.vloc = BLE_GATTS_VLOC_STACK;
    
        memset(&char_md, 0, sizeof(char_md));
    
        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;
    
        ble_uuid.type = p_nus->uuid_type;
        ble_uuid.uuid = BLE_UUID_NUS_TXS_CHARACTERISTIC;
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_nus_init->nus_txs_attr_md.read_perm;
        attr_md.write_perm = p_nus_init->nus_txs_attr_md.write_perm;
    
        attr_md.vloc    = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth = 0;
        attr_md.wr_auth = 0;
        attr_md.vlen    = 1;
    
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        attr_char_value.init_len  = sizeof(uint8_t);
        attr_char_value.init_offs = 0;
        attr_char_value.max_len   = BLE_NUS_MAX_TX_CHAR_LEN;
    
        return sd_ble_gatts_characteristic_add(p_nus->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_nus->txs_handles);
        /**@snippet [Adding proprietary characteristic to the SoftDevice] */
    }
    
    /**@brief Function for adding RX characteristic.
     *
     * @param[in] p_nus       Nordic UART Service structure.
     * @param[in] p_nus_init  Information needed to initialize the service.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static uint32_t rx_char_add(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init)
    {
        ble_gatts_char_md_t char_md;
        ble_gatts_attr_t    attr_char_value;
        ble_uuid_t          ble_uuid;
        ble_gatts_attr_md_t attr_md;
    
        memset(&char_md, 0, sizeof(char_md));
    
        char_md.char_props.write         = 1;
        char_md.char_props.write_wo_resp = 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                = NULL;
        char_md.p_sccd_md                = NULL;
    
        ble_uuid.type = p_nus->uuid_type;
        ble_uuid.uuid = BLE_UUID_NUS_RX_CHARACTERISTIC;
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    
        attr_md.vloc    = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth = 0;
        attr_md.wr_auth = 0;
        attr_md.vlen    = 1;
    
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        attr_char_value.init_len  = 1;
        attr_char_value.init_offs = 0;
        attr_char_value.max_len   = BLE_NUS_MAX_RX_CHAR_LEN;
    
        return sd_ble_gatts_characteristic_add(p_nus->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_nus->rx_handles);
    }
    
    /**@brief Function for adding RXS characteristic.
     *
     * @param[in] p_nus       Nordic UART Service structure.
     * @param[in] p_nus_init  Information needed to initialize the service.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static uint32_t rxs_char_add(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init)
    {
        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));
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        cccd_md.write_perm = p_nus_init->nus_rxs_attr_md.cccd_write_perm;
        cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    
        memset(&char_md, 0, sizeof(char_md));
    
        char_md.char_props.write         = 1;
        char_md.char_props.write_wo_resp = 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;
    
        ble_uuid.type = p_nus->uuid_type;
        ble_uuid.uuid = BLE_UUID_NUS_RXS_CHARACTERISTIC;
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_nus_init->nus_rxs_attr_md.read_perm;
        attr_md.write_perm = p_nus_init->nus_rxs_attr_md.write_perm;
    
        attr_md.vloc    = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth = 0;
        attr_md.wr_auth = 0;
        attr_md.vlen    = 1;
    
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        attr_char_value.init_len  = 1;
        attr_char_value.init_offs = 0;
        attr_char_value.max_len   = BLE_NUS_MAX_RX_CHAR_LEN;
    
        return sd_ble_gatts_characteristic_add(p_nus->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_nus->rxs_handles);
    }
    
    void ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
    {
        if ((p_context == NULL) || (p_ble_evt == NULL))
        {
            return;
        }
    
        ble_nus_t * p_nus = (ble_nus_t *)p_context;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                on_connect(p_nus, p_ble_evt);
                break;
    
            case BLE_GATTS_EVT_WRITE:
                on_write(p_nus, p_ble_evt);
                break;
    
            case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                on_hvx_tx_complete(p_nus, p_ble_evt);
                break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
    {
        ret_code_t    err_code;
        ble_uuid_t    ble_uuid;
        ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;
    
        VERIFY_PARAM_NOT_NULL(p_nus);
        VERIFY_PARAM_NOT_NULL(p_nus_init);
    
        // Initialize the service structure.
        p_nus->data_handler = p_nus_init->data_handler;
    
        /**@snippet [Adding proprietary Service to the SoftDevice] */
        // Add a custom base UUID.
        err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_nus->uuid_type);
        VERIFY_SUCCESS(err_code);
    
        ble_uuid.type = p_nus->uuid_type;
        ble_uuid.uuid = BLE_UUID_NUS_SERVICE;
    
        // Add the service.
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                            &ble_uuid,
                                            &p_nus->service_handle);
        /**@snippet [Adding proprietary Service to the SoftDevice] */
        VERIFY_SUCCESS(err_code);
    
        // Add the RX Characteristic.
        err_code = rx_char_add(p_nus, p_nus_init);
        VERIFY_SUCCESS(err_code);
    
        // Add the TX Characteristic.
        err_code = tx_char_add(p_nus, p_nus_init);
        VERIFY_SUCCESS(err_code);
    
        // Add the RXS Characteristic.
        err_code = rxs_char_add(p_nus, p_nus_init);
        VERIFY_SUCCESS(err_code);
    
        // Add the TXS Characteristic.
        err_code = txs_char_add(p_nus, p_nus_init);
        VERIFY_SUCCESS(err_code);
    
        return NRF_SUCCESS;
    }
    
    
    uint32_t ble_nus_data_send(ble_nus_t * p_nus,
                               uint8_t   * p_data,
                               uint16_t  * p_length,
                               uint16_t    conn_handle)
    {
        ret_code_t                 err_code;
        ble_gatts_hvx_params_t     hvx_params;
        ble_nus_client_context_t * p_client;
    
        VERIFY_PARAM_NOT_NULL(p_nus);
    
        err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage, conn_handle, (void *) &p_client);
        VERIFY_SUCCESS(err_code);
    
        if ((conn_handle == BLE_CONN_HANDLE_INVALID) || (p_client == NULL))
        {
            return NRF_ERROR_NOT_FOUND;
        }
    
        if (!p_client->is_notification_enabled)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (*p_length > BLE_NUS_MAX_DATA_LEN)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        memset(&hvx_params, 0, sizeof(hvx_params));
    
        hvx_params.handle = p_nus->tx_handles.value_handle;
        hvx_params.p_data = p_data;
        hvx_params.p_len  = p_length;
        hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    
        return sd_ble_gatts_hvx(conn_handle, &hvx_params);
    }
    
    uint32_t ble_nus_data_send_secure(ble_nus_t * p_nus,
                               uint8_t   * p_data,
                               uint16_t  * p_length,
                               uint16_t    conn_handle)
    {
        ret_code_t                 err_code;
        ble_gatts_hvx_params_t     hvx_params;
        ble_nus_client_context_t * p_client;
    
        VERIFY_PARAM_NOT_NULL(p_nus);
    
        err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage, conn_handle, (void *) &p_client);
        VERIFY_SUCCESS(err_code);
    
        if ((conn_handle == BLE_CONN_HANDLE_INVALID) || (p_client == NULL))
        {
            return NRF_ERROR_NOT_FOUND;
        }
    
        if (!p_client->is_secure_notification_enabled)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (*p_length > BLE_NUS_MAX_DATA_LEN)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        memset(&hvx_params, 0, sizeof(hvx_params));
    
        hvx_params.handle = p_nus->txs_handles.value_handle;
        hvx_params.p_data = p_data;
        hvx_params.p_len  = p_length;
        hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    
        return sd_ble_gatts_hvx(conn_handle, &hvx_params);
    }
    
    #endif // NRF_MODULE_ENABLED(BLE_NUS)
    

  • /**
     * Copyright (c) 2012 - 2018, 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>
    
    #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"
    NRF_LOG_MODULE_REGISTER();
    
    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))
        {
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                switch (p_chars[i].characteristic.uuid.uuid)
                {
                    case BLE_UUID_NUS_RX_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rx_handle = p_chars[i].characteristic.handle_value;
                        break;
    
                    case BLE_UUID_NUS_TX_CHARACTERISTIC:
                        nus_c_evt.handles.nus_tx_handle = p_chars[i].characteristic.handle_value;
                        nus_c_evt.handles.nus_tx_cccd_handle = p_chars[i].cccd_handle;
                        break;
    
                    case BLE_UUID_NUS_RXS_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rxs_handle = p_chars[i].characteristic.handle_value;
                        break;
    
                    case BLE_UUID_NUS_TXS_CHARACTERISTIC:
                        nus_c_evt.handles.nus_txs_handle = p_chars[i].characteristic.handle_value;
                        nus_c_evt.handles.nus_txs_cccd_handle = p_chars[i].cccd_handle;
                        break;
    
                    default:
                        break;
                }
            }
            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 TX 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, ble_evt_t const * p_ble_evt)
    {
        // HVX can only occur from client sending.
        if (   (p_ble_nus_c->handles.nus_tx_handle != BLE_GATT_HANDLE_INVALID)
            && (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.nus_tx_handle || p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.nus_txs_handle)
            && (p_ble_nus_c->evt_handler != NULL))
        {
            ble_nus_c_evt_t ble_nus_c_evt;
            if(p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.nus_txs_handle) {
              ble_nus_c_evt.evt_type = BLE_NUS_C_EVT_NUS_TXS_EVT;
            } else {
              ble_nus_c_evt.evt_type = BLE_NUS_C_EVT_NUS_TX_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);
            NRF_LOG_DEBUG("Client sending data.");
        }
    }
    
    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_tx_handle = BLE_GATT_HANDLE_INVALID;
        p_ble_nus_c->handles.nus_rx_handle = BLE_GATT_HANDLE_INVALID;
        p_ble_nus_c->handles.nus_txs_handle = BLE_GATT_HANDLE_INVALID;
        p_ble_nus_c->handles.nus_rxs_handle = BLE_GATT_HANDLE_INVALID;
    
        return ble_db_discovery_evt_register(&uart_uuid);
    }
    
    void ble_nus_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ble_nus_c_t * p_ble_nus_c = (ble_nus_c_t *)p_context;
    
        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_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;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    /**@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;
    
        ble_gattc_write_params_t const 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);
    }
    
    
    uint32_t ble_nus_c_tx_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_tx_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_tx_cccd_handle, true);
    }
    
    uint32_t ble_nus_c_txs_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_txs_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_txs_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)
        {
            NRF_LOG_WARNING("Content too long.");
            return NRF_ERROR_INVALID_PARAM;
        }
        if (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            NRF_LOG_WARNING("Connection handle invalid.");
            return NRF_ERROR_INVALID_STATE;
        }
    
        ble_gattc_write_params_t const write_params =
        {
            .write_op = BLE_GATT_OP_WRITE_CMD,
            .flags    = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
            .handle   = p_ble_nus_c->handles.nus_rx_handle,
            .offset   = 0,
            .len      = length,
            .p_value  = p_string
        };
    
        return sd_ble_gattc_write(p_ble_nus_c->conn_handle, &write_params);
    }
    
    uint32_t ble_nus_c_string_send_secure(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)
        {
            NRF_LOG_WARNING("Content too long.");
            return NRF_ERROR_INVALID_PARAM;
        }
        if (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            NRF_LOG_WARNING("Connection handle invalid.");
            return NRF_ERROR_INVALID_STATE;
        }
    
        ble_gattc_write_params_t const write_params =
        {
            .write_op = BLE_GATT_OP_WRITE_CMD,
            .flags    = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
            .handle   = p_ble_nus_c->handles.nus_rxs_handle,
            .offset   = 0,
            .len      = length,
            .p_value  = p_string
        };
    
        return sd_ble_gattc_write(p_ble_nus_c->conn_handle, &write_params);
    }
    
    uint32_t ble_nus_c_handles_assign(ble_nus_c_t               * p_ble_nus,
                                      uint16_t                    conn_handle,
                                      ble_nus_c_handles_t const * 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_tx_cccd_handle = p_peer_handles->nus_tx_cccd_handle;
            p_ble_nus->handles.nus_tx_handle      = p_peer_handles->nus_tx_handle;
            p_ble_nus->handles.nus_rx_handle      = p_peer_handles->nus_rx_handle;
            p_ble_nus->handles.nus_txs_cccd_handle = p_peer_handles->nus_txs_cccd_handle;
            p_ble_nus->handles.nus_txs_handle      = p_peer_handles->nus_txs_handle;
            p_ble_nus->handles.nus_rxs_handle      = p_peer_handles->nus_rxs_handle;
        }
        return NRF_SUCCESS;
    }
    #endif // NRF_MODULE_ENABLED(BLE_NUS_C)
    

Related