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

Problems while discovering services on a multilink central

Hi,

I developped a multilink central based on the example code ble_app_multilink_central.

I want to connect to several peripherals with two different custom services as shown in the following schematic.

The connection work but I have problems while discovering the services.

Sometimes the central discover all the services but often some services are not discovered and I don't understand why.

The discovery handler executes many times for each connection and the event type is often BLE_DB_DISCOVERY_ERROR.

Here is my discovery handler :

Here is an example of what can happen during a connection with 2 RFID and 1 Relay :

Here we can see that we have the relay on conn_handle 0x0 and the RFIDs on conn_handle 0x1 and 0x2.

We can see that the RFID service is not discovered on the conn_handle 0x1.

My questions are :

-When does the discovery handler execute for each connection?

-How to obtain the event BLE_DB_DISCOVERY_COMPLETE?

-Why does the discovery handler execute several time on each connection? And why is it not executed again while the service is not discovered?

Parents
  • If I remember correctly there is a limitation with the discovery module. so you should only do service discovery on one peer at the time. Can you try to add a check so you do not start service discovery on the new device if the process is already ongoing?

  • Do you have any other suggestions to improve my service discovery please run_ar?

  • Hi, thank you for your answer.

    I don't know if this info can be useful but for my hardware I use 2 nRF52840DK and 2 BMD-340-EVAL.

    My code : 

    I use now the db_discovery library from SDK v15.3.

    I did the following tests with one central and 2 RFID peripheral and 1 RELAY peripheral.

    With my code I get the issue 1 time for 2 try.

     

    This is an example of when it works :

    The discovery works and then I get the event “RFID_C_EVT_DISCOVERY_COMPLETE” and start scanning again.

    This is an example of when it doesn’t work :

    According to the DEBUG logs, it seems that the discovery is successful. But I don’t get the event “RELAY_C_EVT_DISCOVERY_COMPLETE”.

    I can’t reproduce the issue when I use only one peripheral.

    Example code (SDK 15.3) :

    For the following test I use 1 ble_app_hrs_rscs_relay, 1 ble_app_hrs and 1 ble_app_rscs.

    I had to try at least 15 time to reproduce the issue.

     

    Example of only the HRS service has been discovered :

    Here we have the connection handle 0x0 and 0x1 but only one service is discovered.

    I can’t reproduce the issue when I use only one peripheral.

  • Hi Jules, 

    I believe the issue of the ble_app_hrs_rscs_relay can be solved by starting scan after the service has been discovered (like in your code) it must be related to the issue when 2 service discovery was performed at the same time. I attached the main.c that i modified to do what similar to your code. Could you have a quick test to see if you have the same issue ?

     

    /**
     * Copyright (c) 2014 - 2019, 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 Heart Rate and Running Speed relay application main file.
     *
     * @detail This application demonstrates a simple relay, which passes on the values it receives.
     * By combining a collector part on one end and a sensor part on the other,
     * the s130 can function simultaneously as a central and a peripheral device.
     * 
     * The following figure shows how the sensor ble_app_hrs connects and interacts with the relay
     * in the same manner it would connect to a Heart Rate Collector. In this case, the relay
     * application acts as a central.
     *
     * On the other side, a collector (such as Master Control Panel or ble_app_hrs_c) connects
     * and interacts with the relay in the same manner it would connect to a heart rate sensor peripheral.
     *
     * LED layout:
     * LED 1: Central side is scanning.       LED 2: Central side is connected to a peripheral.
     * LED 3: Peripheral side is advertising. LED 4: Peripheral side is connected to a central.
     *
     * @note While testing, make sure that the Sensor and Collector are actually connecting to the relay,
     *       and not directly to each other!
     *
     *    Peripheral                  Relay                    Central
     *    +--------+        +-----------|----------+        +-----------+
     *    | Heart  |        | Heart     |   Heart  |        |           |
     *    | Rate   | -----> | Rate     -|-> Rate   | -----> | Collector |
     *    | Sensor |        | Collector |   Sensor |        |           |
     *    +--------+        +-----------|   and    |        +-----------+
     *                      | Running   |   Running|
     *    +--------+        | Speed    -|-> Speed  |
     *    | Running|------> | Collector |   Sensor |
     *    | Speed  |        +-----------|----------+
     *    | Sensor |
     *    +--------+
     */
    
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nordic_common.h"
    #include "nrf_sdh.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_sdh_ble.h"
    #include "peer_manager.h"
    #include "peer_manager_handler.h"
    #include "app_timer.h"
    #include "bsp_btn_ble.h"
    #include "ble.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "ble_db_discovery.h"
    #include "ble_hrs.h"
    #include "ble_rscs.h"
    #include "ble_hrs_c.h"
    #include "ble_rscs_c.h"
    #include "ble_conn_state.h"
    #include "nrf_fstorage.h"
    #include "fds.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_qwr.h"
    #include "nrf_pwr_mgmt.h"
    #include "nrf_ble_scan.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #define PERIPHERAL_ADVERTISING_LED      BSP_BOARD_LED_2
    #define PERIPHERAL_CONNECTED_LED        BSP_BOARD_LED_3
    #define CENTRAL_SCANNING_LED            BSP_BOARD_LED_0
    #define CENTRAL_CONNECTED_LED           BSP_BOARD_LED_1
    
    #define DEVICE_NAME                     "nRF Relay"                                 /**< Name of device used for advertising. */
    #define MANUFACTURER_NAME               "NordicSemiconductor"                       /**< Manufacturer. Passed to Device Information Service. */
    #define APP_ADV_INTERVAL                300                                         /**< The advertising interval (in units of 0.625 ms). This value corresponds to 187.5 ms. */
    
    #define APP_ADV_DURATION                18000                                       /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    
    #define APP_BLE_CONN_CFG_TAG            1                                           /**< Tag that identifies the SoftDevice BLE configuration. */
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                       /**< Time from initiating event (connect or start of notification) to the first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                      /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3                                           /**< Number of attempts before giving up the connection parameter negotiation. */
    
    #define SEC_PARAM_BOND                  1                                           /**< Perform bonding. */
    #define SEC_PARAM_MITM                  0                                           /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC                  0                                           /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS              0                                           /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                        /**< No I/O capabilities. */
    #define SEC_PARAM_OOB                   0                                           /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE          7                                           /**< Minimum encryption key size in octets. */
    #define SEC_PARAM_MAX_KEY_SIZE          16                                          /**< Maximum encryption key size in octets. */
    
    #define HART_RATE_SERVICE_UUID_IDX      0                                           /**< Hart Rate service UUID index in array. */
    #define RSCS_SERVICE_UUID_IDX           1                                           /**< RSCS service UUID index in array. */
    
    /**@brief   Priority of the application BLE event handler.
     * @note    You shouldn't need to modify this value.
     */
    #define APP_BLE_OBSERVER_PRIO           3
    
    
    static ble_hrs_t m_hrs;                                             /**< Heart Rate Service instance. */
    static ble_rscs_t m_rscs;                                           /**< Running Speed and Cadence Service instance. */
    static ble_hrs_c_t m_hrs_c;                                         /**< Heart Rate Service client instance. */
    static ble_rscs_c_t m_rscs_c;                                       /**< Running Speed and Cadence Service client instance. */
    
    NRF_BLE_GATT_DEF(m_gatt);                                           /**< GATT module instance. */
    NRF_BLE_QWRS_DEF(m_qwr, NRF_SDH_BLE_TOTAL_LINK_COUNT);              /**< Context for the Queued Write module.*/
    BLE_ADVERTISING_DEF(m_advertising);                                 /**< Advertising module instance. */
    BLE_DB_DISCOVERY_ARRAY_DEF(m_db_discovery, 2);                      /**< Database discovery module instances. */
    NRF_BLE_SCAN_DEF(m_scan);                                           /**< Scanning module instance. */
    
    static uint16_t m_conn_handle_hrs_c  = BLE_CONN_HANDLE_INVALID;     /**< Connection handle for the HRS central application */
    static uint16_t m_conn_handle_rscs_c = BLE_CONN_HANDLE_INVALID;     /**< Connection handle for the RSC central application */
    
    /**@brief Names that the central application scans for, and that are advertised by the peripherals.
     *  If these are set to empty strings, the UUIDs defined below are used.
     */
    static char const m_target_periph_name[] = "";
    
    /**@brief UUIDs that the central application scans for if the name above is set to an empty string,
     * and that are to be advertised by the peripherals.
     */
    static ble_uuid_t m_adv_uuids[] =
    {
        {BLE_UUID_HEART_RATE_SERVICE,        BLE_UUID_TYPE_BLE},
        {BLE_UUID_RUNNING_SPEED_AND_CADENCE, BLE_UUID_TYPE_BLE}
    };
    
    
    static ble_gap_scan_params_t m_scan_param =                 /**< Scan parameters requested for scanning and connection. */
    {
        .active        = 0x01,
        .interval      = NRF_BLE_SCAN_SCAN_INTERVAL,
        .window        = NRF_BLE_SCAN_SCAN_WINDOW,
        .filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL,
        .timeout       = NRF_BLE_SCAN_SCAN_DURATION,
        .scan_phys     = BLE_GAP_PHY_1MBPS,
        .extended      = true,
    };
    
    
    /**@brief Function for handling asserts in the SoftDevice.
     *
     * @details This function is called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and is not meant for the 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 handling errors from the Connection Parameters module.
     *
     * @param[in] nrf_error  Error code containing information about what went wrong.
     */
    static void conn_params_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    static void scan_evt_handler(scan_evt_t const * p_scan_evt)
    {
        ret_code_t err_code;
        ble_gap_evt_adv_report_t const * p_adv = 
                       p_scan_evt->params.filter_match.p_adv_report;
        ble_gap_scan_params_t    const * p_scan_param = 
                       p_scan_evt->p_scan_params;
    
        switch(p_scan_evt->scan_evt_id)
        {
            case NRF_BLE_SCAN_EVT_FILTER_MATCH:
            {
                // Initiate connection.
                err_code = sd_ble_gap_connect(&p_adv->peer_addr,
                                              p_scan_param,
                                              &m_scan.conn_params,
                                              APP_BLE_CONN_CFG_TAG);
                APP_ERROR_CHECK(err_code);
            } break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for initialization the scanning and setting the filters.
     */
    static void scan_init(void)
    {
        ret_code_t          err_code;
        nrf_ble_scan_init_t init_scan;
    
        memset(&init_scan, 0, sizeof(init_scan));
    
        init_scan.p_scan_param = &m_scan_param;
    
        err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
        APP_ERROR_CHECK(err_code);
    
        if (strlen(m_target_periph_name) != 0)
        {
            err_code = nrf_ble_scan_filter_set(&m_scan, 
                                               SCAN_NAME_FILTER, 
                                               m_target_periph_name);
            APP_ERROR_CHECK(err_code);
        }
    
        err_code = nrf_ble_scan_filter_set(&m_scan, 
                                           SCAN_UUID_FILTER, 
                                           &m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_ble_scan_filter_set(&m_scan, 
                                           SCAN_UUID_FILTER, 
                                           &m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_ble_scan_filters_enable(&m_scan, 
                                               NRF_BLE_SCAN_ALL_FILTER, 
                                               false);
        APP_ERROR_CHECK(err_code);
    
    }
    
    
    /**@brief Function for initializing the scanning.
     */
    static void scan_start(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_ble_scan_start(&m_scan);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the advertising and the scanning.
     */
    static void adv_scan_start(void)
    {
        ret_code_t err_code;
    
        //check if there are no flash operations in progress
        if (!nrf_fstorage_is_busy(NULL))
        {
            // Start scanning for peripherals and initiate connection to devices which
            // advertise Heart Rate or Running speed and cadence UUIDs.
            scan_start();
    
            // Turn on the LED to signal scanning.
            bsp_board_led_on(CENTRAL_SCANNING_LED);
    
            // Start advertising.
            err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            APP_ERROR_CHECK(err_code);
        }
    }
    
    
    /**@brief Function for handling Peer Manager events.
     *
     * @param[in] p_evt  Peer Manager event.
     */
    static void pm_evt_handler(pm_evt_t const * p_evt)
    {
        pm_handler_on_pm_evt(p_evt);
        pm_handler_disconnect_on_sec_failure(p_evt);
        pm_handler_flash_clean(p_evt);
    
        switch (p_evt->evt_id)
        {
            case PM_EVT_PEERS_DELETE_SUCCEEDED:
                adv_scan_start();
                break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for changing filter settings after establishing the connection.
     */
    static void filter_settings_change(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_ble_scan_all_filter_remove(&m_scan);
        APP_ERROR_CHECK(err_code);
    
        if (strlen(m_target_periph_name) != 0)
        {
            err_code = nrf_ble_scan_filter_set(&m_scan, 
                                               SCAN_NAME_FILTER, 
                                               m_target_periph_name);
            APP_ERROR_CHECK(err_code);
        }
    
        if ((m_conn_handle_hrs_c != BLE_CONN_HANDLE_INVALID) && 
            (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID))
        {
            err_code = nrf_ble_scan_filter_set(&m_scan, 
                                               SCAN_UUID_FILTER, 
                                               &m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
        }
    
        if ((m_conn_handle_rscs_c != BLE_CONN_HANDLE_INVALID) && 
             m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID)
        {
            err_code = nrf_ble_scan_filter_set(&m_scan, 
                                               SCAN_UUID_FILTER, 
                                               &m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
        }
    
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Handles events coming from the Heart Rate central module.
     */
    static void hrs_c_evt_handler(ble_hrs_c_t * p_hrs_c, ble_hrs_c_evt_t * p_hrs_c_evt)
    {
        switch (p_hrs_c_evt->evt_type)
        {
            case BLE_HRS_C_EVT_DISCOVERY_COMPLETE:
            {
                if (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID)
                {
                    ret_code_t err_code;
    
                    m_conn_handle_hrs_c = p_hrs_c_evt->conn_handle;
                    NRF_LOG_INFO("HRS discovered on conn_handle 0x%x", m_conn_handle_hrs_c);
    
                    filter_settings_change();
    
                    err_code = ble_hrs_c_handles_assign(p_hrs_c,
                                                        m_conn_handle_hrs_c,
                                                        &p_hrs_c_evt->params.peer_db);
                    APP_ERROR_CHECK(err_code);
                    // Initiate bonding.
                    err_code = pm_conn_secure(m_conn_handle_hrs_c, false);
                    if (err_code != NRF_ERROR_BUSY)
                    {
                        APP_ERROR_CHECK(err_code);
                    }
    
                    // Heart rate service discovered. Enable notification of Heart Rate Measurement.
                    err_code = ble_hrs_c_hrm_notif_enable(p_hrs_c);
                    APP_ERROR_CHECK(err_code);
                      bsp_board_led_on(CENTRAL_CONNECTED_LED);
                if (ble_conn_state_central_conn_count() == 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_HRS_C_EVT_DISCOVERY_COMPLETE
    
            case BLE_HRS_C_EVT_HRM_NOTIFICATION:
            {
                ret_code_t err_code;
    
                NRF_LOG_INFO("Heart Rate = %d", p_hrs_c_evt->params.hrm.hr_value);
    
                err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, p_hrs_c_evt->params.hrm.hr_value);
                if ((err_code != NRF_SUCCESS) &&
                    (err_code != NRF_ERROR_INVALID_STATE) &&
                    (err_code != NRF_ERROR_RESOURCES) &&
                    (err_code != NRF_ERROR_BUSY) &&
                    (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                    )
                {
                    APP_ERROR_HANDLER(err_code);
                }
            } break; // BLE_HRS_C_EVT_HRM_NOTIFICATION
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    /**@brief Handles events coming from  Running Speed and Cadence central module.
     */
    static void rscs_c_evt_handler(ble_rscs_c_t * p_rscs_c, ble_rscs_c_evt_t * p_rscs_c_evt)
    {
        switch (p_rscs_c_evt->evt_type)
        {
            case BLE_RSCS_C_EVT_DISCOVERY_COMPLETE:
            {
                if (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID)
                {
                    ret_code_t err_code;
    
                    m_conn_handle_rscs_c = p_rscs_c_evt->conn_handle;
                    NRF_LOG_INFO("Running Speed and Cadence service discovered on conn_handle 0x%x",
                                 m_conn_handle_rscs_c);
    
                    filter_settings_change();
    
                    err_code = ble_rscs_c_handles_assign(p_rscs_c,
                                                        m_conn_handle_rscs_c,
                                                        &p_rscs_c_evt->params.rscs_db);
                    APP_ERROR_CHECK(err_code);
    
                    // Initiate bonding.
                    err_code = pm_conn_secure(m_conn_handle_rscs_c, false);
                    if (err_code != NRF_ERROR_BUSY)
                    {
                        APP_ERROR_CHECK(err_code);
                    }
    
                    // Running Speed Cadence Service discovered. Enable notifications.
                    err_code = ble_rscs_c_rsc_notif_enable(p_rscs_c);
                    APP_ERROR_CHECK(err_code);
                      bsp_board_led_on(CENTRAL_CONNECTED_LED);
                if (ble_conn_state_central_conn_count() == 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_RSCS_C_EVT_DISCOVERY_COMPLETE:
    
            case BLE_RSCS_C_EVT_RSC_NOTIFICATION:
            {
                ret_code_t      err_code;
                ble_rscs_meas_t rscs_measurment;
    
                NRF_LOG_INFO("Speed      = %d", p_rscs_c_evt->params.rsc.inst_speed);
    
                rscs_measurment.is_running                  = p_rscs_c_evt->params.rsc.is_running;
                rscs_measurment.is_inst_stride_len_present  = p_rscs_c_evt->params.rsc.is_inst_stride_len_present;
                rscs_measurment.is_total_distance_present   = p_rscs_c_evt->params.rsc.is_total_distance_present;
    
                rscs_measurment.inst_stride_length = p_rscs_c_evt->params.rsc.inst_stride_length;
                rscs_measurment.inst_cadence       = p_rscs_c_evt->params.rsc.inst_cadence;
                rscs_measurment.inst_speed         = p_rscs_c_evt->params.rsc.inst_speed;
                rscs_measurment.total_distance     = p_rscs_c_evt->params.rsc.total_distance;
    
                err_code = ble_rscs_measurement_send(&m_rscs, &rscs_measurment);
                if ((err_code != NRF_SUCCESS) &&
                    (err_code != NRF_ERROR_INVALID_STATE) &&
                    (err_code != NRF_ERROR_RESOURCES) &&
                    (err_code != NRF_ERROR_BUSY) &&
                    (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                    )
                {
                    APP_ERROR_HANDLER(err_code);
                }
            } break; // BLE_RSCS_C_EVT_RSC_NOTIFICATION
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    /**@brief Function for assigning new connection handle to available instance of QWR module.
     *
     * @param[in] conn_handle New connection handle.
     */
    static void multi_qwr_conn_handle_assign(uint16_t conn_handle)
    {
        for (uint32_t i = 0; i < NRF_SDH_BLE_TOTAL_LINK_COUNT; i++)
        {
            if (m_qwr[i].conn_handle == BLE_CONN_HANDLE_INVALID)
            {
                ret_code_t err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr[i], conn_handle);
                APP_ERROR_CHECK(err_code);
                break;
            }
        }
    }
    
    
    /**@brief   Function for handling BLE events from the central application.
     *
     * @details This function parses scanning reports and initiates a connection to peripherals when a
     *          target UUID is found. It updates the status of LEDs used to report the central application
     *          activity.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     */
    static void on_ble_central_evt(ble_evt_t const * p_ble_evt)
    {
        ret_code_t            err_code;
        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 is connected (HR or RSC), initiate DB
            // discovery, update LEDs status, and resume scanning, if necessary.
            case BLE_GAP_EVT_CONNECTED:
            {
                NRF_LOG_INFO("Central connected");
                // If no Heart Rate sensor or RSC sensor is currently connected, try to find them on this peripheral.
                if (   (m_conn_handle_hrs_c  == BLE_CONN_HANDLE_INVALID)
                    || (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID))
                {
                    NRF_LOG_INFO("Attempt to find HRS or RSC on conn_handle 0x%x", p_gap_evt->conn_handle);
    
                    err_code = ble_db_discovery_start(&m_db_discovery[0], p_gap_evt->conn_handle);
                    if (err_code == NRF_ERROR_BUSY)
                    {
                        err_code = ble_db_discovery_start(&m_db_discovery[1], p_gap_evt->conn_handle);
                        APP_ERROR_CHECK(err_code);
                    }
                    else
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                }
    
                // Assign connection handle to the QWR module.
                multi_qwr_conn_handle_assign(p_gap_evt->conn_handle);
    
                // Update LEDs status, and check whether to look for more peripherals to connect to.
              
            } break; // BLE_GAP_EVT_CONNECTED
    
            // Upon disconnection, reset the connection handle of the peer that disconnected,
            // update the LEDs status and start scanning again.
            case BLE_GAP_EVT_DISCONNECTED:
            {
                if (p_gap_evt->conn_handle == m_conn_handle_hrs_c)
                {
                    NRF_LOG_INFO("HRS central disconnected (reason: %d)",
                                 p_gap_evt->params.disconnected.reason);
    
                    m_conn_handle_hrs_c = BLE_CONN_HANDLE_INVALID;
                    
                    err_code = nrf_ble_scan_filter_set(&m_scan, 
                                                       SCAN_UUID_FILTER, 
                                                       &m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
                    APP_ERROR_CHECK(err_code);
                }
                if (p_gap_evt->conn_handle == m_conn_handle_rscs_c)
                {
                    NRF_LOG_INFO("RSC central disconnected (reason: %d)",
                                 p_gap_evt->params.disconnected.reason);
    
                    m_conn_handle_rscs_c = BLE_CONN_HANDLE_INVALID;
    
                    err_code = nrf_ble_scan_filter_set(&m_scan, 
                                                       SCAN_UUID_FILTER, 
                                                       &m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
                    APP_ERROR_CHECK(err_code);
                }
    
                if (   (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID)
                    || (m_conn_handle_hrs_c  == BLE_CONN_HANDLE_INVALID))
                {
                    // Start scanning.
                    scan_start();
    
                    // Update LEDs status.
                    bsp_board_led_on(CENTRAL_SCANNING_LED);
                }
    
                if (ble_conn_state_central_conn_count() == 0)
                {
                    bsp_board_led_off(CENTRAL_CONNECTED_LED);
                }
            } break; // BLE_GAP_EVT_DISCONNECTED
    
            case BLE_GAP_EVT_TIMEOUT:
            {
                // No timeout for scanning is specified, so only connection attemps can timeout.
                if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
                {
                    NRF_LOG_INFO("Connection Request timed out.");
                }
            } break;
    
            case 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;
    
            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;
    
            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 handling BLE events from peripheral applications.
     * @details Updates the status LEDs used to report the activity of the peripheral applications.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     */
    static void on_ble_peripheral_evt(ble_evt_t const * p_ble_evt)
    {
        ret_code_t            err_code;
        ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Peripheral connected");
                bsp_board_led_off(PERIPHERAL_ADVERTISING_LED);
                bsp_board_led_on(PERIPHERAL_CONNECTED_LED);
    
                // Assign connection handle to the QWR module.
                multi_qwr_conn_handle_assign(p_ble_evt->evt.gap_evt.conn_handle);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Peripheral disconnected. conn_handle: 0x%x, reason: 0x%x",
                             p_gap_evt->conn_handle,
                             p_gap_evt->params.disconnected.reason);
    
                bsp_board_led_off(PERIPHERAL_CONNECTED_LED);
                break;
    
            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;
    
            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 handling advertising events.
     *
     * @param[in] ble_adv_evt  Advertising event.
     */
    static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
    {
        switch (ble_adv_evt)
        {
            case BLE_ADV_EVT_FAST:
            {
                NRF_LOG_INFO("Fast advertising.");
                bsp_board_led_on(PERIPHERAL_ADVERTISING_LED);
            } break;
    
            case BLE_ADV_EVT_IDLE:
            {
                ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
                APP_ERROR_CHECK(err_code);
            } break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    /**@brief Function for checking whether a bluetooth stack event is an advertising timeout.
     *
     * @param[in] p_ble_evt Bluetooth stack event.
     */
    static bool ble_evt_is_advertising_timeout(ble_evt_t const * p_ble_evt)
    {
        return (p_ble_evt->header.evt_id == BLE_GAP_EVT_ADV_SET_TERMINATED);
    }
    
    
    /**@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)
    {
        uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
        uint16_t role        = ble_conn_state_role(conn_handle);
    
        // Based on the role this device plays in the connection, dispatch to the right handler.
        if (role == BLE_GAP_ROLE_PERIPH || ble_evt_is_advertising_timeout(p_ble_evt))
        {
            ble_hrs_on_ble_evt(p_ble_evt, &m_hrs);
            ble_rscs_on_ble_evt(p_ble_evt, &m_rscs);
            on_ble_peripheral_evt(p_ble_evt);
        }
        else if ((role == BLE_GAP_ROLE_CENTRAL) || (p_ble_evt->header.evt_id == BLE_GAP_EVT_ADV_REPORT))
        {
            ble_hrs_c_on_ble_evt(p_ble_evt, &m_hrs_c);
            ble_rscs_c_on_ble_evt(p_ble_evt, &m_rscs_c);
            on_ble_central_evt(p_ble_evt);
        }
    }
    
    
    /**@brief Heart Rate Collector initialization.
     */
    static void hrs_c_init(void)
    {
        ret_code_t       err_code;
        ble_hrs_c_init_t hrs_c_init_obj;
    
        hrs_c_init_obj.evt_handler = hrs_c_evt_handler;
    
        err_code = ble_hrs_c_init(&m_hrs_c, &hrs_c_init_obj);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief RSC collector initialization.
     */
    static void rscs_c_init(void)
    {
        ret_code_t        err_code;
        ble_rscs_c_init_t rscs_c_init_obj;
    
        rscs_c_init_obj.evt_handler = rscs_c_evt_handler;
    
        err_code = ble_rscs_c_init(&m_rscs_c, &rscs_c_init_obj);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@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 initializing the Peer Manager.
     */
    static void peer_manager_init(void)
    {
        ble_gap_sec_params_t sec_param;
        ret_code_t err_code;
    
        err_code = pm_init();
        APP_ERROR_CHECK(err_code);
    
        memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
    
        // Security parameters to be used for all security procedures.
        sec_param.bond           = SEC_PARAM_BOND;
        sec_param.mitm           = SEC_PARAM_MITM;
        sec_param.lesc           = SEC_PARAM_LESC;
        sec_param.keypress       = SEC_PARAM_KEYPRESS;
        sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
        sec_param.oob            = SEC_PARAM_OOB;
        sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
        sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
        sec_param.kdist_own.enc  = 1;
        sec_param.kdist_own.id   = 1;
        sec_param.kdist_peer.enc = 1;
        sec_param.kdist_peer.id  = 1;
    
        err_code = pm_sec_params_set(&sec_param);
        APP_ERROR_CHECK(err_code);
    
        err_code = pm_register(pm_evt_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Clear bond information from persistent storage.
     */
    static void delete_bonds(void)
    {
        ret_code_t err_code;
    
        NRF_LOG_INFO("Erase bonds!");
    
        err_code = pm_peers_delete();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing buttons and LEDs.
     *
     * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to
     *                            wake the application up.
     */
    static void buttons_leds_init(bool * p_erase_bonds)
    {
        ret_code_t err_code;
        bsp_event_t startup_event;
    
        err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, NULL);
        APP_ERROR_CHECK(err_code);
    
        err_code = bsp_btn_ble_init(NULL, &startup_event);
        APP_ERROR_CHECK(err_code);
    
        *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
    }
    
    
    /**@brief Function for initializing the GAP.
     *
     * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
     *          device, including the device name, appearance, and the preferred connection parameters.
     */
    static void gap_params_init(void)
    {
        ret_code_t              err_code;
        ble_gap_conn_params_t   gap_conn_params;
        ble_gap_conn_sec_mode_t sec_mode;
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
        err_code = sd_ble_gap_device_name_set(&sec_mode,
                                              (const uint8_t *)DEVICE_NAME,
                                              strlen(DEVICE_NAME));
        APP_ERROR_CHECK(err_code);
    
        memset(&gap_conn_params, 0, sizeof(gap_conn_params));
    
        gap_conn_params.min_conn_interval = m_scan.conn_params.min_conn_interval;
        gap_conn_params.max_conn_interval = m_scan.conn_params.max_conn_interval;
        gap_conn_params.slave_latency     = m_scan.conn_params.slave_latency;
        gap_conn_params.conn_sup_timeout  = m_scan.conn_params.conn_sup_timeout;
    
        err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
        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);
    }
    
    
    /**@brief Function for initializing the Connection Parameters module.
     */
    static void conn_params_init(void)
    {
        ret_code_t             err_code;
        ble_conn_params_init_t cp_init;
    
        memset(&cp_init, 0, sizeof(cp_init));
    
        cp_init.p_conn_params                  = NULL;
        cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
        cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
        cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
        cp_init.start_on_notify_cccd_handle    = BLE_CONN_HANDLE_INVALID; // Start upon connection.
        cp_init.disconnect_on_fail             = true;
        cp_init.evt_handler                    = NULL;  // Ignore events.
        cp_init.error_handler                  = conn_params_error_handler;
    
        err_code = ble_conn_params_init(&cp_init);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling database discovery events.
     *
     * @details This function is a callback function to handle events from the database discovery module.
     *          Depending on the UUIDs that are discovered, this function forwards 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)
    {
        ble_hrs_on_db_disc_evt(&m_hrs_c, p_evt);
        ble_rscs_on_db_disc_evt(&m_rscs_c, p_evt);
    }
    
    
    /**
     * @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 for handling Queued Write module errors.
     *
     * @details A pointer to this function is passed to each service that may need to inform the
     *          application about an error.
     *
     * @param[in]   nrf_error   Error code that contains information about what went wrong.
     */
    static void nrf_qwr_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    /**@brief Function for initializing services that are be used by the application.
     *
     * @details Initialize the Heart Rate, Battery and Device Information services.
     */
    static void services_init(void)
    {
        ret_code_t         err_code;
        ble_hrs_init_t     hrs_init;
        ble_rscs_init_t    rscs_init;
        nrf_ble_qwr_init_t qwr_init = {0};
        uint8_t            body_sensor_location;
    
        // Initialize Queued Write module instances.
        qwr_init.error_handler = nrf_qwr_error_handler;
    
        for (uint32_t i = 0; i < NRF_SDH_BLE_TOTAL_LINK_COUNT; i++)
        {
            err_code = nrf_ble_qwr_init(&m_qwr[i], &qwr_init);
            APP_ERROR_CHECK(err_code);
        }
    
        // Initialize the Heart Rate Service.
        body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER;
    
        memset(&hrs_init, 0, sizeof(hrs_init));
    
        hrs_init.evt_handler                 = NULL;
        hrs_init.is_sensor_contact_supported = true;
        hrs_init.p_body_sensor_location      = &body_sensor_location;
    
        // Here the sec level for the Heart Rate Service can be changed or increased.
        hrs_init.hrm_cccd_wr_sec = SEC_OPEN;
        hrs_init.bsl_rd_sec      = SEC_OPEN;
    
        err_code = ble_hrs_init(&m_hrs, &hrs_init);
        APP_ERROR_CHECK(err_code);
    
        // Initialize the Running Speed and Cadence Service.
        memset(&rscs_init, 0, sizeof(rscs_init));
    
        rscs_init.evt_handler = NULL;
        rscs_init.feature     = BLE_RSCS_FEATURE_INSTANT_STRIDE_LEN_BIT |
                                BLE_RSCS_FEATURE_WALKING_OR_RUNNING_STATUS_BIT;
    
        // Here the sec level for the Running Speed and Cadence Service can be changed or increased.
        rscs_init.rsc_feature_rd_sec   = SEC_OPEN;
        rscs_init.rsc_meas_cccd_wr_sec = SEC_OPEN;
    
        err_code = ble_rscs_init(&m_rscs, &rscs_init);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the advertising functionality.
     */
    static void advertising_init(void)
    {
        ret_code_t             err_code;
        ble_advertising_init_t init;
    
        memset(&init, 0, sizeof(init));
    
        init.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
        init.advdata.include_appearance      = true;
        init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
    
        init.config.ble_adv_fast_enabled  = true;
        init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
        init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
    
        init.evt_handler = on_adv_evt;
    
        err_code = ble_advertising_init(&m_advertising, &init);
        APP_ERROR_CHECK(err_code);
    
        ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
    }
    
    
    /**@brief Function for initializing logging.
     */
    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 power management.
     */
    static void power_management_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling the idle state (main loop). If there is no pending log operation,
              then sleeps until the next event occurs.
     */
    static void idle_state_handle(void)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }
    
    
    /**@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 application main entry.
     */
    int main(void)
    {
        bool erase_bonds;
    
        // Initialize.
        log_init();
        timer_init();
        buttons_leds_init(&erase_bonds);
        power_management_init();
        ble_stack_init();
        scan_init();
        gap_params_init();
        gatt_init();
        conn_params_init();
        db_discovery_init();
        peer_manager_init();
        hrs_c_init();
        rscs_c_init();
        services_init();
        advertising_init();
    
        // Start execution.
        NRF_LOG_INFO("Relay example started.");
    
        if (erase_bonds == true)
        {
            // Scanning and advertising is done upon PM_EVT_PEERS_DELETE_SUCCEEDED event.
            delete_bonds();
        }
        else
        {
            adv_scan_start();
        }
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }
    

    In your code, you already fixed the issue. So there could be a different bug. 

    First could you explain why in your log, both of the service was with "UUID 0x1" ? 

    As you already noticed, from the log we can see that the service and characteristic discovery was finished but you didn't get the EVT_DISCOVERY_COMPLETE event in the the handler in main.c 

    We need to debug this.

    We print "Discovery of service with UUID 0x%x completed with success" inside on_descriptor_discovery_rsp() and then we call discovery_complete_evt_trigger() 

    Inside discovery_complete_evt_trigger(), we will collect the events until they match with the number of handler (m_num_of_handlers_reg) and then we will send out to each handlers in main.c 

    We will not send the event(s) out if the number of queued event is still lower than the m_num_of_handlers_reg. 

    Could you try printing out some debug log inside discovery_complete_evt_trigger() ? I suspect that pending_user_evts_send() is not called when it should be called. 

  • Hi Hung Bui,

    Thank you for your help.

    For the ble_app_hrs_rscs_relay example :

    I have tried to start scanning after the discovery is complete and it works. I can’t find the issue anymore.

    For my code :

     

    The two custom services have the same 16 bits UUID but not the same base UUID. 

    I was thinking it was possible to do this but maybe it’s a misunderstanding?

     

    This is the definition of the UUIDs for my custom service “RELAY” :

    This is the definition of the UUIDs for my custom service “RFID” :

    These are the structures I use in my main.c :

    This is in my sdk_config.h :

    I added NRF_LOG_DEBUG() in discovery_complete_evt_trigger() :

    /**@brief     Function for triggering a Discovery Complete or Service Not Found event to the
     *            application.
     *
     * @details   This function will fetch the event handler based on the UUID of the service being
     *            discovered. (The event handler is registered by the application beforehand).
     *            It then triggers an event indicating the completion of the service discovery.
     *            If no event handler was found, then this function will do nothing.
     *
     * @param[in] p_db_discovery Pointer to the DB discovery structure.
     * @param[in] is_srv_found   Variable to indicate if the service was found at the peer.
     * @param[in] conn_handle    Connection Handle.
     */
    static void discovery_complete_evt_trigger(ble_db_discovery_t * p_db_discovery,
                                               bool                 is_srv_found,
                                               uint16_t             conn_handle)
    {
        ble_db_discovery_evt_handler_t   p_evt_handler;
        ble_gatt_db_srv_t              * p_srv_being_discovered;
    
        p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);
    
        NRF_LOG_DEBUG("m_num_of_handlers_reg = %d",m_num_of_handlers_reg);
        NRF_LOG_DEBUG("m_pending_usr_evt_index = %d",m_pending_usr_evt_index);
    
        p_evt_handler = registered_handler_get(&(p_srv_being_discovered->srv_uuid));
    
        if (p_evt_handler != NULL)
        {
            if (m_pending_usr_evt_index < DB_DISCOVERY_MAX_USERS)
            {
                // Insert an event into the pending event list.
                m_pending_user_evts[m_pending_usr_evt_index].evt.conn_handle = conn_handle;
                m_pending_user_evts[m_pending_usr_evt_index].evt.params.discovered_db =
                    *p_srv_being_discovered;
    
                if (is_srv_found)
                {
                    m_pending_user_evts[m_pending_usr_evt_index].evt.evt_type =
                        BLE_DB_DISCOVERY_COMPLETE;
                }
                else
                {
                    m_pending_user_evts[m_pending_usr_evt_index].evt.evt_type =
                        BLE_DB_DISCOVERY_SRV_NOT_FOUND;
                }
    
                m_pending_user_evts[m_pending_usr_evt_index].evt_handler = p_evt_handler;
                m_pending_usr_evt_index++;
    
                NRF_LOG_DEBUG("m_pending_usr_evt_index = %d",m_pending_usr_evt_index);
    
                if (m_pending_usr_evt_index == m_num_of_handlers_reg)
                {
                    // All registered modules have pending events. Send all pending events to the user
                    // modules.
                    pending_user_evts_send();
                }
                else
                {
                    // Too many events pending. Do nothing. (Ideally this should not happen.)
                }
            }
        }
    }

    Now we will see m_num_of_handlers_reg and m_pending_usr_evt_index in the logs.

    I observed that even for the last service discovery which don't work, m_num_of_handlers_reg is equal to m_pending_usr_evt_index so the function pending_user_evts_send() should be called.

    I added NRF_LOG_DEBUG() in pending_user_evts_send() to see which event type is passed to the event handler :

    /**@brief Function for sending all pending discovery events to the corresponding user modules.
     */
    static void pending_user_evts_send(void)
    {
        for (uint32_t i = 0; i < m_num_of_handlers_reg; i++)
        {
            
            NRF_LOG_DEBUG("m_pending_user_evts[i].evt = %d",m_pending_user_evts[i].evt.evt_type);
    
            // Pass the event to the corresponding event handler.
            m_pending_user_evts[i].evt_handler(&(m_pending_user_evts[i].evt));
        }
    
        m_pending_usr_evt_index = 0;
    }

    Here is an example of the logs when I don't get the event EVT_DISCOVERY_COMPLETE :

    <info> app: ------------------------------------------------------------------------------------------
    <info> app: -------------------------------------Concentrator-----------------------------------------
    <info> app: ------------------------------------------------------------------------------------------
    <info> app: Scanning for RFID and RELAY.
    <info> app: >>>>>RFID found<<<<<
    <info> app: Connection 0x0 established, attempt to find RFID or RELAY
    <debug> ble_db_disc: Starting discovery of service with UUID 0x1 on connection handle 0x0.
    <debug> ble_db_disc: Found service UUID 0x1.
    <debug> ble_db_disc: Discovery of service with UUID 0x1 completed with success on connection handle 0x0.
    <debug> ble_db_disc: m_num_of_handlers_reg = 2
    <debug> ble_db_disc: m_pending_usr_evt_index = 0
    <debug> ble_db_disc: m_pending_usr_evt_index = 1
    <debug> ble_db_disc: Starting discovery of service with UUID 0x1 on connection handle 0x0.
    <debug> ble_db_disc: Service UUID 0x1 not found.
    <debug> ble_db_disc: m_num_of_handlers_reg = 2
    <debug> ble_db_disc: m_pending_usr_evt_index = 1
    <debug> ble_db_disc: m_pending_usr_evt_index = 2
    <debug> ble_db_disc: m_pending_user_evts[i].evt.evt_type = 0
    <info> app: **********RFID service discovered on conn_handle 0x0**********
    <info> app: Scanning for RFID and RELAY.
    <debug> ble_db_disc: m_pending_user_evts[i].evt.evt_type = 2
    <info> app: >>>>>RFID found<<<<<
    <info> app: Connection 0x1 established, attempt to find RFID or RELAY
    <debug> ble_db_disc: Starting discovery of service with UUID 0x1 on connection handle 0x1.
    <debug> ble_db_disc: Found service UUID 0x1.
    <debug> ble_db_disc: Discovery of service with UUID 0x1 completed with success on connection handle 0x1.
    <debug> ble_db_disc: m_num_of_handlers_reg = 2
    <debug> ble_db_disc: m_pending_usr_evt_index = 0
    <debug> ble_db_disc: m_pending_usr_evt_index = 1
    <debug> ble_db_disc: Starting discovery of service with UUID 0x1 on connection handle 0x1.
    <debug> ble_db_disc: Service UUID 0x1 not found.
    <debug> ble_db_disc: m_num_of_handlers_reg = 2
    <debug> ble_db_disc: m_pending_usr_evt_index = 1
    <debug> ble_db_disc: m_pending_usr_evt_index = 2
    <debug> ble_db_disc: m_pending_user_evts[i].evt.evt_type = 0
    <info> app: **********RFID service discovered on conn_handle 0x1**********
    <info> app: Scanning for RFID and RELAY.
    <debug> ble_db_disc: m_pending_user_evts[i].evt.evt_type = 2
    <info> app: >>>>>RELAY found<<<<<
    <info> app: Connection 0x2 established, attempt to find RFID or RELAY
    <debug> ble_db_disc: Starting discovery of service with UUID 0x1 on connection handle 0x2.
    <debug> ble_db_disc: Service UUID 0x1 not found.
    <debug> ble_db_disc: m_num_of_handlers_reg = 2
    <debug> ble_db_disc: m_pending_usr_evt_index = 0
    <debug> ble_db_disc: m_pending_usr_evt_index = 1
    <debug> ble_db_disc: Starting discovery of service with UUID 0x1 on connection handle 0x2.
    <debug> ble_db_disc: Found service UUID 0x1.
    <debug> ble_db_disc: Discovery of service with UUID 0x1 completed with success on connection handle 0x2.
    <debug> ble_db_disc: m_num_of_handlers_reg = 2
    <debug> ble_db_disc: m_pending_usr_evt_index = 1
    <debug> ble_db_disc: m_pending_usr_evt_index = 2
    <debug> ble_db_disc: m_pending_user_evts[i].evt.evt_type = 2
    <debug> ble_db_disc: m_pending_user_evts[i].evt.evt_type = 0

    Now, I don't really understand what is going wrong.

  • Hi Jules, 

    I think we are getting close, now the problem is around why relay_on_db_disc_evt() (or rfid_on_db_disc_evt() ) wouldn't detect when there is an UUID match (evt_type = 0 ). 

    There is a following check: 

    if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
    p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID &&
    p_evt->params.discovered_db.srv_uuid.type == p_relay_c->uuid_type)

    I suspect it could be something wrong with the uuid_type. If you can print out those values in the log, we may be able to find why the function is called but not executing anything. 

    We can try to test here and debug your application as well. But it doesn't built here. There is some absolute path you set (P:\Nordic\)

    If possible please send the latest source code. Are you still on SDK v15.0 or you moved to SDK v15.3 ? 

  • Hi Hung Bui,

    I added logs so we can see the parameters inside the checks in rfid_on_db_disc_evt() and in relay_on_db_disc_evt() when the event is BLE_DB_DISCOVERY_COMPLETE :

    void rfid_on_db_disc_evt(rfid_c_t * p_rfid_c, ble_db_discovery_evt_t const * p_evt)
    {
        if(p_evt->evt_type == 0)
        {
            NRF_LOG_INFO("rfid_on_db_disc_evt :");
            NRF_LOG_INFO("\tp_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : %d",p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : %d",p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.type : %d\t\t\tp_rfid_c->uuid_type : %d",p_evt->params.discovered_db.srv_uuid.type,p_rfid_c->uuid_type);
        } 
    
        // Check if the Led Button Service was discovered.
        if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
            p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID &&
            p_evt->params.discovered_db.srv_uuid.type == p_rfid_c->uuid_type)
        {
            rfid_c_evt_t evt;
    
            evt.evt_type    = RFID_C_EVT_DISCOVERY_COMPLETE;
            evt.conn_handle = p_evt->conn_handle;
    
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                //NRF_LOG_INFO("Discovering characteristics.");
                const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
                switch (p_char->characteristic.uuid.uuid)
                {
                    case SENSORS_FRAME_CHAR_UUID:
                        evt.params.peer_db.sensors_frame_handle      = p_char->characteristic.handle_value;
                        evt.params.peer_db.sensors_frame_cccd_handle = p_char->cccd_handle;
                        break;
                    
                    case MEASURE_REQUEST_CHAR_UUID:
                        evt.params.peer_db.measure_request_handle     = p_char->characteristic.handle_value;
                        break;
    
                    default:
                        break;
                }
            }
    
            //NRF_LOG_INFO("RFID Service discovered at peer.");
            //If the instance has been assigned prior to db_discovery, assign the db_handles
            if (p_rfid_c->conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                if ((p_rfid_c->peer_rfid_db.sensors_frame_handle      == BLE_GATT_HANDLE_INVALID)&&
                    (p_rfid_c->peer_rfid_db.sensors_frame_cccd_handle == BLE_GATT_HANDLE_INVALID))
                {
                    p_rfid_c->peer_rfid_db = evt.params.peer_db;
                }
            }
    
            p_rfid_c->evt_handler(p_rfid_c, &evt);
    
        }
    }
    void relay_on_db_disc_evt(relay_c_t * p_relay_c, ble_db_discovery_evt_t const * p_evt)
    {
        if(p_evt->evt_type == 0)
        {
            NRF_LOG_INFO("relay_on_db_disc_evt :");
            NRF_LOG_INFO("\tp_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : %d",p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : %d",p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.type : %d\t\t\tp_relay_c->uuid_type : %d",p_evt->params.discovered_db.srv_uuid.type,p_relay_c->uuid_type);
        }    
    
        // Check if the Led Button Service was discovered.
        if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
            p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID &&
            p_evt->params.discovered_db.srv_uuid.type == p_relay_c->uuid_type)
        {
            relay_c_evt_t evt;
    
            evt.evt_type    = RELAY_C_EVT_DISCOVERY_COMPLETE;
            evt.conn_handle = p_evt->conn_handle;
    
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                //NRF_LOG_INFO("Discovering characteristics.");
                const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
                switch (p_char->characteristic.uuid.uuid)
                {
                    case BLACKLIST_CHAR_UUID:
                        evt.params.peer_db.blacklist_handle      = p_char->characteristic.handle_value;
                        evt.params.peer_db.blacklist_cccd_handle = p_char->cccd_handle;
                        break;
                    case TRAME_CONCENTRATEUR_CHAR_UUID:
                        evt.params.peer_db.trame_concentrateur_handle     = p_char->characteristic.handle_value;
                        break;
                    default:
                        break;
                }
            }
            //NRF_LOG_INFO("RELAY Service discovered at peer.");
            //If the instance has been assigned prior to db_discovery, assign the db_handles
            if (p_relay_c->conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                if ((p_relay_c->peer_relay_db.blacklist_handle      == BLE_GATT_HANDLE_INVALID)&&
                    (p_relay_c->peer_relay_db.blacklist_cccd_handle == BLE_GATT_HANDLE_INVALID))
                {
                    p_relay_c->peer_relay_db = evt.params.peer_db;
                }
            }
            p_relay_c->evt_handler(p_relay_c, &evt);
        }
    }

    You were right, it seems like the problem is coming from the uuid_type, I get this :

    <info> app: ------------------------------------------------------------------------------------------
    <info> app: -------------------------------------Concentrator-----------------------------------------
    <info> app: ------------------------------------------------------------------------------------------
    <info> app: Scanning for RFID and RELAY.
    <info> app: >>>>>RFID found<<<<<
    <info> app: Connection 0x0 established, attempt to find RFID or RELAY
    <info> app: rfid_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_rfid_c->uuid_type : 2
    <info> app: **********RFID service discovered on conn_handle 0x0**********
    <info> app: Scanning for RFID and RELAY.
    <info> app: relay_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_relay_c->uuid_type : 3
    <info> app: >>>>>RFID found<<<<<
    <info> app: Connection 0x1 established, attempt to find RFID or RELAY
    <info> app: rfid_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_rfid_c->uuid_type : 2
    <info> app: **********RFID service discovered on conn_handle 0x1**********
    <info> app: Scanning for RFID and RELAY.
    <info> app: relay_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_relay_c->uuid_type : 21
    <info> app: >>>>>RELAY found<<<<<
    <info> app: Connection 0x2 established, attempt to find RFID or RELAY
    <info> app: rfid_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 3			p_rfid_c->uuid_type : 2
    <info> app: relay_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 3			p_relay_c->uuid_type : 178
    

    I will send you my source code in a private message.

    (P:\Nordic\) was the folder where I put SDK 15 and SDK 15.3.  (Now it is C:/Dev/nordic/)

    You have to put these two SDK in the folder of your choice and replace the path by the path of the folder you choosed. 

    I use SDK 15 but there is a exception. Only for the ble_db_discovery module, I use the SDK 15.3

    I did this in my .emProject file :

    ...
    
    c_user_include_directories= "... ;C:/Dev/nordic/nRF5_SDK_15.3.0_59ac345/components/ble/ble_db_discovery; ... "
    ...
    <folder Name="nRF_BLE">
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_advdata.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/ble_advertising/ble_advertising.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_conn_state.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.3.0_59ac345/components/ble/ble_db_discovery/ble_db_discovery.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_srv_common.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/nrf_ble_gatt/nrf_ble_gatt.c" />
    </folder>
    ...

Reply
  • Hi Hung Bui,

    I added logs so we can see the parameters inside the checks in rfid_on_db_disc_evt() and in relay_on_db_disc_evt() when the event is BLE_DB_DISCOVERY_COMPLETE :

    void rfid_on_db_disc_evt(rfid_c_t * p_rfid_c, ble_db_discovery_evt_t const * p_evt)
    {
        if(p_evt->evt_type == 0)
        {
            NRF_LOG_INFO("rfid_on_db_disc_evt :");
            NRF_LOG_INFO("\tp_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : %d",p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : %d",p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.type : %d\t\t\tp_rfid_c->uuid_type : %d",p_evt->params.discovered_db.srv_uuid.type,p_rfid_c->uuid_type);
        } 
    
        // Check if the Led Button Service was discovered.
        if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
            p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID &&
            p_evt->params.discovered_db.srv_uuid.type == p_rfid_c->uuid_type)
        {
            rfid_c_evt_t evt;
    
            evt.evt_type    = RFID_C_EVT_DISCOVERY_COMPLETE;
            evt.conn_handle = p_evt->conn_handle;
    
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                //NRF_LOG_INFO("Discovering characteristics.");
                const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
                switch (p_char->characteristic.uuid.uuid)
                {
                    case SENSORS_FRAME_CHAR_UUID:
                        evt.params.peer_db.sensors_frame_handle      = p_char->characteristic.handle_value;
                        evt.params.peer_db.sensors_frame_cccd_handle = p_char->cccd_handle;
                        break;
                    
                    case MEASURE_REQUEST_CHAR_UUID:
                        evt.params.peer_db.measure_request_handle     = p_char->characteristic.handle_value;
                        break;
    
                    default:
                        break;
                }
            }
    
            //NRF_LOG_INFO("RFID Service discovered at peer.");
            //If the instance has been assigned prior to db_discovery, assign the db_handles
            if (p_rfid_c->conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                if ((p_rfid_c->peer_rfid_db.sensors_frame_handle      == BLE_GATT_HANDLE_INVALID)&&
                    (p_rfid_c->peer_rfid_db.sensors_frame_cccd_handle == BLE_GATT_HANDLE_INVALID))
                {
                    p_rfid_c->peer_rfid_db = evt.params.peer_db;
                }
            }
    
            p_rfid_c->evt_handler(p_rfid_c, &evt);
    
        }
    }
    void relay_on_db_disc_evt(relay_c_t * p_relay_c, ble_db_discovery_evt_t const * p_evt)
    {
        if(p_evt->evt_type == 0)
        {
            NRF_LOG_INFO("relay_on_db_disc_evt :");
            NRF_LOG_INFO("\tp_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : %d",p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : %d",p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID);
            NRF_LOG_INFO("\tp_evt->params.discovered_db.srv_uuid.type : %d\t\t\tp_relay_c->uuid_type : %d",p_evt->params.discovered_db.srv_uuid.type,p_relay_c->uuid_type);
        }    
    
        // Check if the Led Button Service was discovered.
        if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
            p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID &&
            p_evt->params.discovered_db.srv_uuid.type == p_relay_c->uuid_type)
        {
            relay_c_evt_t evt;
    
            evt.evt_type    = RELAY_C_EVT_DISCOVERY_COMPLETE;
            evt.conn_handle = p_evt->conn_handle;
    
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                //NRF_LOG_INFO("Discovering characteristics.");
                const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
                switch (p_char->characteristic.uuid.uuid)
                {
                    case BLACKLIST_CHAR_UUID:
                        evt.params.peer_db.blacklist_handle      = p_char->characteristic.handle_value;
                        evt.params.peer_db.blacklist_cccd_handle = p_char->cccd_handle;
                        break;
                    case TRAME_CONCENTRATEUR_CHAR_UUID:
                        evt.params.peer_db.trame_concentrateur_handle     = p_char->characteristic.handle_value;
                        break;
                    default:
                        break;
                }
            }
            //NRF_LOG_INFO("RELAY Service discovered at peer.");
            //If the instance has been assigned prior to db_discovery, assign the db_handles
            if (p_relay_c->conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                if ((p_relay_c->peer_relay_db.blacklist_handle      == BLE_GATT_HANDLE_INVALID)&&
                    (p_relay_c->peer_relay_db.blacklist_cccd_handle == BLE_GATT_HANDLE_INVALID))
                {
                    p_relay_c->peer_relay_db = evt.params.peer_db;
                }
            }
            p_relay_c->evt_handler(p_relay_c, &evt);
        }
    }

    You were right, it seems like the problem is coming from the uuid_type, I get this :

    <info> app: ------------------------------------------------------------------------------------------
    <info> app: -------------------------------------Concentrator-----------------------------------------
    <info> app: ------------------------------------------------------------------------------------------
    <info> app: Scanning for RFID and RELAY.
    <info> app: >>>>>RFID found<<<<<
    <info> app: Connection 0x0 established, attempt to find RFID or RELAY
    <info> app: rfid_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_rfid_c->uuid_type : 2
    <info> app: **********RFID service discovered on conn_handle 0x0**********
    <info> app: Scanning for RFID and RELAY.
    <info> app: relay_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_relay_c->uuid_type : 3
    <info> app: >>>>>RFID found<<<<<
    <info> app: Connection 0x1 established, attempt to find RFID or RELAY
    <info> app: rfid_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_rfid_c->uuid_type : 2
    <info> app: **********RFID service discovered on conn_handle 0x1**********
    <info> app: Scanning for RFID and RELAY.
    <info> app: relay_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 2			p_relay_c->uuid_type : 21
    <info> app: >>>>>RELAY found<<<<<
    <info> app: Connection 0x2 established, attempt to find RFID or RELAY
    <info> app: rfid_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RFID_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 3			p_rfid_c->uuid_type : 2
    <info> app: relay_on_db_disc_evt :
    <info> app: 	p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.uuid == RELAY_UUID : 1
    <info> app: 	p_evt->params.discovered_db.srv_uuid.type : 3			p_relay_c->uuid_type : 178
    

    I will send you my source code in a private message.

    (P:\Nordic\) was the folder where I put SDK 15 and SDK 15.3.  (Now it is C:/Dev/nordic/)

    You have to put these two SDK in the folder of your choice and replace the path by the path of the folder you choosed. 

    I use SDK 15 but there is a exception. Only for the ble_db_discovery module, I use the SDK 15.3

    I did this in my .emProject file :

    ...
    
    c_user_include_directories= "... ;C:/Dev/nordic/nRF5_SDK_15.3.0_59ac345/components/ble/ble_db_discovery; ... "
    ...
    <folder Name="nRF_BLE">
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_advdata.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/ble_advertising/ble_advertising.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_conn_state.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.3.0_59ac345/components/ble/ble_db_discovery/ble_db_discovery.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_srv_common.c" />
    <file file_name="C:/Dev/nordic/nRF5_SDK_15.0.0_a53641a/components/ble/nrf_ble_gatt/nrf_ble_gatt.c" />
    </folder>
    ...

Children
  • Hi Jules, 

    I think I found something wrong here. 

    As you can see in the log, the p_relay_c->uuid_type showing quite strange value (21, 178). 

    I think it was because of this function: 

    static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
    {
      rfid_on_db_disc_evt(&m_rfid_c[p_evt->conn_handle], p_evt);
      relay_on_db_disc_evt(&m_relay_c[p_evt->conn_handle], p_evt);
    }

    In our multilink example this work because we only have one service lbs and there can be a simple match between the index of the m_lbs_c array and the index of connection handle. When it's not the case with your application. 

    You are declaring m_rfid_c array with the size of 7 and m_relay_c with size of 1. Calling a function using m_relay_c[p_evt->conn_handle] will cause trouble if the conn_handle is not equal to 0.

    And since you don't know which one will be the first one to connect you can't be sure that the conn_handle = 0 will be always be the relay. 

    So you need to define a table that keep track of the connection handle that match with the relay and the list of active RFID node in an array. And keep that table (array) up to date on each CONNECTED/DISCONNECTED event. (You can detect if a device is a relay or a RFID by the if check inside rfid_on_db_disc_evt() and    relay_on_db_disc_evt() ) 

  • Hi Hung Bui,

    I missed this and the error was due to this, thank you for your help.

    Now I have a table containing the types (RFID_TYPE our RELAY_TYPE) of devices for each conn_handle which is updated on DISCOVERY_COMPLETE and BLE_GAP_EVT_DISCONNECTED.

    I can count the RFID devices to know which instance of m_rfid_c I have to call in db_disc_handler. 

    For the relay, since I always use one relay, I define only one instance of relay client and call this one in db_disc_handler.

  • Hi Jules, 

    Glad that the issue is now fixed :D Sorry that the case has been dragging for some time. It's a bit hard to catch the bug :D

  • Hi    I am facing the same issue in my central device. Can you please help me sharing how to created a table to keep a track of conn_handle and how to keep it updated? 

    Regards,

    Snehal

Related