Handling data two times from one of the peripherals

Hello,

I have been struggling with multilink uart example for a while. I have done 3 peripherals and one central communication but still I have some problems. I am using SDK 14.2.0 and 4 EYSHSNZWZ modules from Taiyo Yuden.

Central side sends data to peripherals that I send from computer via RS232 correctly, however when I send data from peripherals continuously, one of the peripherals sends the data two times. I think it is because of the central code, not the peripherals because I use same code for peripherals. I think that central shows the data two times. For example for handle 2, peripheral sends 5 bytes but central shows 10 bytes. For the other connection handles, central shows what is send from peripherals.

The main code for central is here:

/**
 * Copyright (c) 2014 - 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.
 * 
 */
/**
 * @brief BLE LED Button Service central and client application main file.
 *
 * This example can be a central for up to 8 peripherals.
 * The peripheral is called ble_app_blinky and can be found in the ble_peripheral
 * folder.
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nordic_common.h"
#include "app_error.h"
#include "app_uart.h"
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "app_timer.h"
#include "app_util.h"
#include "bsp_btn_ble.h"
#include "ble.h"
#include "ble_hci.h"
#include "ble_advdata.h"
#include "ble_advertising.h"
#include "ble_conn_params.h"
#include "ble_db_discovery.h"
#include "ble_conn_state.h"
#include "ble_nus_c.h"
#include "nrf_ble_gatt.h"
#include "nrf_delay.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"


#define APP_BLE_CONN_CFG_TAG      1                                     /**< A tag that refers to the BLE stack configuration we set with @ref sd_ble_cfg_set. Default tag is @ref APP_BLE_CONN_CFG_TAG. */
#define APP_BLE_OBSERVER_PRIO     3                                     /**< Application's BLE observer priority. You shouldn't need to modify this value. */

#define UART_TX_BUF_SIZE        256                                     /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE        256                                     /**< UART RX buffer size. */

#define CENTRAL_SCANNING_LED      BSP_BOARD_LED_0
#define CENTRAL_CONNECTED_LED     BSP_BOARD_LED_1
#define LEDBUTTON_LED             BSP_BOARD_LED_2                       /**< LED to indicate a change of state of the the Button characteristic on the peer. */

#define LEDBUTTON_BUTTON          BSP_BUTTON_0                          /**< Button that will write to the LED characteristic of the peer. */
#define BUTTON_DETECTION_DELAY    APP_TIMER_TICKS(50)                   /**< Delay from a GPIOTE event until a button is reported as pushed (in number of timer ticks). */

#define SCAN_INTERVAL             0x00A0                                /**< Determines scan interval in units of 0.625 millisecond. */
#define SCAN_WINDOW               0x0050                                /**< Determines scan window in units of 0.625 millisecond. */
#define SCAN_TIMEOUT              0x0000                                /**< Timout when scanning. 0x0000 disables timeout. */

#define MIN_CONNECTION_INTERVAL   MSEC_TO_UNITS(7.5, UNIT_1_25_MS)      /**< Determines minimum connection interval in milliseconds. */
#define MAX_CONNECTION_INTERVAL   MSEC_TO_UNITS(30, UNIT_1_25_MS)       /**< Determines maximum connection interval in milliseconds. */
#define SLAVE_LATENCY             0                                     /**< Determines slave latency in terms of connection events. */
#define SUPERVISION_TIMEOUT       MSEC_TO_UNITS(4000, UNIT_10_MS)       /**< Determines supervision time-out in units of 10 milliseconds. */

#define UUID16_SIZE             2                                       /**< Size of 16 bit UUID */
#define UUID32_SIZE             4                                       /**< Size of 32 bit UUID */
#define UUID128_SIZE            16                                      /**< Size of 128 bit UUID */

#define ECHOBACK_BLE_UART_DATA  1                                       /**< Echo the UART data that is received over the Nordic UART Service back to the sender. */

NRF_BLE_GATT_DEF(m_gatt);                                               /**< GATT module instance. */
BLE_NUS_C_ARRAY_DEF(m_ble_nus_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT);                                             /**< BLE NUS service client instance. */
BLE_DB_DISCOVERY_DEF(m_db_disc);  /**< Database discovery module instances. */

static char const m_target_periph_name[] = "Nordic_Blinky";             /**< Name of the device we try to connect to. This name is searched for in the scan report data*/
static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH; /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */


/**@brief Scan parameters requested for scanning and connection. */
static ble_gap_scan_params_t const m_scan_params =
{
    .active   = 0,
    .interval = SCAN_INTERVAL,
    .window   = SCAN_WINDOW,
    .timeout  = SCAN_TIMEOUT,
    #if (NRF_SD_BLE_API_VERSION <= 2)
        .selective   = 0,
        .p_whitelist = NULL,
    #endif
    #if (NRF_SD_BLE_API_VERSION >= 3)
        .use_whitelist  = 0,
        .adv_dir_report = 0,
    #endif
};

/**@brief Connection parameters requested for connection. */
static ble_gap_conn_params_t const m_connection_param =
{
    (uint16_t)MIN_CONNECTION_INTERVAL,
    (uint16_t)MAX_CONNECTION_INTERVAL,
    (uint16_t)SLAVE_LATENCY,
    (uint16_t)SUPERVISION_TIMEOUT
};


/**@brief Function to handle asserts in the SoftDevice.
 *
 * @details This function will be called in case of an assert in the SoftDevice.
 *
 * @warning This handler is an example only and does not fit a final product. You need to analyze
 *          how your product is supposed to react in case of Assert.
 * @warning On assert from the SoftDevice, the system can only recover on reset.
 *
 * @param[in] line_num     Line number of the failing ASSERT call.
 * @param[in] p_file_name  File name of the failing ASSERT call.
 */
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
    app_error_handler(0xDEADBEEF, line_num, p_file_name);
}


/**@brief Function for the LEDs initialization.
 *
 * @details Initializes all LEDs used by the application.
 */
static void leds_init(void)
{
    bsp_board_leds_init();
}


/**
 * @brief Parses advertisement data, providing length and location of the field in case
 *        matching data is found.
 *
 * @param[in]  type       Type of data to be looked for in advertisement data.
 * @param[in]  p_advdata  Advertisement report length and pointer to report.
 * @param[out] p_typedata If data type requested is found in the data report, type data length and
 *                        pointer to data will be populated here.
 *
 * @retval NRF_SUCCESS if the data type is found in the report.
 * @retval NRF_ERROR_NOT_FOUND if the data type could not be found.
 */
static uint32_t adv_report_parse(uint8_t type, uint8_array_t * p_advdata, uint8_array_t * p_typedata)
{
    uint32_t  index = 0;
    uint8_t * p_data;

    p_data = p_advdata->p_data;

    while (index < p_advdata->size)
    {
        uint8_t field_length = p_data[index];
        uint8_t field_type   = p_data[index + 1];

        if (field_type == type)
        {
            p_typedata->p_data = &p_data[index + 2];
            p_typedata->size   = field_length - 1;
            return NRF_SUCCESS;
        }
        index += field_length + 1;
    }
    return NRF_ERROR_NOT_FOUND;
}


/**@brief Function to start scanning. */
static void scan_start(void)
{
    ret_code_t ret;

    (void) sd_ble_gap_scan_stop();

    NRF_LOG_INFO("Start scanning for device name %s.", (uint32_t)m_target_periph_name);
    ret = sd_ble_gap_scan_start(&m_scan_params);
    APP_ERROR_CHECK(ret);

    ret = bsp_indication_set(BSP_INDICATE_SCANNING);
    APP_ERROR_CHECK(ret);
}



/**@brief Function for handling the advertising report BLE event.
 *
 * @param[in] p_ble_evt  Bluetooth stack event.
 */
static void on_adv_report(ble_evt_t const * p_ble_evt)
{
    uint32_t      err_code;
    uint8_array_t adv_data;
    uint8_array_t dev_name;
    bool          do_connect = false;

    // For readibility.
    ble_gap_evt_t  const * p_gap_evt  = &p_ble_evt->evt.gap_evt;
    ble_gap_addr_t const * peer_addr  = &p_gap_evt->params.adv_report.peer_addr;

    // Prepare advertisement report for parsing.
    adv_data.p_data = (uint8_t *)p_gap_evt->params.adv_report.data;
    adv_data.size   = p_gap_evt->params.adv_report.dlen;

    // Search for advertising names.
    bool found_name = false;
    err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
                                &adv_data,
                                &dev_name);
    if (err_code != NRF_SUCCESS)
    {
        // Look for the short local name if it was not found as complete.
        err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &dev_name);
        if (err_code != NRF_SUCCESS)
        {
            // If we can't parse the data, then exit.
            return;
        }
        else
        {
            found_name = true;
        }
    }
    else
    {
        found_name = true;
    }

    if (found_name)
    {
        if (strlen(m_target_periph_name) != 0)
        {
            if (memcmp(m_target_periph_name, dev_name.p_data, dev_name.size) == 0)
            {
                do_connect = true;
            }
        }
    }

    if (do_connect)
    {
        // Initiate connection.
        err_code = sd_ble_gap_connect(peer_addr, &m_scan_params, &m_connection_param, APP_BLE_CONN_CFG_TAG);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Connection Request Failed, reason %d", err_code);
        }
    }
}


/**@brief Function for handling BLE events.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 * @param[in]   p_context   Unused.
 */
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    ret_code_t err_code;

    // For readability.
    ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;

    switch (p_ble_evt->header.evt_id)
    {
        // Upon connection, check which peripheral has connected, initiate DB
        // discovery, update LEDs status and resume scanning if necessary.
        case BLE_GAP_EVT_CONNECTED:
        {
            NRF_LOG_INFO("Connection 0x%x established, starting DB discovery.",
                         p_gap_evt->conn_handle);

            APP_ERROR_CHECK_BOOL(p_gap_evt->conn_handle < NRF_SDH_BLE_CENTRAL_LINK_COUNT);

            //err_code = ble_nus_c_handles_assign(&m_ble_nus_c[p_gap_evt->conn_handle], p_gap_evt->conn_handle, NULL);
            err_code = ble_nus_c_handles_assign(&m_ble_nus_c[p_gap_evt->conn_handle], p_ble_evt->evt.gap_evt.conn_handle, NULL);
            
            NRF_LOG_INFO("m_ble_nus_c[0x%x]: conn_handle = 0x%x, rx = 0x%x, tx = 0x%x, cccd = 0x%x",
                p_gap_evt->conn_handle,
                m_ble_nus_c[p_gap_evt->conn_handle].conn_handle,
                m_ble_nus_c[p_gap_evt->conn_handle].handles.nus_rx_handle,
                m_ble_nus_c[p_gap_evt->conn_handle].handles.nus_tx_handle,
                m_ble_nus_c[p_gap_evt->conn_handle].handles.nus_tx_cccd_handle);
            
            
            APP_ERROR_CHECK(err_code);

            //err_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle);

          //err_code = ble_db_discovery_start(&m_db_disc, p_gap_evt->conn_handle);
          err_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle);

            if (err_code != NRF_ERROR_BUSY)
            {
                APP_ERROR_CHECK(err_code);
            }

            // Update LEDs status, and check if we should be looking for more
            // peripherals to connect to.
            bsp_board_led_on(CENTRAL_CONNECTED_LED);
            if (ble_conn_state_n_centrals() == NRF_SDH_BLE_CENTRAL_LINK_COUNT)
            {
                bsp_board_led_off(CENTRAL_SCANNING_LED);
            }
            else
            {
                // Resume scanning.
               bsp_board_led_on(CENTRAL_SCANNING_LED);
               scan_start();
            }
        } break; // BLE_GAP_EVT_CONNECTED

        // Upon disconnection, reset the connection handle of the peer which disconnected, update
        // the LEDs status and start scanning again.
        case BLE_GAP_EVT_DISCONNECTED:
        {
            NRF_LOG_INFO("Central link 0x%x disconnected (reason: 0x%x)",
                         p_gap_evt->conn_handle,
                         p_gap_evt->params.disconnected.reason);

            if (ble_conn_state_n_centrals() == 0)
            {
                //err_code = app_button_disable();
                //APP_ERROR_CHECK(err_code);

                // Turn off connection indication LED
                bsp_board_led_off(CENTRAL_CONNECTED_LED);
            }

            // Start scanning
            scan_start();

            // Turn on LED for indicating scanning
            bsp_board_led_on(CENTRAL_SCANNING_LED);

        } break;

        case BLE_GAP_EVT_ADV_REPORT:
            on_adv_report(p_ble_evt);
            break;

        case BLE_GAP_EVT_TIMEOUT:
        {
            // We have not specified a timeout for scanning, so only connection attemps can timeout.
            if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
            {
                NRF_LOG_DEBUG("Connection request timed out.");
            }
        } break;

        case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST.");
            // Accept parameters requested by peer.
            err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle,
                                        &p_gap_evt->params.conn_param_update_request.conn_params);
            APP_ERROR_CHECK(err_code);
        } break;

#ifndef S140
        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("PHY update request.");
            ble_gap_phys_t const phys =
            {
                .rx_phys = BLE_GAP_PHY_AUTO,
                .tx_phys = BLE_GAP_PHY_AUTO,
            };
            err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
            APP_ERROR_CHECK(err_code);
        } break;
#endif

        case BLE_GATTC_EVT_TIMEOUT:
        {
            // Disconnect on GATT Client timeout event.
            NRF_LOG_DEBUG("GATT Client Timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GATTS_EVT_TIMEOUT:
        {
            // Disconnect on GATT Server timeout event.
            NRF_LOG_DEBUG("GATT Server Timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
        } break;

        default:
            // No implementation needed.
            break;
    }
}



/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupts.
 */
static void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    // Register a handler for BLE events.
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}


/**@brief Function for handling database discovery events.
 *
 * @details This function is callback function to handle events from the database discovery module.
 *          Depending on the UUIDs that are discovered, this function should forward the events
 *          to their respective services.
 *
 * @param[in] p_event  Pointer to the database discovery event.
 */
static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
{
    NRF_LOG_DEBUG("call to ble_lbs_on_db_disc_evt for instance %d and link 0x%x!",
                  p_evt->conn_handle,
                  p_evt->conn_handle);

    ble_nus_c_on_db_disc_evt(&m_ble_nus_c[p_evt->conn_handle], p_evt);
}

/**@brief Function for handling characters received by the Nordic UART Service.
 *
 * @details This function takes a list of characters of length data_len and prints the characters out on UART.
 *          If @ref ECHOBACK_BLE_UART_DATA is set, the data is sent back to sender.
 */
static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
{
    ret_code_t ret_val;

    NRF_LOG_DEBUG("Receiving data.");
    NRF_LOG_HEXDUMP_DEBUG(p_data, data_len);

    for (uint32_t i = 0; i < data_len; i++)
    {
        do
        {
            ret_val = app_uart_put(p_data[i]);
            if ((ret_val != NRF_SUCCESS) && (ret_val != NRF_ERROR_BUSY))
            {
                NRF_LOG_ERROR("app_uart_put failed for index 0x%04x.", i);
                APP_ERROR_CHECK(ret_val);
            }
        } while (ret_val == NRF_ERROR_BUSY);
    }
    if (p_data[data_len-1] == '\r')
    {
        while (app_uart_put('\n') == NRF_ERROR_BUSY);
    }
   /* if (ECHOBACK_BLE_UART_DATA)
    {
        // Send data back to peripheral.

        do
        {   
        for(uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
        {
            ret_val = ble_nus_c_string_send(&m_ble_nus_c[i], p_data, data_len);
            if ((ret_val != NRF_SUCCESS) && (ret_val != NRF_ERROR_BUSY))
            {
                NRF_LOG_ERROR("Failed sending NUS message. Error 0x%x. ", ret_val);
                APP_ERROR_CHECK(ret_val);
            }
            }
        } while (ret_val == NRF_ERROR_BUSY);

    }
    */
}


/**@brief   Function for handling app_uart events.
 *
 * @details This function will receive a single character from the app_uart module and append it to
 *          a string. The string will be be sent over BLE when the last character received was a
 *          'new line' '\n' (hex 0x0A) or if the string has reached the maximum data length.
 */
void uart_event_handle(app_uart_evt_t * p_event)
{
    uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
    uint16_t index = 0;
    uint32_t ret_val = NRF_ERROR_INVALID_STATE;

    switch (p_event->evt_type)
    {
        /**@snippet [Handling data from UART] */
        case APP_UART_DATA_READY:
            UNUSED_VARIABLE(app_uart_get(&data_array[index]));
            index++;

            if ((data_array[index - 1] == '\n') || (index >= (m_ble_nus_max_data_len)))
            {
                //NRF_LOG_DEBUG("Ready to send data over BLE NUS");
               // NRF_LOG_INFO("Ready to send data over BLE NUS");
                NRF_LOG_HEXDUMP_DEBUG(data_array, index);

                for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++) {
                  if (m_ble_nus_c[i].conn_handle != BLE_CONN_HANDLE_INVALID && 
                    0 != m_ble_nus_c[i].handles.nus_tx_handle && 
                    0 != m_ble_nus_c[i].handles.nus_rx_handle )
                  {
                    ret_val = ble_nus_c_string_send(&m_ble_nus_c[i], data_array, index);

                    if ((ret_val != NRF_ERROR_INVALID_STATE) && (ret_val != NRF_ERROR_BUSY)) {
                      NRF_LOG_ERROR("CON HND id is %d, UUID %d, Tx %d, Rx %d, RETVAL %d",
                          m_ble_nus_c[i].conn_handle,
                          m_ble_nus_c[i].uuid_type,
                          m_ble_nus_c[i].handles.nus_tx_handle,
                          m_ble_nus_c[i].handles.nus_rx_handle,
                          ret_val);
                      //APP_ERROR_CHECK(ret_val);
                    }
                  }
                }

                index = 0;
            }
            break;

        /**@snippet [Handling data from UART] */
        case APP_UART_COMMUNICATION_ERROR:
            NRF_LOG_ERROR("Communication error occurred while handling UART.");
            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            NRF_LOG_ERROR("Error occurred in FIFO module used by UART.");
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        default:
            break;
    }
}


/**@brief Callback handling NUS Client events.
 *
 * @details This function is called to notify the application of NUS client events.
 *
 * @param[in]   p_ble_nus_c   NUS Client Handle. This identifies the NUS client
 * @param[in]   p_ble_nus_evt Pointer to the NUS Client event.
 */

/**@snippet [Handling events from the ble_nus_c module] */
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_ble_nus_evt)
{
    ret_code_t err_code;

    switch (p_ble_nus_evt->evt_type)
    {
        case BLE_NUS_C_EVT_DISCOVERY_COMPLETE:
            NRF_LOG_INFO("Discovery on handle %d complete.", p_ble_nus_evt->conn_handle);

            //  Burasi GAP event, discovery event degil. Burayý deðiþtirmek lazým

            err_code = ble_nus_c_handles_assign(&p_ble_nus_c[p_ble_nus_evt->conn_handle], p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);
            APP_ERROR_CHECK(err_code);

            NRF_LOG_INFO("m_ble_nus_c[0x%x]: conn_handle = 0x%x, rx = 0x%x, tx = 0x%x, cccd = 0x%x",
                                p_ble_nus_evt->conn_handle,
                                m_ble_nus_c[p_ble_nus_evt->conn_handle].conn_handle,
                                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_rx_handle,
                                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_handle,
                                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_cccd_handle);
            

            err_code = ble_nus_c_tx_notif_enable(&p_ble_nus_c[p_ble_nus_evt->conn_handle]);
            
            //err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);
            if (err_code != NRF_ERROR_BUSY)
             {
                 APP_ERROR_CHECK(err_code);                                         
             }
            NRF_LOG_INFO("Connected to device with Nordic UART Service on handle %d.", p_ble_nus_evt->conn_handle);

            break;
        case BLE_NUS_C_EVT_NUS_TX_EVT:
            NRF_LOG_INFO("Uartttan data alindi.");
            ble_nus_chars_received_uart_print(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);
            break;

        case BLE_NUS_C_EVT_DISCONNECTED:
          NRF_LOG_INFO("Disconnected.");
          
          if( NULL == p_ble_nus_evt ) {
            NRF_LOG_INFO( "ple_ble_nus_evt is null" );
          }
          /*
          else if( BLE_CONN_HANDLE_INVALID != p_ble_nus_evt->conn_handle ) {
            NRF_LOG_INFO("m_ble_nus_c[0x%x]: DISCONNECTED = 0x%x, rx = 0x%x, tx = 0x%x, cccd = 0x%x",
                p_ble_nus_evt->conn_handle,
                m_ble_nus_c[p_ble_nus_evt->conn_handle].conn_handle,
                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_rx_handle,
                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_handle,
                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_cccd_handle);
           }
           */
           else {
            NRF_LOG_INFO( "Event Handler connection pointer is null: conn_handle: %d", p_ble_nus_evt->conn_handle );
          }
          

          scan_start();
          break;
    }
}

/** @brief Database discovery initialization.
 */
static void db_discovery_init(void)
{
    ret_code_t err_code = ble_db_discovery_init(db_disc_handler);
    APP_ERROR_CHECK(err_code);
}


/** @brief Function to sleep until a BLE event is received by the application.
 */
static void power_manage(void)
{
    ret_code_t err_code = sd_app_evt_wait();
    APP_ERROR_CHECK(err_code);
}


/** @brief Function for initializing the log module.
 */
static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}


/** @brief Function for initializing the timer.
 */
static void timer_init(void)
{
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing the GATT module.
 */
static void gatt_init(void)
{
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
    APP_ERROR_CHECK(err_code);
        err_code = nrf_ble_gatt_att_mtu_central_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for initializing the UART. */
static void uart_init(void)
{
    ret_code_t err_code;

    app_uart_comm_params_t const comm_params =
    {
        .rx_pin_no    = RX_PIN_NUMBER,
        .tx_pin_no    = TX_PIN_NUMBER,
        .rts_pin_no   = RTS_PIN_NUMBER,
        .cts_pin_no   = CTS_PIN_NUMBER,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity   = false,
        .baud_rate    = UART_BAUDRATE_BAUDRATE_Baud115200
    };

    APP_UART_FIFO_INIT(&comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_event_handle,
                       APP_IRQ_PRIORITY_LOWEST,
                       err_code);

    APP_ERROR_CHECK(err_code);
}

/**@brief Function for initializing the NUS Client. */
static void nus_c_init(void) 
{
  ret_code_t err_code;
  ble_nus_c_init_t init;
  init.evt_handler = ble_nus_c_evt_handler;
  for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++) {
    err_code = ble_nus_c_init(&m_ble_nus_c[i], &init);
    APP_ERROR_CHECK(err_code);
  }

  /*
   ble_nus_c_init_t init[NRF_SDH_BLE_CENTRAL_LINK_COUNT];

  for(uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
  {

    init[i].evt_handler = ble_nus_c_evt_handler;
    
    err_code = ble_nus_c_init(&m_ble_nus_c[i], &init[i]);
    APP_ERROR_CHECK(err_code);
    }
    */
}

int main(void)
{
    log_init();
    timer_init();
    leds_init();
    uart_init();
    ble_stack_init();
    gatt_init();
    db_discovery_init();
    //lbs_c_init();
    nus_c_init();
    ble_conn_state_init();
    NRF_LOG_INFO("Multilink example started.");

    // Start scanning for peripherals and initiate connection to devices which  advertise.
    scan_start();

    // Turn on the LED to signal scanning.
    bsp_board_led_on(CENTRAL_SCANNING_LED);

    for (;;)
    {
        if (!NRF_LOG_PROCESS())
        {
            power_manage();
        }
    }
}
 

I get no errors, but I should solve this "two times" problem. There should be a point that I failed to notice. Please help me on this. Thank you in advance.

By the way, same thing happens while sending data from central to peripherals. I mean, data is sent to handle 2, two times. :) but other handles receive the data as is is sent.

  • Hello,

    I have seen a few cases regarding conn_handles and nus_c + multilink, so I looked into it. The issue is that the handles are not handled correctly. E.g. the notif_enable() function which is called in the DISCOVERY_COMPLETE event is different in lbs_c and nus_c.

     

    I have been trying to get it to work for a while, and finally it worked, but I can't say exactly which change I did that made it work.

     

    The only changes I did was in main.c and ble_nus_c.c, so I have attached these. I am sorry, because I have removed some logging and added some logging, but you can clean it up if you like.

     

    Try your project with the attached files, and let me know whether it works or not.

    main.c
    /**
     * Copyright (c) 2014 - 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.
     * 
     */
    /**
     * @brief BLE LED Button Service central and client application main file.
     *
     * This example can be a central for up to 8 peripherals.
     * The peripheral is called ble_app_blinky and can be found in the ble_peripheral
     * folder.
     */
    
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nordic_common.h"
    #include "app_error.h"
    #include "app_uart.h"
    #include "nrf_sdh.h"
    #include "nrf_sdh_ble.h"
    #include "app_timer.h"
    #include "app_util.h"
    #include "bsp_btn_ble.h"
    #include "ble.h"
    #include "ble_hci.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "ble_db_discovery.h"
    #include "ble_conn_state.h"
    #include "ble_nus_c.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_delay.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #define APP_BLE_CONN_CFG_TAG      1                                     /**< A tag that refers to the BLE stack configuration we set with @ref sd_ble_cfg_set. Default tag is @ref APP_BLE_CONN_CFG_TAG. */
    #define APP_BLE_OBSERVER_PRIO     3                                     /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    
    #define UART_TX_BUF_SIZE        256                                     /**< UART TX buffer size. */
    #define UART_RX_BUF_SIZE        256                                     /**< UART RX buffer size. */
    
    #define CENTRAL_SCANNING_LED      BSP_BOARD_LED_0
    #define CENTRAL_CONNECTED_LED     BSP_BOARD_LED_1
    #define LEDBUTTON_LED             BSP_BOARD_LED_2                       /**< LED to indicate a change of state of the the Button characteristic on the peer. */
    
    #define LEDBUTTON_BUTTON          BSP_BUTTON_0                          /**< Button that will write to the LED characteristic of the peer. */
    #define BUTTON_DETECTION_DELAY    APP_TIMER_TICKS(50)                   /**< Delay from a GPIOTE event until a button is reported as pushed (in number of timer ticks). */
    
    #define SCAN_INTERVAL             0x00A0                                /**< Determines scan interval in units of 0.625 millisecond. */
    #define SCAN_WINDOW               0x0050                                /**< Determines scan window in units of 0.625 millisecond. */
    #define SCAN_TIMEOUT              0x0000                                /**< Timout when scanning. 0x0000 disables timeout. */
    
    #define MIN_CONNECTION_INTERVAL   MSEC_TO_UNITS(7.5, UNIT_1_25_MS)      /**< Determines minimum connection interval in milliseconds. */
    #define MAX_CONNECTION_INTERVAL   MSEC_TO_UNITS(30, UNIT_1_25_MS)       /**< Determines maximum connection interval in milliseconds. */
    #define SLAVE_LATENCY             0                                     /**< Determines slave latency in terms of connection events. */
    #define SUPERVISION_TIMEOUT       MSEC_TO_UNITS(4000, UNIT_10_MS)       /**< Determines supervision time-out in units of 10 milliseconds. */
    
    #define UUID16_SIZE             2                                       /**< Size of 16 bit UUID */
    #define UUID32_SIZE             4                                       /**< Size of 32 bit UUID */
    #define UUID128_SIZE            16                                      /**< Size of 128 bit UUID */
    
    #define ECHOBACK_BLE_UART_DATA  1                                       /**< Echo the UART data that is received over the Nordic UART Service back to the sender. */
    
    NRF_BLE_GATT_DEF(m_gatt);                                               /**< GATT module instance. */
    BLE_NUS_C_ARRAY_DEF(m_ble_nus_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT);                                             /**< BLE NUS service client instance. */
    //BLE_DB_DISCOVERY_DEF(m_db_disc);  /**< Database discovery module instances. */
    
    BLE_DB_DISCOVERY_ARRAY_DEF(m_db_disc, NRF_SDH_BLE_CENTRAL_LINK_COUNT);   /**< Database discovery module instances. */
    
    static char const m_target_periph_name[] = "Nordic_Blinky";             /**< Name of the device we try to connect to. This name is searched for in the scan report data*/
    static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH; /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
    
    volatile uint8_t m_counter = 0;
    
    
    /**@brief Scan parameters requested for scanning and connection. */
    static ble_gap_scan_params_t const m_scan_params =
    {
        .active   = 0,
        .interval = SCAN_INTERVAL,
        .window   = SCAN_WINDOW,
        .timeout  = SCAN_TIMEOUT,
        #if (NRF_SD_BLE_API_VERSION <= 2)
            .selective   = 0,
            .p_whitelist = NULL,
        #endif
        #if (NRF_SD_BLE_API_VERSION >= 3)
            .use_whitelist  = 0,
            .adv_dir_report = 0,
        #endif
    };
    
    /**@brief Connection parameters requested for connection. */
    static ble_gap_conn_params_t const m_connection_param =
    {
        (uint16_t)MIN_CONNECTION_INTERVAL,
        (uint16_t)MAX_CONNECTION_INTERVAL,
        (uint16_t)SLAVE_LATENCY,
        (uint16_t)SUPERVISION_TIMEOUT
    };
    
    
    /**@brief Function to handle asserts in the SoftDevice.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyze
     *          how your product is supposed to react in case of Assert.
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num     Line number of the failing ASSERT call.
     * @param[in] p_file_name  File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
    {
        app_error_handler(0xDEADBEEF, line_num, p_file_name);
    }
    
    
    /**@brief Function for the LEDs initialization.
     *
     * @details Initializes all LEDs used by the application.
     */
    static void leds_init(void)
    {
        bsp_board_leds_init();
    }
    
    
    /**
     * @brief Parses advertisement data, providing length and location of the field in case
     *        matching data is found.
     *
     * @param[in]  type       Type of data to be looked for in advertisement data.
     * @param[in]  p_advdata  Advertisement report length and pointer to report.
     * @param[out] p_typedata If data type requested is found in the data report, type data length and
     *                        pointer to data will be populated here.
     *
     * @retval NRF_SUCCESS if the data type is found in the report.
     * @retval NRF_ERROR_NOT_FOUND if the data type could not be found.
     */
    static uint32_t adv_report_parse(uint8_t type, uint8_array_t * p_advdata, uint8_array_t * p_typedata)
    {
        uint32_t  index = 0;
        uint8_t * p_data;
    
        p_data = p_advdata->p_data;
    
        while (index < p_advdata->size)
        {
            uint8_t field_length = p_data[index];
            uint8_t field_type   = p_data[index + 1];
    
            if (field_type == type)
            {
                p_typedata->p_data = &p_data[index + 2];
                p_typedata->size   = field_length - 1;
                return NRF_SUCCESS;
            }
            index += field_length + 1;
        }
        return NRF_ERROR_NOT_FOUND;
    }
    
    
    /**@brief Function to start scanning. */
    static void scan_start(void)
    {
        ret_code_t ret;
    
        (void) sd_ble_gap_scan_stop();
    
        NRF_LOG_INFO("Start scanning for device name %s.", (uint32_t)m_target_periph_name);
        ret = sd_ble_gap_scan_start(&m_scan_params);
        APP_ERROR_CHECK(ret);
    
        ret = bsp_indication_set(BSP_INDICATE_SCANNING);
        APP_ERROR_CHECK(ret);
    }
    
    
    
    /**@brief Function for handling the advertising report BLE event.
     *
     * @param[in] p_ble_evt  Bluetooth stack event.
     */
    static void on_adv_report(ble_evt_t const * p_ble_evt)
    {
        uint32_t      err_code;
        uint8_array_t adv_data;
        uint8_array_t dev_name;
        bool          do_connect = false;
    
        // For readibility.
        ble_gap_evt_t  const * p_gap_evt  = &p_ble_evt->evt.gap_evt;
        ble_gap_addr_t const * peer_addr  = &p_gap_evt->params.adv_report.peer_addr;
    
        // Prepare advertisement report for parsing.
        adv_data.p_data = (uint8_t *)p_gap_evt->params.adv_report.data;
        adv_data.size   = p_gap_evt->params.adv_report.dlen;
    
        // Search for advertising names.
        bool found_name = false;
        err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
                                    &adv_data,
                                    &dev_name);
        if (err_code != NRF_SUCCESS)
        {
            // Look for the short local name if it was not found as complete.
            err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &dev_name);
            if (err_code != NRF_SUCCESS)
            {
                // If we can't parse the data, then exit.
                return;
            }
            else
            {
                found_name = true;
            }
        }
        else
        {
            found_name = true;
        }
    
        if (found_name)
        {
            if (strlen(m_target_periph_name) != 0)
            {
                if (memcmp(m_target_periph_name, dev_name.p_data, dev_name.size) == 0)
                {
                    do_connect = true;
                }
            }
        }
    
        if (do_connect)
        {
            // Initiate connection.
            err_code = sd_ble_gap_connect(peer_addr, &m_scan_params, &m_connection_param, APP_BLE_CONN_CFG_TAG);
            if (err_code != NRF_SUCCESS)
            {
                NRF_LOG_ERROR("Connection Request Failed, reason %d", err_code);
            }
        }
    }
    
    
    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t err_code;
    
        // For readability.
        ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
    
        switch (p_ble_evt->header.evt_id)
        {
            // Upon connection, check which peripheral has connected, initiate DB
            // discovery, update LEDs status and resume scanning if necessary.
            case BLE_GAP_EVT_CONNECTED:
            {
                NRF_LOG_INFO("Connection 0x%x established, starting DB discovery.",
                             p_gap_evt->conn_handle);
    
                APP_ERROR_CHECK_BOOL(p_gap_evt->conn_handle < NRF_SDH_BLE_CENTRAL_LINK_COUNT);
    
                err_code = ble_nus_c_handles_assign(&m_ble_nus_c[p_gap_evt->conn_handle], p_gap_evt->conn_handle, NULL);
                //err_code = ble_lbs_c_handles_assign(&m_lbs_c    [p_gap_evt->conn_handle], p_gap_evt->conn_handle, NULL);
                
                APP_ERROR_CHECK(err_code);
    
              //err_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle);
    
              err_code = ble_db_discovery_start(&m_db_disc[p_gap_evt->conn_handle], p_gap_evt->conn_handle);
              //err_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle);
    
                if (err_code != NRF_ERROR_BUSY)
                {
                    APP_ERROR_CHECK(err_code);
                }
    
                // Update LEDs status, and check if we should be looking for more
                // peripherals to connect to.
                bsp_board_led_on(CENTRAL_CONNECTED_LED);
                if (ble_conn_state_n_centrals() == NRF_SDH_BLE_CENTRAL_LINK_COUNT)
                {
                    bsp_board_led_off(CENTRAL_SCANNING_LED);
                }
                else
                {
                    // Resume scanning.
                   bsp_board_led_on(CENTRAL_SCANNING_LED);
                   scan_start();
                }
            } break; // BLE_GAP_EVT_CONNECTED
    
            // Upon disconnection, reset the connection handle of the peer which disconnected, update
            // the LEDs status and start scanning again.
            case BLE_GAP_EVT_DISCONNECTED:
            {
                NRF_LOG_INFO("Central link 0x%x disconnected (reason: 0x%x)",
                             p_gap_evt->conn_handle,
                             p_gap_evt->params.disconnected.reason);
    
                if (ble_conn_state_n_centrals() == 0)
                {
                    //err_code = app_button_disable();
                    //APP_ERROR_CHECK(err_code);
    
                    // Turn off connection indication LED
                    bsp_board_led_off(CENTRAL_CONNECTED_LED);
                }
    
                // Start scanning
                scan_start();
    
                // Turn on LED for indicating scanning
                bsp_board_led_on(CENTRAL_SCANNING_LED);
    
            } break;
    
            case BLE_GAP_EVT_ADV_REPORT:
                on_adv_report(p_ble_evt);
                break;
    
            case BLE_GAP_EVT_TIMEOUT:
            {
                // We have not specified a timeout for scanning, so only connection attemps can timeout.
                if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
                {
                    NRF_LOG_DEBUG("Connection request timed out.");
                }
            } break;
    
            case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST.");
                // Accept parameters requested by peer.
                err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle,
                                            &p_gap_evt->params.conn_param_update_request.conn_params);
                APP_ERROR_CHECK(err_code);
            } break;
    
    #ifndef S140
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    #endif
    
            case BLE_GATTC_EVT_TIMEOUT:
            {
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("GATT Client Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GATTS_EVT_TIMEOUT:
            {
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("GATT Server Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
            } break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    
    /**@brief Function for initializing the BLE stack.
     *
     * @details Initializes the SoftDevice and the BLE event interrupts.
     */
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Register a handler for BLE events.
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
    }
    
    
    /**@brief Function for handling database discovery events.
     *
     * @details This function is callback function to handle events from the database discovery module.
     *          Depending on the UUIDs that are discovered, this function should forward the events
     *          to their respective services.
     *
     * @param[in] p_event  Pointer to the database discovery event.
     */
    void db_disc_handler(ble_db_discovery_evt_t * p_evt)
    {
        NRF_LOG_DEBUG("call to ble_lbs_on_db_disc_evt for instance %d and link 0x%x!",
                      p_evt->conn_handle,
                      p_evt->conn_handle);
    
        ble_nus_c_on_db_disc_evt(&m_ble_nus_c[p_evt->conn_handle], p_evt);
    }
    
    /**@brief Function for handling characters received by the Nordic UART Service.
     *
     * @details This function takes a list of characters of length data_len and prints the characters out on UART.
     *          If @ref ECHOBACK_BLE_UART_DATA is set, the data is sent back to sender.
     */
    static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
    {
        ret_code_t ret_val;
    
        NRF_LOG_DEBUG("Receiving data.");
        NRF_LOG_HEXDUMP_DEBUG(p_data, data_len);
    
        for (uint32_t i = 0; i < data_len; i++)
        {
            do
            {
                ret_val = app_uart_put(p_data[i]);
                if ((ret_val != NRF_SUCCESS) && (ret_val != NRF_ERROR_BUSY))
                {
                    NRF_LOG_ERROR("app_uart_put failed for index 0x%04x.", i);
                    APP_ERROR_CHECK(ret_val);
                }
            } while (ret_val == NRF_ERROR_BUSY);
        }
        if (p_data[data_len-1] == '\r')
        {
            while (app_uart_put('\n') == NRF_ERROR_BUSY);
        }
       /*if (ECHOBACK_BLE_UART_DATA)
        {
            // Send data back to peripheral.
    
            do
            {   
            for(uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
            {
                ret_val = ble_nus_c_string_send(&m_ble_nus_c[i], p_data, data_len);
                if ((ret_val != NRF_SUCCESS) && (ret_val != NRF_ERROR_BUSY))
                {
                    NRF_LOG_ERROR("Failed sending NUS message. Error 0x%x. ", ret_val);
                    APP_ERROR_CHECK(ret_val);
                }
                }
            } while (ret_val == NRF_ERROR_BUSY);
    
        }
        */
    }
    
    
    /**@brief   Function for handling app_uart events.
     *
     * @details This function will receive a single character from the app_uart module and append it to
     *          a string. The string will be be sent over BLE when the last character received was a
     *          'new line' '\n' (hex 0x0A) or if the string has reached the maximum data length.
     */
    void uart_event_handle(app_uart_evt_t * p_event)
    {
        uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
        uint16_t index = 0;
        uint32_t ret_val = NRF_ERROR_INVALID_STATE;
    
        switch (p_event->evt_type)
        {
            /**@snippet [Handling data from UART] */
            case APP_UART_DATA_READY:
                UNUSED_VARIABLE(app_uart_get(&data_array[index]));
                index++;
    
                if ((data_array[index - 1] == '\n') || (index >= (m_ble_nus_max_data_len)))
                {
                    //NRF_LOG_DEBUG("Ready to send data over BLE NUS");
                   // NRF_LOG_INFO("Ready to send data over BLE NUS");
                    NRF_LOG_HEXDUMP_DEBUG(data_array, index);
    
                    for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++) {
                      if (m_ble_nus_c[i].conn_handle != BLE_CONN_HANDLE_INVALID && 
                        0 != m_ble_nus_c[i].handles.nus_tx_handle && 
                        0 != m_ble_nus_c[i].handles.nus_rx_handle )
                      {
                        ret_val = ble_nus_c_string_send(&m_ble_nus_c[i], data_array, index);
    
                        if ((ret_val != NRF_ERROR_INVALID_STATE) && (ret_val != NRF_ERROR_BUSY)) {
                          NRF_LOG_ERROR("CON HND id is %d, UUID %d, Tx %d, Rx %d, RETVAL %d",
                              m_ble_nus_c[i].conn_handle,
                              m_ble_nus_c[i].uuid_type,
                              m_ble_nus_c[i].handles.nus_tx_handle,
                              m_ble_nus_c[i].handles.nus_rx_handle,
                              ret_val);
                          //APP_ERROR_CHECK(ret_val);
                        }
                      }
                    }
    
                    index = 0;
                }
                break;
    
            /**@snippet [Handling data from UART] */
            case APP_UART_COMMUNICATION_ERROR:
                NRF_LOG_ERROR("Communication error occurred while handling UART.");
                APP_ERROR_HANDLER(p_event->data.error_communication);
                break;
    
            case APP_UART_FIFO_ERROR:
                NRF_LOG_ERROR("Error occurred in FIFO module used by UART.");
                APP_ERROR_HANDLER(p_event->data.error_code);
                break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Callback handling NUS Client events.
     *
     * @details This function is called to notify the application of NUS client events.
     *
     * @param[in]   p_ble_nus_c   NUS Client Handle. This identifies the NUS client
     * @param[in]   p_ble_nus_evt Pointer to the NUS Client event.
     */
    
    /**@snippet [Handling events from the ble_nus_c module] */
    void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_ble_nus_evt)
    {
        ret_code_t err_code;
    
        switch (p_ble_nus_evt->evt_type)
        {
            case BLE_NUS_C_EVT_DISCOVERY_COMPLETE:
                NRF_LOG_INFO("Discovery on handle %d complete.", p_ble_nus_evt->conn_handle);
    
                //  Burasi GAP event, discovery event degil. Burayy de?i?tirmek lazym
    
                //err_code = ble_nus_c_handles_assign(&p_ble_nus_c[p_ble_nus_evt->conn_handle], p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);
                //APP_ERROR_CHECK(err_code);           
    
                //err_code = ble_nus_c_tx_notif_enable(&p_ble_nus_c[p_ble_nus_evt->conn_handle]);
                err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);
                m_counter++;
                
                //err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);
                if (err_code != NRF_ERROR_BUSY)
                 {
                     APP_ERROR_CHECK(err_code);                                         
                 }
                 //else
                 {
                    NRF_LOG_INFO("notif_enable err_code = 0x%x", err_code);
                 }
    
                NRF_LOG_INFO("Connected to device with Nordic UART Service on handle %d.", p_ble_nus_evt->conn_handle);
                NRF_LOG_INFO("m_c = %d", m_counter);
    
    //            NRF_LOG_INFO("m_ble_nus_c[0x%x]: conn_handle = 0x%x, rx = 0x%x, tx = 0x%x, cccd = 0x%x",
    //                                p_ble_nus_evt->conn_handle,
    //                                m_ble_nus_c[p_ble_nus_evt->conn_handle].conn_handle,
    //                                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_rx_handle,
    //                                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_handle,
    //                                m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_cccd_handle);
                NRF_LOG_INTERNAL_FLUSH();
    
                break; //BLE_NUS_C_EVT_DISCOVERY_COMPLETE
            case BLE_NUS_C_EVT_NUS_TX_EVT:
                NRF_LOG_INFO("TX EVT conn_handle = 0x%x, rx = 0x%x, tx = 0x%x, cccd = 0x%x, data len: %d",
                                    p_ble_nus_evt->conn_handle,
                                    p_ble_nus_evt->handles.nus_rx_handle,
                                    p_ble_nus_evt->handles.nus_tx_handle,
                                    p_ble_nus_evt->handles.nus_tx_cccd_handle, 
                                    p_ble_nus_evt->data_len );
    
                ble_nus_chars_received_uart_print(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);
                break;
    
            case BLE_NUS_C_EVT_DISCONNECTED:
              NRF_LOG_INFO("Disconnected.");
              
              if( NULL == p_ble_nus_evt ) {
                NRF_LOG_INFO( "ple_ble_nus_evt is null" );
              }
              /*
              else if( BLE_CONN_HANDLE_INVALID != p_ble_nus_evt->conn_handle ) {
                NRF_LOG_INFO("m_ble_nus_c[0x%x]: DISCONNECTED = 0x%x, rx = 0x%x, tx = 0x%x, cccd = 0x%x",
                    p_ble_nus_evt->conn_handle,
                    m_ble_nus_c[p_ble_nus_evt->conn_handle].conn_handle,
                    m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_rx_handle,
                    m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_handle,
                    m_ble_nus_c[p_ble_nus_evt->conn_handle].handles.nus_tx_cccd_handle);
               }
               */
               else {
                NRF_LOG_INFO( "Event Handler connection pointer is null: conn_handle: %d", p_ble_nus_evt->conn_handle );
              }
              
    
              scan_start();
              break;
        }
    }
    
    /** @brief Database discovery initialization.
     */
    static void db_discovery_init(void)
    {
        ret_code_t err_code = ble_db_discovery_init(db_disc_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /** @brief Function to sleep until a BLE event is received by the application.
     */
    static void power_manage(void)
    {
        ret_code_t err_code = sd_app_evt_wait();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /** @brief Function for initializing the log module.
     */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    
    /** @brief Function for initializing the timer.
     */
    static void timer_init(void)
    {
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling events from the GATT library. */
    void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
    {
        if (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED)
        {
            NRF_LOG_INFO("ATT MTU exchange completed.");
    
            m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
            NRF_LOG_INFO("Ble NUS max data length set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);
        }
    }
    
    /**@brief Function for initializing the GATT module.
     */
    static void gatt_init(void)
    {
        //ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
        ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
        APP_ERROR_CHECK(err_code);
    
    
        err_code = nrf_ble_gatt_att_mtu_central_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for initializing the UART. */
    static void uart_init(void)
    {
        ret_code_t err_code;
    
        app_uart_comm_params_t const comm_params =
        {
            .rx_pin_no    = RX_PIN_NUMBER,
            .tx_pin_no    = TX_PIN_NUMBER,
            .rts_pin_no   = RTS_PIN_NUMBER,
            .cts_pin_no   = CTS_PIN_NUMBER,
            .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
            .use_parity   = false,
            .baud_rate    = UART_BAUDRATE_BAUDRATE_Baud115200
        };
    
        APP_UART_FIFO_INIT(&comm_params,
                           UART_RX_BUF_SIZE,
                           UART_TX_BUF_SIZE,
                           uart_event_handle,
                           APP_IRQ_PRIORITY_LOWEST,
                           err_code);
    
        APP_ERROR_CHECK(err_code);
    }
    
    ble_nus_c_init_t init[NRF_SDH_BLE_CENTRAL_LINK_COUNT];
    
    /**@brief Function for initializing the NUS Client. */
    static void nus_c_init(void) 
    {
        ret_code_t err_code;
    
        ble_nus_c_init_t nus_c_init_obj;
    
        nus_c_init_obj.evt_handler = ble_nus_c_evt_handler;
    
        for(uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
        {
            err_code = ble_nus_c_init(&m_ble_nus_c[i], &nus_c_init_obj);
            APP_ERROR_CHECK(err_code);
        }    
    }
    
    int main(void)
    {
        log_init();
        timer_init();
        leds_init();
        uart_init();
        ble_stack_init();
        gatt_init();
        db_discovery_init();
        //lbs_c_init();
        nus_c_init();
        ble_conn_state_init();
        NRF_LOG_INFO("Multilink example started.");
    
        // Start scanning for peripherals and initiate connection to devices which  advertise.
        scan_start();
    
        // Turn on the LED to signal scanning.
        bsp_board_led_on(CENTRAL_SCANNING_LED);
    
        for (;;)
        {
            if (!NRF_LOG_PROCESS())
            {
                power_manage();
            }
        }
    }
    ble_nus_c.c
    /**
     * 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>
    
    #include "ble.h"
    #include "ble_nus_c.h"
    #include "ble_gattc.h"
    #include "ble_srv_common.h"
    #include "app_error.h"
    #include "nrf_delay.h"
    
    #define NRF_LOG_MODULE_NAME ble_nus
    #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)
    {
    
        // 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))
        {
    
            ble_nus_c_evt_t nus_c_evt;
    
            nus_c_evt.evt_type = BLE_NUS_C_EVT_DISCOVERY_COMPLETE;
            nus_c_evt.conn_handle = p_evt->conn_handle;
    
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
            const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
                switch (p_char->characteristic.uuid.uuid)
                {
                    case BLE_UUID_NUS_RX_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rx_handle = p_char->characteristic.handle_value;
                        NRF_LOG_INFO("discovered RX_CHAR on conn_handle %x", p_evt->conn_handle);
                        break;
    
                    case BLE_UUID_NUS_TX_CHARACTERISTIC:
                        nus_c_evt.handles.nus_tx_handle = p_char->characteristic.handle_value;
                        nus_c_evt.handles.nus_tx_cccd_handle = p_char->cccd_handle;
                        NRF_LOG_INFO("discovered TX_CHAR on conn_handle %x", p_evt->conn_handle);
                        break;
    
                    default:
                        break;
                }
            }
            if (p_ble_nus_c->conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                if ((p_ble_nus_c->handles.nus_rx_handle      == BLE_GATT_HANDLE_INVALID) &&
                    (p_ble_nus_c->handles.nus_tx_handle      == BLE_GATT_HANDLE_INVALID) &&
                    (p_ble_nus_c->handles.nus_tx_cccd_handle == BLE_GATT_HANDLE_INVALID))
                    {
                        p_ble_nus_c->handles = nus_c_evt.handles;
                    }
            }
    
            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_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_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.");
        }
    
    
    }
    */
    
    
    //devzone g´┐Żncellemesi ile:
    static void on_hvx(ble_nus_c_t * p_ble_nus_c, ble_evt_t const * p_ble_evt)
    {
    
          // Check if the event is from the link for this instance
          if (p_ble_nus_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
          {
              return;
          }
    
          // Check if this is a TX notification.
          if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.nus_tx_handle)
          {
              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_nus_c->evt_handler != NULL))
                 {
                    ble_nus_c_evt_t ble_nus_c_evt;
    
                    ble_nus_c_evt.conn_handle = p_ble_nus_c->conn_handle;
                    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.");
                 }
          }
    
    //    // 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_nus_c->evt_handler != NULL))
    //    {
    //        ble_nus_c_evt_t ble_nus_c_evt;
    //
    //        ble_nus_c_evt.conn_handle = p_ble_nus_c->conn_handle;
    //        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.");
    //    }
    }
    
    
    /*
    //devzone g´┐Żncellemesi + bizim tx ve rx eklemelerimizle:
    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_nus_c->evt_handler != NULL))
        {
            ble_nus_c_evt_t ble_nus_c_evt;
    
            ble_nus_c_evt.conn_handle = p_ble_nus_c->conn_handle;
            ble_nus_c_evt.handles.nus_tx_handle = p_ble_nus_c->handles.nus_tx_handle;
            ble_nus_c_evt.handles.nus_rx_handle = p_ble_nus_c->handles.nus_rx_handle;
            ble_nus_c_evt.handles.nus_tx_cccd_handle = p_ble_nus_c->handles.nus_tx_cccd_handle;
    
            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;
    
        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)
    {
    
        //NRF_LOG_INFO("Cccd confi.");
        
        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
        };
        //nrf_delay_ms(100);
        
        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);
    //    NRF_LOG_WARNING("tx notif enable.");
        NRF_LOG_INFO("notif: conn_handle %d", p_ble_nus_c->conn_handle);
        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_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_handles_assign(ble_nus_c_t               * p_ble_nus_c,
                                      uint16_t                    conn_handle,
                                      ble_nus_c_handles_t const * p_peer_handles)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
    
        p_ble_nus_c->conn_handle = conn_handle;
        NRF_LOG_INFO("assign conn_handle %d", p_ble_nus_c->conn_handle);
        if (p_peer_handles != NULL)
        {
              p_ble_nus_c->handles = *p_peer_handles;
    //        p_ble_nus_c->handles.nus_tx_cccd_handle = p_peer_handles->nus_tx_cccd_handle;
    //        p_ble_nus_c->handles.nus_tx_handle      = p_peer_handles->nus_tx_handle;
    //        p_ble_nus_c->handles.nus_rx_handle      = p_peer_handles->nus_rx_handle;
        }
        return NRF_SUCCESS;
    }
    #endif // NRF_MODULE_ENABLED(BLE_NUS_C)
    

    Best regards,

    Edvin