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

Timer unable to start multiple instances of app timer

Hi , 

I have been working on an example code in SDK : nRF5_SDK_17.0.2_d674dde\examples\ble_central_and_peripheral\experimental\ble_app_multirole_lesc\pca10040\s132\ses\

So I wanted to create a repeated timeout based event to be  executed. So I created a the timer instance and started  the timer; however, the timeout even is not happening could you help me. 

I have provided the section fo the code where I have created the timer inside the main function. 

#define timer_ticks APP_TIMER_TICKS(3000)

APP_TIMER_DEF(mytimer_id);

////app_timer_id_t mytimer_id;
error_code = app_timer_create(&mytimer_id, APP_TIMER_MODE_REPEATED, timer_event_handler);
APP_ERROR_CHECK(error_code);
error_code = app_timer_start(&mytimer_id,timer_ticks,NULL);

The initilialization fo the timer is already done prior to this. 

  • well, I have reverted back the interrupt priority level, I have declared the rest of the timer in the main as you said. I have gone through the example code you mentione. The example code doesnt deal with mulitple instance of the  timer being created. Also the  issue i face , remains the same. 

  • Hi,

    Rommel said:
    The example code doesnt deal with mulitple instance of the  timer being created

     I disagree, the ble_app_hrs example use 4 instances of the app timer library. Why do you want to make multiple instances of the timer? 

    Try comparing your code with that example.

    regards

    Jared 

  • Hi, 

    I have tried the same, as per what is given in the code; however, the same doesnt work upon integration with ,ble_app_multirole_lesc.

  • Here is an example that shows how to setup two timers. LED2 and LED4 will blink on the DK:

    /**
     * Copyright (c) 2016 - 2020, 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 Application main file for the BLE multirole LE Secure Connections (LESC) example.
     *
     * @detail This application demonstrates bonding with LE Secure Connections both as a peripheral and as a central.
     *
     * 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: This application requires the use of an external ECC library for public key and shared secret calculation.
     *        Refer to the application's documentation for more details.
     *
     */
    
    #include "sdk_config.h"
    #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 "app_util.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_hrs_c.h"
    #include "ble_conn_state.h"
    #include "fds.h"
    #include "nrf_crypto.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_lesc.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 LESC_DEBUG_MODE                 0                                               /**< Set to 1 to use the LESC debug keys. The debug mode allows you to use a sniffer to inspect traffic. */
    #define LESC_MITM_NC                    1                                               /**< Use MITM (Numeric Comparison). */
    
    /** @brief The maximum number of peripheral and central links combined. */
    #define NRF_BLE_LINK_COUNT              (NRF_SDH_BLE_PERIPHERAL_LINK_COUNT + NRF_SDH_BLE_CENTRAL_LINK_COUNT)
    
    #define APP_BLE_CONN_CFG_TAG            1                                               /**< Tag that identifies the SoftDevice BLE configuration. */
    
    #define CENTRAL_SCANNING_LED            BSP_BOARD_LED_0
    #define CENTRAL_CONNECTED_LED           BSP_BOARD_LED_1
    #define PERIPHERAL_ADVERTISING_LED      BSP_BOARD_LED_2
    #define PERIPHERAL_CONNECTED_LED        BSP_BOARD_LED_3
    
    #define SCAN_DURATION                   0x0000                                          /**< Duration of the scanning in units of 10 milliseconds. If set to 0x0000, scanning continues until it is explicitly disabled. */
    #define APP_ADV_DURATION                18000                                           /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    
    
    #define SEC_PARAMS_BOND                 1                                               /**< Perform bonding. */
    #if LESC_MITM_NC
    #define SEC_PARAMS_MITM                 1                                               /**< Man In The Middle protection required. */
    #define SEC_PARAMS_IO_CAPABILITIES      BLE_GAP_IO_CAPS_DISPLAY_YESNO                   /**< Display Yes/No to force Numeric Comparison. */
    #else
    #define SEC_PARAMS_MITM                 0                                               /**< Man In The Middle protection required. */
    #define SEC_PARAMS_IO_CAPABILITIES      BLE_GAP_IO_CAPS_NONE                            /**< No I/O caps. */
    #endif
    #define SEC_PARAMS_LESC                 1                                               /**< LE Secure Connections pairing required. */
    #define SEC_PARAMS_KEYPRESS             0                                               /**< Keypress notifications not required. */
    #define SEC_PARAMS_OOB                  0                                               /**< Out Of Band data not available. */
    #define SEC_PARAMS_MIN_KEY_SIZE         7                                               /**< Minimum encryption key size in octets. */
    #define SEC_PARAMS_MAX_KEY_SIZE         16                                              /**< Maximum encryption key size in octets. */
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                           /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                          /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3                                               /**< Number of attempts before giving up the connection parameter negotiation. */
    
    /**@brief   Priority of the application BLE event handler.
     * @note    There is no need to modify this value.
     */
    #define APP_BLE_OBSERVER_PRIO           3
    
    APP_TIMER_DEF(m_repeated_timer_id);
    APP_TIMER_DEF(m_repeated_timer_id_2);
    
    typedef struct
    {
        bool           is_connected;
        ble_gap_addr_t address;
    } conn_peer_t;
    
    NRF_BLE_GQ_DEF(m_ble_gatt_queue,                                            /**< BLE GATT Queue instance. */
                   NRF_SDH_BLE_CENTRAL_LINK_COUNT,
                   NRF_BLE_GQ_QUEUE_SIZE);
    BLE_HRS_DEF(m_hrs);                                                         /**< Heart Rate Service instance. */
    BLE_HRS_C_DEF(m_hrs_c);                                                     /**< Structure used to identify the Heart Rate client module. */
    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_DEF(m_db_disc);                                            /**< Database discovery module instance. */
    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 volatile uint16_t  m_conn_handle_num_comp_central     = BLE_CONN_HANDLE_INVALID;  /**< Connection handle for the central that needs a numeric comparison button press. */
    static volatile uint16_t  m_conn_handle_num_comp_peripheral  = BLE_CONN_HANDLE_INVALID;  /**< Connection handle for the peripheral that needs a numeric comparison button press. */
    
    static conn_peer_t        m_connected_peers[NRF_BLE_LINK_COUNT];                         /**< Array of connected peers. */
    
    static char * roles_str[] =
    {
        "INVALID_ROLE",
        "PERIPHERAL",
        "CENTRAL",
    };
    
    /**@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 const char 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}};
    
                                       
    /**@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 the Heart Rate Service Client.
     *
     * @param[in]   nrf_error   Error code containing information about what went wrong.
     */
    static void hrs_c_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    /**
     * Timeout handler for the requested timer
     * 
     * 
     */
     
    static void repeated_timer_handler(void * p_context)
    {
        nrf_gpio_pin_toggle(LED_4);
    }
    
    static void repeated_timer_handler_2(void * p_context)
    {
        nrf_gpio_pin_toggle(LED_2);
    }
    
    
    
    /**@brief Function for handling errors from the Connection Parameters module.
     *
     * @param[in] nrf_error  Error code that contains information about what went wrong.
     */
    static void conn_params_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    /**@brief Function for handling Scanning Module events.
     */
    static void scan_evt_handler(scan_evt_t const * p_scan_evt)
    {
        ret_code_t err_code;
    
        switch(p_scan_evt->scan_evt_id)
        {
            case NRF_BLE_SCAN_EVT_CONNECTING_ERROR:
            {
                err_code = p_scan_evt->params.connecting_err.err_code;
                APP_ERROR_CHECK(err_code);
            } break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for initializing the scanning and setting the filters.
     */
    static void scan_init(void)
    {
        ret_code_t          err_code;
        ble_uuid_t          target_uuid = 
        {
            .uuid = BLE_UUID_HEART_RATE_SERVICE,
            .type = BLE_UUID_TYPE_BLE
        };
        nrf_ble_scan_init_t init_scan;
    
        memset(&init_scan, 0, sizeof(init_scan));
    
        init_scan.connect_if_match = true;
        init_scan.conn_cfg_tag     = APP_BLE_CONN_CFG_TAG;
    
        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, 
                                           &target_uuid);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_ble_scan_filters_enable(&m_scan, 
                                               NRF_BLE_SCAN_NAME_FILTER | NRF_BLE_SCAN_UUID_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);
    
        NRF_LOG_INFO("Scanning");
    }
    
    
    /**@brief Function for initializing the advertising and the scanning.
     */
    static void adv_scan_start(void)
    {
        ret_code_t err_code;
    
        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);
    
        NRF_LOG_INFO("Advertising");
    }
    
    
    /**@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);
        }
    }
    
    
    /**@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;
    
                    // We do not want to connect to two peripherals offering the same service, so when
                    // a UUID is matched, we check whether we are not already connected to a peer which
                    // offers the same service
                    filter_settings_change();
    
                    NRF_LOG_INFO("CENTRAL: HRS discovered on conn_handle 0x%x",
                                 m_conn_handle_hrs_c);
    
                    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);
    
                    // 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);
                }
            } break; // BLE_HRS_C_EVT_DISCOVERY_COMPLETE
    
            case BLE_HRS_C_EVT_HRM_NOTIFICATION:
            {
                NRF_LOG_INFO("CENTRAL: Heart Rate = %d", p_hrs_c_evt->params.hrm.hr_value);
            } break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    /**@brief Function for checking whether a link already exists with a newly connected peer.
     *
     * @details This function checks whether the newly connected device is already connected.
     *
     * @param[in]   p_connected_evt Bluetooth connected event.
     * @return                      True if the peer's address is found in the list of connected peers,
     *                              false otherwise.
     */
    static bool is_already_connected(ble_gap_addr_t const * p_connected_adr)
    {
        for (uint32_t i = 0; i < NRF_BLE_LINK_COUNT; i++)
        {
            if (m_connected_peers[i].is_connected)
            {
                if (m_connected_peers[i].address.addr_type == p_connected_adr->addr_type)
                {
                    if (memcmp(m_connected_peers[i].address.addr,
                               p_connected_adr->addr,
                               sizeof(m_connected_peers[i].address.addr)) == 0)
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    
    /** @brief Function for handling a numeric comparison match request. */
    static void on_match_request(uint16_t conn_handle, uint8_t role)
    {
        // Mark the appropriate conn_handle as pending. The rest is handled on button press.
        NRF_LOG_INFO("Press Button 1 to confirm, Button 2 to reject");
        if (role == BLE_GAP_ROLE_CENTRAL)
        {
            m_conn_handle_num_comp_central = conn_handle;
        }
        else if (role == BLE_GAP_ROLE_PERIPH)
        {
            m_conn_handle_num_comp_peripheral = conn_handle;
        }
    }
    
    
    
    
    
    /**@brief Function for assigning new connection handle to the 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_BLE_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 Stack events that are common to both the central and peripheral roles.
     * @param[in] conn_handle Connection Handle.
     * @param[in] p_ble_evt  Bluetooth stack event.
     */
    static void on_ble_evt(uint16_t conn_handle, ble_evt_t const * p_ble_evt)
    {
        char        passkey[BLE_GAP_PASSKEY_LEN + 1];
        uint16_t    role = ble_conn_state_role(conn_handle);
    
        pm_handler_secure_on_connection(p_ble_evt);
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                m_connected_peers[conn_handle].is_connected = true;
                m_connected_peers[conn_handle].address = p_ble_evt->evt.gap_evt.params.connected.peer_addr;
                multi_qwr_conn_handle_assign(conn_handle);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                memset(&m_connected_peers[conn_handle], 0x00, sizeof(m_connected_peers[0]));
                break;
    
            case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
                NRF_LOG_INFO("%s: BLE_GAP_EVT_SEC_PARAMS_REQUEST", nrf_log_push(roles_str[role]));
                break;
    
            case BLE_GAP_EVT_PASSKEY_DISPLAY:
                memcpy(passkey, p_ble_evt->evt.gap_evt.params.passkey_display.passkey, BLE_GAP_PASSKEY_LEN);
                passkey[BLE_GAP_PASSKEY_LEN] = 0x00;
                NRF_LOG_INFO("%s: BLE_GAP_EVT_PASSKEY_DISPLAY: passkey=%s match_req=%d",
                             nrf_log_push(roles_str[role]),
                             nrf_log_push(passkey),
                             p_ble_evt->evt.gap_evt.params.passkey_display.match_request);
    
                if (p_ble_evt->evt.gap_evt.params.passkey_display.match_request)
                {
                    on_match_request(conn_handle, role);
                }
                break;
    
            case BLE_GAP_EVT_AUTH_KEY_REQUEST:
                NRF_LOG_INFO("%s: BLE_GAP_EVT_AUTH_KEY_REQUEST", nrf_log_push(roles_str[role]));
                break;
    
            case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
                NRF_LOG_INFO("%s: BLE_GAP_EVT_LESC_DHKEY_REQUEST", nrf_log_push(roles_str[role]));
                break;
    
             case BLE_GAP_EVT_AUTH_STATUS:
                 NRF_LOG_INFO("%s: BLE_GAP_EVT_AUTH_STATUS: status=0x%x bond=0x%x lv4: %d kdist_own:0x%x kdist_peer:0x%x",
                              nrf_log_push(roles_str[role]),
                              p_ble_evt->evt.gap_evt.params.auth_status.auth_status,
                              p_ble_evt->evt.gap_evt.params.auth_status.bonded,
                              p_ble_evt->evt.gap_evt.params.auth_status.sm1_levels.lv4,
                              *((uint8_t *)&p_ble_evt->evt.gap_evt.params.auth_status.kdist_own),
                              *((uint8_t *)&p_ble_evt->evt.gap_evt.params.auth_status.kdist_peer));
                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,
                };
                ret_code_t err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    /**@brief Function for handling BLE Stack events that are related to central application.
     *
     * @details This function keeps the connection handles of central application up-to-date. It
     * parses scanning reports, initiates a connection attempt to peripherals when a target UUID
     * is found, and manages connection parameter update requests. Additionally, it updates the status
     * of LEDs used to report the central application's activity.
     *
     * @note        Since this function updates connection handles, @ref BLE_GAP_EVT_DISCONNECTED events
     *              must be dispatched to the target application before invoking this function.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     */
    static void on_ble_central_evt(ble_evt_t const * p_ble_evt)
    {
        ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
        ret_code_t            err_code;
    
        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, handle: %d.", p_gap_evt->conn_handle);
                // If no Heart Rate Sensor is currently connected, try to find them on this peripheral.
                if (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID)
                {
                    NRF_LOG_INFO("CENTRAL: Searching for HRS on conn_handle 0x%x", p_gap_evt->conn_handle);
    
                    err_code = ble_db_discovery_start(&m_db_disc, p_gap_evt->conn_handle);
                    APP_ERROR_CHECK(err_code);
                }
                // Update status of LEDs.
                bsp_board_led_off(CENTRAL_SCANNING_LED);
                bsp_board_led_on(CENTRAL_CONNECTED_LED);
            } break; // BLE_GAP_EVT_CONNECTED
    
            // Upon disconnection, reset the connection handle of the peer that disconnected, update
            // the status of LEDs, and start scanning again.
            case BLE_GAP_EVT_DISCONNECTED:
            {
                NRF_LOG_INFO("CENTRAL: Disconnected, handle: %d, reason: 0x%x",
                             p_gap_evt->conn_handle,
                           p_gap_evt->params.disconnected.reason);
    
                // Update the status of LEDs.
                bsp_board_led_off(CENTRAL_CONNECTED_LED);
                bsp_board_led_on(CENTRAL_SCANNING_LED);
    
                if (p_gap_evt->conn_handle == m_conn_handle_hrs_c)
                {
                    ble_uuid_t target_uuid = {.uuid = BLE_UUID_HEART_RATE_SERVICE, .type = BLE_UUID_TYPE_BLE};
                    m_conn_handle_hrs_c    = BLE_CONN_HANDLE_INVALID;
    
                    err_code = nrf_ble_scan_filter_set(&m_scan, 
                                                       SCAN_UUID_FILTER, 
                                                       &target_uuid);
                    APP_ERROR_CHECK(err_code);
                }
                
                scan_start();
            } break; // BLE_GAP_EVT_DISCONNECTED
    
            case BLE_GAP_EVT_TIMEOUT:
            {
                // Timeout for scanning is not specified, so only connection attemps can time out.
                if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
                {
                    NRF_LOG_DEBUG("CENTRAL: 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_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("CENTRAL: 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("CENTRAL: 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 Stack events that involves peripheral applications. Manages the
     * LEDs used to report the status 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;
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("PERIPHERAL: Connected, handle %d.", p_ble_evt->evt.gap_evt.conn_handle);
                bsp_board_led_off(PERIPHERAL_ADVERTISING_LED);
                bsp_board_led_on(PERIPHERAL_CONNECTED_LED);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("PERIPHERAL: Disconnected, handle %d, reason 0x%x.",
                             p_ble_evt->evt.gap_evt.conn_handle,
                             p_ble_evt->evt.gap_evt.params.disconnected.reason);
                // LED indication will be changed when advertising starts.
            break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("PERIPHERAL: 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("PERIPHERAL: 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:
                bsp_board_led_on(PERIPHERAL_ADVERTISING_LED);
                bsp_board_led_off(PERIPHERAL_CONNECTED_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 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);
    
        if (    (p_ble_evt->header.evt_id == BLE_GAP_EVT_CONNECTED)
            &&  (is_already_connected(&p_ble_evt->evt.gap_evt.params.connected.peer_addr)))
        {
            NRF_LOG_INFO("%s: Already connected to this device as %s (handle: %d), disconnecting.",
                         (role == BLE_GAP_ROLE_PERIPH) ? "PERIPHERAL" : "CENTRAL",
                         (role == BLE_GAP_ROLE_PERIPH) ? "CENTRAL"    : "PERIPHERAL",
                         conn_handle);
    
            (void)sd_ble_gap_disconnect(conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
    
            // Do not process the event further.
            return;
        }
    
        on_ble_evt(conn_handle, p_ble_evt);
    
        if (role == BLE_GAP_ROLE_PERIPH)
        {
            // Manages peripheral LEDs.
            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))
        {
            on_ble_central_evt(p_ble_evt);
        }
    }
    
    
    /**@brief Function for initializing the Heart Rate Service client. */
    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;
        hrs_c_init_obj.error_handler = hrs_c_error_handler;
        hrs_c_init_obj.p_gatt_queue  = &m_ble_gatt_queue;
    
        err_code = ble_hrs_c_init(&m_hrs_c, &hrs_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 by 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_params;
        ret_code_t err_code;
    
        err_code = pm_init();
        APP_ERROR_CHECK(err_code);
    
        memset(&sec_params, 0, sizeof(ble_gap_sec_params_t));
    
        // Security parameters to be used for all security procedures.
        sec_params.bond           = SEC_PARAMS_BOND;
        sec_params.mitm           = SEC_PARAMS_MITM;
        sec_params.lesc           = SEC_PARAMS_LESC;
        sec_params.keypress       = SEC_PARAMS_KEYPRESS;
        sec_params.io_caps        = SEC_PARAMS_IO_CAPABILITIES;
        sec_params.oob            = SEC_PARAMS_OOB;
        sec_params.min_key_size   = SEC_PARAMS_MIN_KEY_SIZE;
        sec_params.max_key_size   = SEC_PARAMS_MAX_KEY_SIZE;
        sec_params.kdist_own.enc  = 1;
        sec_params.kdist_own.id   = 1;
        sec_params.kdist_peer.enc = 1;
        sec_params.kdist_peer.id  = 1;
    
        err_code = pm_sec_params_set(&sec_params);
        APP_ERROR_CHECK(err_code);
    
        err_code = pm_register(pm_evt_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /** @brief Delete all data stored for all peers. */
    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 accepting or rejecting a numeric comparison. */
    static void num_comp_reply(uint16_t conn_handle, bool accept)
    {
        uint8_t    key_type;
        ret_code_t err_code;
    
        if (accept)
        {
            NRF_LOG_INFO("Numeric Match. Conn handle: %d", conn_handle);
            key_type = BLE_GAP_AUTH_KEY_TYPE_PASSKEY;
        }
        else
        {
            NRF_LOG_INFO("Numeric REJECT. Conn handle: %d", conn_handle);
            key_type = BLE_GAP_AUTH_KEY_TYPE_NONE;
        }
    
        err_code = sd_ble_gap_auth_key_reply(conn_handle,
                                             key_type,
                                             NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /** @brief Function for handling button presses for numeric comparison match requests. */
    static void on_num_comp_button_press(bool accept)
    {
        // Check whether any links have pending match requests, and if so, send a reply.
        if (m_conn_handle_num_comp_central != BLE_CONN_HANDLE_INVALID)
        {
            num_comp_reply(m_conn_handle_num_comp_central, accept);
            m_conn_handle_num_comp_central = BLE_CONN_HANDLE_INVALID;
        }
        else if (m_conn_handle_num_comp_peripheral != BLE_CONN_HANDLE_INVALID)
        {
            num_comp_reply(m_conn_handle_num_comp_peripheral, accept);
            m_conn_handle_num_comp_peripheral = BLE_CONN_HANDLE_INVALID;
        }
    }
    
    
    /**@brief Function for handling events from the BSP module.
     *
     * @param[in]   event   Event generated by button press.
     */
    static void bsp_event_handler(bsp_event_t event)
    {
        ret_code_t err_code;
        switch (event)
        {
            case BSP_EVENT_KEY_0:
                err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, 87);
                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);
                }
    
                on_num_comp_button_press(true);
                break;
    
          case BSP_EVENT_KEY_1:
                on_num_comp_button_press(false);
                break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for initializing buttons and LEDs.
     *
     * @param[out] p_erase_bonds  True if the clear bonding button is pressed to
     *                            wake the application up. False otherwise.
     */
    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, bsp_event_handler);
        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 = m_scan.conn_params;
    
        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 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 containing information about what went wrong.
     */
    static void nrf_qwr_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    /**@brief Function for initializing the Queued Write instances.
     */
    static void qwr_init(void)
    {
        ret_code_t         err_code;
        nrf_ble_qwr_init_t qwr_init_obj = {0};
    
        qwr_init_obj.error_handler = nrf_qwr_error_handler;
    
        for (uint32_t i = 0; i < NRF_BLE_LINK_COUNT; i++)
        {
            err_code = nrf_ble_qwr_init(&m_qwr[i], &qwr_init_obj);
            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_GATT_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);
    }
    
    
    /**@brief Function for initializing the database discovery module. */
    static void db_discovery_init(void)
    {
        ble_db_discovery_init_t db_init;
    
        memset(&db_init, 0, sizeof(db_init));
    
        db_init.evt_handler =  db_disc_handler;
        db_init.p_gatt_queue = &m_ble_gatt_queue;
    
        ret_code_t err_code = ble_db_discovery_init(&db_init);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the Heart Rate service. */
    static void hrs_init(void)
    {
        ret_code_t     err_code;
        ble_hrs_init_t hrs_init_params;
        uint8_t        body_sensor_location;
    
        // Initialize the Heart Rate Service.
        body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER;
    
        memset(&hrs_init_params, 0, sizeof(hrs_init_params));
    
        hrs_init_params.evt_handler                 = NULL;
        hrs_init_params.is_sensor_contact_supported = true;
        hrs_init_params.p_body_sensor_location      = &body_sensor_location;
    
        // Require LESC with MITM (Numeric Comparison).
        hrs_init_params.hrm_cccd_wr_sec = SEC_MITM;
        hrs_init_params.bsl_rd_sec      = SEC_MITM;
    
        err_code = ble_hrs_init(&m_hrs, &hrs_init_params);
        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 = 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 the timer. */
    static void timer_init(void)
    {
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@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).
     *
     * @details Handles any pending log or key operations, or both, then sleeps until the next event occurs.
     */
    static void idle_state_handle(void)
    {
        ret_code_t err_code;
        
        err_code = nrf_ble_lesc_request_handler();
        APP_ERROR_CHECK(err_code);
        
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }
    
    /**@brief Create timers.
     */
    static void create_timers()
    {
        ret_code_t err_code;
    
        // Create timers
        err_code = app_timer_create(&m_repeated_timer_id,
                                    APP_TIMER_MODE_REPEATED,
                                    repeated_timer_handler);
        APP_ERROR_CHECK(err_code);
    
            err_code = app_timer_create(&m_repeated_timer_id_2,
                                    APP_TIMER_MODE_REPEATED,
                                    repeated_timer_handler_2);
        APP_ERROR_CHECK(err_code);
    
    
    
        err_code = app_timer_start(m_repeated_timer_id, APP_TIMER_TICKS(200), NULL);
        APP_ERROR_CHECK(err_code);
    
        err_code = app_timer_start(m_repeated_timer_id_2, APP_TIMER_TICKS(200), NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    int main(void)
    {
        bool erase_bonds;
    
        // Initialize.
        log_init();
        timer_init();
        create_timers();
        buttons_leds_init(&erase_bonds);
        power_management_init();
        ble_stack_init();
        scan_init();
        gap_params_init();
        gatt_init();
        conn_params_init();
        db_discovery_init();
        qwr_init();
        hrs_init();
        hrs_c_init();
        peer_manager_init();
        advertising_init();
    
        // Start execution.
        NRF_LOG_INFO("LE Secure Connections example started.");
    
        if (erase_bonds == true)
        {
            delete_bonds();
            // Scanning and advertising is started by PM_EVT_PEERS_DELETE_SUCEEDED.
        }
        else
        {
            adv_scan_start();
        }
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }
    

Related