want to achieve 1-2 Mbps through BLE.

I am working on project in which i want to read data from sd card and send it to mobile through BLE , I am using ble_nus_send to send data but when i decrease the delay then its giving me error 19 and when i increase delay then it is running smoothly .

Can i achieve something between 1-2 Mbps through ble_nus_Send or have to do with some other approach . 

Parents
  • Hello,

    ble_nus_send to send data but when i decrease the delay then its giving me error 19 and when i increase delay then it is running smoothly .

    Exactly which function is returning error 19?

    Can i achieve something between 1-2 Mbps through ble_nus_Send or have to do with some other approach . 

    Please see the SoftDevice throughput documentation for what parameters to use to achieve the highest throughputs.

    Best regards,
    Karl

  • Hello, 

    I have seen throughput documentation please tell me how to achieve highest rate and which example can i use to achieve highest rate . 

  • /**
     * Copyright (c) 2014 - 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 BLE Heart Rate Collector application main file.
     *
     * This file contains the source code for a sample heart rate collector.
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    
    #include "bsp.h"
    #include "diskio_blkdev.h"
    #include "ff.h"
    #include "nrf.h"
    #include "nrf_block_dev_sdc.h"
    #include "nrf_delay.h"
    #include "nrf_drv_gpiote.h"
    #include "nrf_drv_timer.h"
    
    #include "app_uart.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "ble_nus.h"
    #include "nrf_ble_qwr.h"
    
    #include "app_error.h"
    #include "app_timer.h"
    #include "app_util.h"
    #include "ble.h"
    #include "ble_bas_c.h"
    #include "ble_conn_state.h"
    #include "ble_db_discovery.h"
    #include "ble_hci.h"
    #include "ble_hrs_c.h"
    #include "ble_srv_common.h"
    #include "bsp_btn_ble.h"
    #include "fds.h"
    #include "nordic_common.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_lesc.h"
    #include "nrf_ble_scan.h"
    #include "nrf_fstorage.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "nrf_pwr_mgmt.h"
    #include "nrf_sdh.h"
    #include "nrf_sdh_ble.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_sdm.h"
    #include "peer_manager.h"
    #include "peer_manager_handler.h"
    
    #if defined(UART_PRESENT)
    #include "nrf_uart.h"
    #endif
    #if defined(UARTE_PRESENT)
    #include "nrf_uarte.h"
    #endif
    
    #define FILE_NAME "hr.CSV"
    #define TEST_STRING "SD card example."
    
    #define NUS_SERVICE_UUID_TYPE BLE_UUID_TYPE_VENDOR_BEGIN
    
    #ifdef BSP_BUTTON_0
    #define PIN_IN BSP_BUTTON_0
    #endif
    #ifndef PIN_IN
    #error "Please indicate input pin"
    #endif
    
    #ifdef BSP_LED_1
    #define PIN_OUT BSP_LED_1
    #endif
    #ifndef PIN_OUT
    #error "Please indicate output pin"
    #endif /**< Context for the Queued Write module.*/
    
    BLE_ADVERTISING_DEF(m_advertising);
    BLE_NUS_DEF(m_nus, 6);
    NRF_BLE_QWRS_DEF(m_qwr, 6);
    static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifier. */
        {
            {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}};
    
    #define APP_ADV_INTERVAL 64 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */
    
    #define APP_ADV_DURATION 18000
    
    volatile uint8_t button_pressed = 0;
    
    volatile int timer_call = 0;
    FRESULT ff_result;
    static FIL file;
    
    uint16_t heart_rate;
    char s_heartrate[4];
    volatile uint8_t c;
    uint32_t bytes_written;
    const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(1);
    #define SDC_SCK_PIN ARDUINO_13_PIN  ///< SDC serial clock (SCK) pin.
    #define SDC_MOSI_PIN ARDUINO_11_PIN ///< SDC serial data in (DI) pin.
    #define SDC_MISO_PIN ARDUINO_12_PIN ///< SDC serial data out (DO) pin.
    #define SDC_CS_PIN ARDUINO_10_PIN   ///< SDC chip select (CS) pin.
    
    #define MIN_CONN_INTERVAL                    (uint16_t)(MSEC_TO_UNITS(7.5, UNIT_1_25_MS)) 
    #define MAX_CONN_INTERVAL                    (uint16_t)(MSEC_TO_UNITS(7.5, UNIT_1_25_MS))    /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */
    #define SLAVE_LATENCY 0                                      /**< Slave latency. */
    #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)     /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */
    #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
    
    #define DEVICE_NAME "nRF Relay"
    static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;
    #define APP_BLE_CONN_CFG_TAG 1 /**< A tag identifying the SoftDevice BLE configuration. */
    
    #define APP_BLE_OBSERVER_PRIO 3 /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    #define APP_SOC_OBSERVER_PRIO 1 /**< Applications' SoC observer priority. You shouldn't need to modify this value. */
    
    #define LESC_DEBUG_MODE 0 /**< Set to 1 to use LESC debug keys, allows you to use a sniffer to inspect traffic. */
    
    #define SEC_PARAM_BOND 1                               /**< Perform bonding. */
    #define SEC_PARAM_MITM 0                               /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC 1                               /**< LE Secure Connections 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 SCAN_DURATION_WITELIST 3000 /**< Duration of the scanning in units of 10 milliseconds. */
    #define HART_RATE_SERVICE_UUID_IDX 0
    #define TARGET_UUID BLE_UUID_HEART_RATE_SERVICE                /**< Target device uuid that application is looking for. */
    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;
    char init[] = "Long,Lat,Alt,Accelx,Accely,Accelz,GYROx,GYROy,GYROz,Magx,Magy,Magz,Heartrate,Bat %  ";
    char data[] = "2221";
    
    /**@brief Macro to unpack 16bit unsigned UUID from octet stream. */
    #define UUID16_EXTRACT(DST, SRC) \
      do {                           \
        (*(DST)) = (SRC)[1];         \
        (*(DST)) <<= 8;              \
        (*(DST)) |= (SRC)[0];        \
      } while (0)
    
    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_C_DEF(m_hrs_c);                   /**< Structure used to identify the heart rate client module. */
    BLE_BAS_C_DEF(m_bas_c);                   /**< Structure used to identify the Battery Service client module. */
    NRF_BLE_GATT_DEF(m_gatt);                 /**< GATT module instance. */
    BLE_DB_DISCOVERY_ARRAY_DEF(m_db_disc, 2); /**< Database discovery module instances. */
    NRF_BLE_SCAN_DEF(m_scan);                 /**< DB discovery module instance. */
                                              /**< Scanning module instance. */
    
    static uint16_t m_conn_handle;           /**< Current connection handle. */
    static bool m_whitelist_disabled;        /**< True if whitelist has been temporarily disabled. */
    static bool m_memory_access_in_progress; /**< Flag to keep track of ongoing operations on persistent memory. */
    
    void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
      c = 1;
    }
    
    NRF_BLOCK_DEV_SDC_DEFINE(
        m_block_dev_sdc,
        NRF_BLOCK_DEV_SDC_CONFIG(
            SDC_SECTOR_SIZE,
            APP_SDCARD_CONFIG(SDC_MOSI_PIN, SDC_MISO_PIN, SDC_SCK_PIN, SDC_CS_PIN)),
        NFR_BLOCK_DEV_INFO_CONFIG("Nordic", "SDC", "1.00"));
    
    void timer_led_event_handler(nrf_timer_event_t event_type, void *p_context) {
      switch (event_type) {
      case NRF_TIMER_EVENT_COMPARE0:
    
        timer_call = 1;
        break;
    
      default:
        //Do nothing.
        break;
      }
    }
    
    static void fatfs_example() {
    
      static FATFS fs;
      static DIR dir;
      static FILINFO fno;
    
      DSTATUS disk_state = STA_NOINIT;
    
      // Initialize FATFS disk I/O interface by providing the block device.
      static diskio_blkdev_t drives[] =
          {
              DISKIO_BLOCKDEV_CONFIG(NRF_BLOCKDEV_BASE_ADDR(m_block_dev_sdc, block_dev), NULL)};
    
      diskio_blockdev_register(drives, ARRAY_SIZE(drives));
    
      NRF_LOG_INFO("Initializing disk 0 (SDC)...");
      for (uint32_t retries = 3; retries && disk_state; --retries) {
        disk_state = disk_initialize(0);
      }
      if (disk_state) {
        NRF_LOG_INFO("Disk initialization failed.");
        return;
      }
    
      uint32_t blocks_per_mb = (1024uL * 1024uL) / m_block_dev_sdc.block_dev.p_ops->geometry(&m_block_dev_sdc.block_dev)->blk_size;
      uint32_t capacity = m_block_dev_sdc.block_dev.p_ops->geometry(&m_block_dev_sdc.block_dev)->blk_count / blocks_per_mb;
      NRF_LOG_INFO("Capacity: %d MB", capacity);
    
      NRF_LOG_INFO("Mounting volume...");
      ff_result = f_mount(&fs, "", 1);
      if (ff_result) {
        NRF_LOG_INFO("Mount failed.");
        return;
      }
    
      NRF_LOG_INFO("\r\n Listing directory: /");
      ff_result = f_opendir(&dir, "/");
      if (ff_result) {
        NRF_LOG_INFO("Directory listing failed!");
        return;
      }
    
      do {
        ff_result = f_readdir(&dir, &fno);
        if (ff_result != FR_OK) {
          NRF_LOG_INFO("Directory read failed.");
          return;
        }
    
        if (fno.fname[0]) {
          if (fno.fattrib & AM_DIR) {
            NRF_LOG_RAW_INFO("   <DIR>   %s", (uint32_t)fno.fname);
          } else {
            NRF_LOG_RAW_INFO("%9lu  %s", fno.fsize, (uint32_t)fno.fname);
          }
        }
      } while (fno.fname[0]);
      NRF_LOG_RAW_INFO("");
      uint8_t string[15];
      NRF_LOG_INFO(string);
      NRF_LOG_INFO("Writing to file " FILE_NAME "...");
      ff_result = f_open(&file, FILE_NAME, FA_READ);
      if (ff_result != FR_OK) {
        NRF_LOG_INFO("Unable to open or create file: " FILE_NAME ".");
        return;
      }
      uint16_t size;
      size = f_size(&file);
      char *data = NULL;
      data = malloc(size); /* allocate memory to store image data */
    
      NRF_LOG_INFO("File size: %d bytes", size);
      int b = 86;
      for (int a = 85; a < size; a = a + 85) {
        //NRF_LOG_INFO("%d \n",a);
        //NRF_LOG_INFO("%d \n",b);
        f_lseek(&file, b);
        ff_result = f_read(&file, string, 85, (UINT *)&bytes_written);
        if (ff_result != FR_OK) {
          NRF_LOG_INFO("read failed\r\n.");
        } else {
          // NRF_LOG_INFO("%d bytes read.", bytes_written);
        }
        //for(int i=0;i<bytes_written;i++)
        //{
        // NRF_LOG_INFO("%d  ->  %c",i,string[i]);
        //nrf_delay_ms(20);
        //}
        b = b + 85;
      }
      NRF_LOG_INFO("done");
      (void)f_close(&file);
    
      //ff_result = f_write(&file, string, sizeof(string), (UINT *) &bytes_written);
      //if (ff_result != FR_OK)
      //{
      //    NRF_LOG_INFO("Write failed\r\n.");
      //}
      //else
      //{
      //    NRF_LOG_INFO("%d bytes written.", bytes_written);
      //}
    
      //ff_result = f_write(&file, "\n",1,&bytes_written);
      //(void) f_close(&file);
      //for(int a=1 ; a<=600 ; a++)
      //{
      //for (int b=1 ;b<=14;b++)
      //{
      //ff_result = f_write(&file, data, sizeof(data), (UINT *) &bytes_written);
      //if (ff_result != FR_OK)
      //{
      //    NRF_LOG_INFO("Write failed\r\n.");
      //}
      //else
      //{
      //    NRF_LOG_INFO("%d bytes written.", bytes_written);
      //}
      //ff_result = f_write(&file, ",",1,&bytes_written);
      //}
      //ff_result = f_write(&file, "\n",1,&bytes_written);
      //nrf_delay_ms(60);
      //}
    }
    /**< Scan parameters requested for scanning and connection. */
    static ble_gap_scan_params_t const m_scan_param =
        {
            .active = 0x01,
    #if (NRF_SD_BLE_API_VERSION > 7)
            .interval_us = NRF_BLE_SCAN_SCAN_INTERVAL * UNIT_0_625_MS,
            .window_us = NRF_BLE_SCAN_SCAN_WINDOW * UNIT_0_625_MS,
    #else
            .interval = NRF_BLE_SCAN_SCAN_INTERVAL,
            .window = NRF_BLE_SCAN_SCAN_WINDOW,
    #endif
            .filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL,
            .timeout = SCAN_DURATION_WITELIST,
            .scan_phys = BLE_GAP_PHY_1MBPS,
            .extended = true,
    
    };
    
    /**@brief Names which the central applications will scan for, and which will be advertised by the peripherals.
     *  if these are set to empty strings, the UUIDs defined below will be used
     */
    static char const m_target_periph_name[] = ""; /**< If you want to connect to a peripheral using a given advertising name, type its name here. */
    static bool is_connect_per_addr = true;        /**< If you want to connect to a peripheral with a given address, set this to true and put the correct address in the variable below. */
    
    static ble_gap_addr_t const m_target_periph_addr =
        {
            /* Possible values for addr_type:
           BLE_GAP_ADDR_TYPE_PUBLIC,
           BLE_GAP_ADDR_TYPE_RANDOM_STATIC,
           BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE,
           BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE. */
            .addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC,
            .addr = {0x8D, 0xFE, 0x23, 0x86, 0x77, 0xD9}};
    
    static void scan_start(void);
    
    /**@brief Function for asserts in the SoftDevice.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyze
     *          how your product is supposed to react in case of Assert.
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num     Line number of the failing ASSERT call.
     * @param[in] p_file_name  File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t *p_file_name) {
      app_error_handler(0xDEADBEEF, line_num, p_file_name);
    }
    
    /**@brief Function for handling the Heart Rate Service Client and Battery Service Client errors.
     *
     * @param[in]   nrf_error   Error code containing information about what went wrong.
     */
    static void service_error_handler(uint32_t nrf_error) {
      APP_ERROR_HANDLER(nrf_error);
    }
    
    /**@brief Function for handling database discovery events.
     *
     * @details This function is callback function to handle events from the database discovery module.
     *          Depending on the UUIDs that are discovered, this function should forward the events
     *          to their respective services.
     *
     * @param[in] p_event  Pointer to the database discovery event.
     */
    static void db_disc_handler(ble_db_discovery_evt_t *p_evt) {
      ble_hrs_on_db_disc_evt(&m_hrs_c, p_evt);
      ble_bas_on_db_disc_evt(&m_bas_c, p_evt);
    }
    
    /**@brief Function for handling Peer Manager events.
     *
     * @param[in] p_evt  Peer Manager event.
     */
    
    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(BSP_BOARD_LED_0);
    
        // Start advertising.
        err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
        APP_ERROR_CHECK(err_code);
      }
    }
    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:
        // Bonds are deleted. Start scanning.
        adv_scan_start();
        break;
    
      default:
        break;
      }
    }
    
    /**
     * @brief Function for shutdown events.
     *
     * @param[in]   event       Shutdown type.
     */
    static bool shutdown_handler(nrf_pwr_mgmt_evt_t event) {
      ret_code_t err_code;
    
      err_code = bsp_indication_set(BSP_INDICATE_IDLE);
      APP_ERROR_CHECK(err_code);
    
      switch (event) {
      case NRF_PWR_MGMT_EVT_PREPARE_WAKEUP:
        // Prepare wakeup buttons.
        err_code = bsp_btn_ble_sleep_mode_prepare();
        APP_ERROR_CHECK(err_code);
        break;
      default:
        break;
      }
    
      return true;
    }
    
    NRF_PWR_MGMT_HANDLER_REGISTER(shutdown_handler, APP_SHUTDOWN_HANDLER_PRIORITY);
    
    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    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;
        }
      }
    }
    volatile uint8_t peristat=0;
    static void on_ble_peripheral_evt(ble_evt_t const *p_ble_evt) {
      uint32_t err_code;
    
      switch (p_ble_evt->header.evt_id) {
      case BLE_GAP_EVT_CONNECTED:
    
        NRF_LOG_INFO("peripheral Connected");
        nrf_delay_ms(20);
        peristat=1;
        err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
        APP_ERROR_CHECK(err_code);
    
        m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
        multi_qwr_conn_handle_assign(p_ble_evt->evt.gap_evt.conn_handle);
        APP_ERROR_CHECK(err_code);
        break;
    
      case BLE_GAP_EVT_DISCONNECTED:
        NRF_LOG_INFO("Disconnected");
        peristat=0;
        // LED indication will be changed when advertising starts.
        m_conn_handle = BLE_CONN_HANDLE_INVALID;
        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_2MBPS | BLE_GAP_PHY_1MBPS | BLE_GAP_PHY_CODED,
                .tx_phys = BLE_GAP_PHY_2MBPS | BLE_GAP_PHY_1MBPS | BLE_GAP_PHY_CODED,
            };
        err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
        APP_ERROR_CHECK(err_code);
      } break;
    
      case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
        // Pairing not supported
        err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
        APP_ERROR_CHECK(err_code);
        break;
    
      case BLE_GATTS_EVT_SYS_ATTR_MISSING:
        // No system attributes have been stored.
        err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
        APP_ERROR_CHECK(err_code);
        break;
    
      case BLE_GATTC_EVT_TIMEOUT:
        // Disconnect on GATT Client timeout event.
        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.
        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;
      }
    }
    
    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_disc[0], p_gap_evt->conn_handle);
          if (err_code == NRF_ERROR_BUSY) {
            err_code = ble_db_discovery_start(&m_db_disc[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.
        bsp_board_led_on(BSP_BOARD_LED_1);
        if (ble_conn_state_central_conn_count() == NRF_SDH_BLE_CENTRAL_LINK_COUNT) {
          bsp_board_led_off(BSP_BOARD_LED_0);
        } else {
          // Resume scanning.
          bsp_board_led_on(BSP_BOARD_LED_0);
          scan_start();
        }
      } 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 ((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(BSP_BOARD_LED_0);
        }
    
        if (ble_conn_state_central_conn_count() == 0) {
          bsp_board_led_off(BSP_BOARD_LED_1);
        }
      } 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 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);
    }
    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_nus_on_ble_evt(p_ble_evt, &m_nus);
        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_nus_on_ble_evt(p_ble_evt,&m_nus);
        on_ble_central_evt(p_ble_evt);
      }
    }
    
    /**@brief SoftDevice SoC event handler.
     *
     * @param[in]   evt_id      SoC event.
     * @param[in]   p_context   Context.
     */
    static void soc_evt_handler(uint32_t evt_id, void *p_context) {
      switch (evt_id) {
      case NRF_EVT_FLASH_OPERATION_SUCCESS:
        /* fall through */
      case NRF_EVT_FLASH_OPERATION_ERROR:
    
        if (m_memory_access_in_progress) {
          m_memory_access_in_progress = false;
          scan_start();
        }
        break;
    
      default:
        // No implementation needed.
        break;
      }
    }
    /**@brief Function for initializing the BLE stack.
     *
     * @details Initializes the SoftDevice and the BLE event interrupt.
     */
    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);
      NRF_LOG_INFO("%d", err_code);
      APP_ERROR_CHECK(err_code);
    
      // Register handlers for BLE and SoC events.
      NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
      NRF_SDH_SOC_OBSERVER(m_soc_observer, APP_SOC_OBSERVER_PRIO, soc_evt_handler, NULL);
    }
    
    /**@brief Function for the Peer Manager initialization.
     */
    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 bonding 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 disabling the use of whitelist for scanning.
     */
    static void whitelist_disable(void) {
      if (!m_whitelist_disabled) {
        NRF_LOG_INFO("Whitelist temporarily disabled.");
        m_whitelist_disabled = true;
        nrf_ble_scan_stop();
        scan_start();
      }
    }
    
    /**@brief Function for handling events from the BSP module.
     *
     * @param[in]   event   Event generated by button press.
     */
    void bsp_event_handler(bsp_event_t event) {
      ret_code_t err_code;
    
      switch (event) {
      case BSP_EVENT_SLEEP:
        nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
        break;
    
      case BSP_EVENT_DISCONNECT:
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
        if (err_code != NRF_ERROR_INVALID_STATE) {
          APP_ERROR_CHECK(err_code);
        }
        break;
    
      case BSP_EVENT_WHITELIST_OFF:
        whitelist_disable();
        break;
    
      default:
        break;
      }
    }
    
    /**@brief Heart Rate Collector Handler.
     */
    static void hrs_c_evt_handler(ble_hrs_c_t *p_hrs_c, ble_hrs_c_evt_t *p_hrs_c_evt) {
      NRF_LOG_INFO("hrs_c_evt_handler");
      ret_code_t err_code;
    
      switch (p_hrs_c_evt->evt_type) {
      case BLE_HRS_C_EVT_DISCOVERY_COMPLETE: {
        NRF_LOG_DEBUG("Heart rate service discovered.");
    
        err_code = ble_hrs_c_handles_assign(p_hrs_c,
            p_hrs_c_evt->conn_handle,
            &p_hrs_c_evt->params.peer_db);
        APP_ERROR_CHECK(err_code);
    
        // Initiate bonding.
        err_code = pm_conn_secure(p_hrs_c_evt->conn_handle, 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);
      } break;
    
      case BLE_HRS_C_EVT_HRM_NOTIFICATION: {
        NRF_LOG_INFO("Heart Rate = %d.", p_hrs_c_evt->params.hrm.hr_value);
        heart_rate = p_hrs_c_evt->params.hrm.hr_value;
    
        if (p_hrs_c_evt->params.hrm.rr_intervals_cnt != 0) {
          uint32_t rr_avg = 0;
          for (uint32_t i = 0; i < p_hrs_c_evt->params.hrm.rr_intervals_cnt; i++) {
            rr_avg += p_hrs_c_evt->params.hrm.rr_intervals[i];
          }
          rr_avg = rr_avg / p_hrs_c_evt->params.hrm.rr_intervals_cnt;
          NRF_LOG_DEBUG("rr_interval (avg) = %d.", rr_avg);
        }
      } break;
    
      default:
        break;
      }
    }
    
    /**@brief Battery level Collector Handler.
     */
    static void bas_c_evt_handler(ble_bas_c_t *p_bas_c, ble_bas_c_evt_t *p_bas_c_evt) {
      NRF_LOG_INFO("bas_c_evt_handler");
      ret_code_t err_code;
    
      switch (p_bas_c_evt->evt_type) {
      case BLE_BAS_C_EVT_DISCOVERY_COMPLETE: {
        err_code = ble_bas_c_handles_assign(p_bas_c,
            p_bas_c_evt->conn_handle,
            &p_bas_c_evt->params.bas_db);
        APP_ERROR_CHECK(err_code);
    
        // Battery service discovered. Enable notification of Battery Level.
        NRF_LOG_DEBUG("Battery Service discovered. Reading battery level.");
    
        err_code = ble_bas_c_bl_read(p_bas_c);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEBUG("Enabling Battery Level Notification.");
        err_code = ble_bas_c_bl_notif_enable(p_bas_c);
        APP_ERROR_CHECK(err_code);
    
      } break;
    
      case BLE_BAS_C_EVT_BATT_NOTIFICATION:
        NRF_LOG_INFO("Battery Level received %d %%.", p_bas_c_evt->params.battery_level);
        break;
    
      case BLE_BAS_C_EVT_BATT_READ_RESP:
        NRF_LOG_INFO("Battery Level Read as %d %%.", p_bas_c_evt->params.battery_level);
        break;
    
      default:
        break;
      }
    }
    
    /**
     * @brief Heart rate collector initialization.
     */
    static void hrs_c_init(void) {
      NRF_LOG_INFO("HRS_C_INIT");
      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 = service_error_handler;
      hrs_c_init_obj.p_gatt_queue = &m_ble_gatt_queue;
    
      ret_code_t err_code = ble_hrs_c_init(&m_hrs_c, &hrs_c_init_obj);
      APP_ERROR_CHECK(err_code);
    }
    
    /**
     * @brief Battery level collector initialization.
     */
    static void bas_c_init(void) {
      NRF_LOG_INFO("BAS_C_INIT");
      ble_bas_c_init_t bas_c_init_obj;
    
      bas_c_init_obj.evt_handler = bas_c_evt_handler;
      bas_c_init_obj.error_handler = service_error_handler;
      bas_c_init_obj.p_gatt_queue = &m_ble_gatt_queue;
    
      ret_code_t err_code = ble_bas_c_init(&m_bas_c, &bas_c_init_obj);
      APP_ERROR_CHECK(err_code);
    }
    
    /**
     * @brief Database discovery collector initialization.
     */
    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 Retrieve a list of peer manager peer IDs.
     *
     * @param[inout] p_peers   The buffer where to store the list of peer IDs.
     * @param[inout] p_size    In: The size of the @p p_peers buffer.
     *                         Out: The number of peers copied in the buffer.
     */
    static void peer_list_get(pm_peer_id_t *p_peers, uint32_t *p_size) {
      pm_peer_id_t peer_id;
      uint32_t peers_to_copy;
    
      peers_to_copy = (*p_size < BLE_GAP_WHITELIST_ADDR_MAX_COUNT) ? *p_size : BLE_GAP_WHITELIST_ADDR_MAX_COUNT;
    
      peer_id = pm_next_peer_id_get(PM_PEER_ID_INVALID);
      *p_size = 0;
    
      while ((peer_id != PM_PEER_ID_INVALID) && (peers_to_copy--)) {
        p_peers[(*p_size)++] = peer_id;
        peer_id = pm_next_peer_id_get(peer_id);
      }
    }
    
    /**@brief Function to start scanning.
     */
    static void scan_start(void) {
      ret_code_t err_code;
    
      if (nrf_fstorage_is_busy(NULL)) {
        m_memory_access_in_progress = true;
        return;
      }
    
      NRF_LOG_INFO("Starting scan.");
    
      err_code = nrf_ble_scan_start(&m_scan);
      APP_ERROR_CHECK(err_code);
    
      err_code = bsp_indication_set(BSP_INDICATE_SCANNING);
      NRF_LOG_INFO("%d", err_code);
      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, 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 nrf log module.
     */
    static void log_init(void) {
      ret_code_t err_code = NRF_LOG_INIT(NULL);
      APP_ERROR_CHECK(err_code);
    
      NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    /**@brief Function for initializing the power management module. */
    static void power_management_init(void) {
      ret_code_t err_code;
      err_code = nrf_pwr_mgmt_init();
      APP_ERROR_CHECK(err_code);
    }
    
    /**@brief GATT module event handler.
     */
    static void gatt_evt_handler(nrf_ble_gatt_t *p_gatt, nrf_ble_gatt_evt_t const *p_evt) {
    
      if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED)) {
        m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
        NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);
      }
      NRF_LOG_DEBUG("ATT MTU exchange completed. central 0x%x peripheral 0x%x",
          p_gatt->att_mtu_desired_central,
          p_gatt->att_mtu_desired_periph);
      switch (p_evt->evt_id) {
      case NRF_BLE_GATT_EVT_ATT_MTU_UPDATED: {
        NRF_LOG_INFO("GATT ATT MTU on connection 0x%x changed to %d.",
            p_evt->conn_handle,
            p_evt->params.att_mtu_effective);
      } break;
    
      case NRF_BLE_GATT_EVT_DATA_LENGTH_UPDATED: {
        NRF_LOG_INFO("Data length for connection 0x%x updated to %d.",
            p_evt->conn_handle,
            p_evt->params.data_length);
      } break;
    
      default:
        break;
      }
    }
    static void whitelist_load()
    {
        ret_code_t   ret;
        pm_peer_id_t peers[8];
        uint32_t     peer_cnt;
    
        memset(peers, PM_PEER_ID_INVALID, sizeof(peers));
        peer_cnt = (sizeof(peers) / sizeof(pm_peer_id_t));
    
        // Load all peers from flash and whitelist them.
        peer_list_get(peers, &peer_cnt);
    
        ret = pm_whitelist_set(peers, peer_cnt);
        APP_ERROR_CHECK(ret);
    
        // Setup the device identies list.
        // Some SoftDevices do not support this feature.
        ret = pm_device_identities_list_set(peers, peer_cnt);
        if (ret != NRF_ERROR_NOT_SUPPORTED)
        {
            APP_ERROR_CHECK(ret);
        }
    }
    static void on_whitelist_req(void)
    {
        ret_code_t err_code;
    
        // Whitelist buffers.
        ble_gap_addr_t whitelist_addrs[8];
        ble_gap_irk_t  whitelist_irks[8];
    
        memset(whitelist_addrs, 0x00, sizeof(whitelist_addrs));
        memset(whitelist_irks,  0x00, sizeof(whitelist_irks));
    
        uint32_t addr_cnt = (sizeof(whitelist_addrs) / sizeof(ble_gap_addr_t));
        uint32_t irk_cnt  = (sizeof(whitelist_irks)  / sizeof(ble_gap_irk_t));
    
        // Reload the whitelist and whitelist all peers.
        whitelist_load();
    
        // Get the whitelist previously set using pm_whitelist_set().
        err_code = pm_whitelist_get(whitelist_addrs, &addr_cnt,
                                    whitelist_irks,  &irk_cnt);
    
        if (((addr_cnt == 0) && (irk_cnt == 0)) ||
            (m_whitelist_disabled))
        {
            // Don't use whitelist.
            err_code = nrf_ble_scan_params_set(&m_scan, NULL);
            APP_ERROR_CHECK(err_code);
        }
    }
    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_WHITELIST_REQUEST:
            {
                on_whitelist_req();
                m_whitelist_disabled = false;
            } break;
    
            case NRF_BLE_SCAN_EVT_CONNECTING_ERROR:
            {
                err_code = p_scan_evt->params.connecting_err.err_code;
                APP_ERROR_CHECK(err_code);
            } break;
    
            case NRF_BLE_SCAN_EVT_SCAN_TIMEOUT:
            {
                NRF_LOG_INFO("Scan timed out.");
                scan_start();
            } break;
    
            case NRF_BLE_SCAN_EVT_FILTER_MATCH:
                break;
            case NRF_BLE_SCAN_EVT_WHITELIST_ADV_REPORT:
                break;
    
            default:
              break;
        }
    }
    
    /**@brief Function for initializing the timer.
     */
    static void timer_init(void) {
      ret_code_t err_code = app_timer_init();
      APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for initializing the GATT module.
     */
    static void gatt_init(void) {
      ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
      APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for initialization scanning and setting 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;
      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);
    
      ble_uuid_t uuid =
          {
              .uuid = TARGET_UUID,
              .type = BLE_UUID_TYPE_BLE,
          };
    
      err_code = nrf_ble_scan_filter_set(&m_scan,
          SCAN_UUID_FILTER,
          &uuid);
      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 (is_connect_per_addr) {
        err_code = nrf_ble_scan_filter_set(&m_scan,
            SCAN_ADDR_FILTER,
            m_target_periph_addr.addr);
        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 handling the idle state (main loop).
     *
     * @details Handle any pending log operation(s), then sleep 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);
    
      NRF_LOG_FLUSH();
      nrf_pwr_mgmt_run();
    }
    
    /**@brief Function for starting a scan, or instead trigger it from peer manager (after
     *        deleting bonds).
     *
     * @param[in] p_erase_bonds Pointer to a bool to determine if bonds will be deleted before scanning.
     */
    void scanning_start(bool *p_erase_bonds) {
      // Start scanning for peripherals and initiate connection
      // with devices that advertise GATT Service UUID.
      if (*p_erase_bonds == true) {
        // Scan is started by the PM_EVT_PEERS_DELETE_SUCCEEDED event.
        delete_bonds();
      } else {
        scan_start();
      }
    }
    
    static void gpio_init(void) {
      ret_code_t err_code;
      NRF_LOG_INFO("INIT gpio");
    
      nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
    
      err_code = nrf_drv_gpiote_out_init(PIN_OUT, &out_config);
      APP_ERROR_CHECK(err_code);
    
      nrf_drv_gpiote_in_event_enable(PIN_IN, true);
    }
    
    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);
    }
    static void on_conn_params_evt(ble_conn_params_evt_t *p_evt) {
      uint32_t err_code;
    
      if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) {
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
      }
    }
    
    static void conn_params_error_handler(uint32_t nrf_error) {
      APP_ERROR_HANDLER(nrf_error);
    }
    
    static void conn_params_init(void) {
      uint32_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;
      cp_init.disconnect_on_fail = false;
      cp_init.evt_handler = on_conn_params_evt;
      cp_init.error_handler = conn_params_error_handler;
    
      err_code = ble_conn_params_init(&cp_init);
      APP_ERROR_CHECK(err_code);
    }
    
    void uart_event_handle(app_uart_evt_t *p_event) {
      static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
      static uint8_t index = 0;
      uint32_t err_code;
    
      switch (p_event->evt_type) {
      case APP_UART_DATA_READY:
        NRF_LOG_INFO("uart event handle");
        UNUSED_VARIABLE(app_uart_get(&data_array[index]));
        index++;
    
        if ((data_array[index - 1] == '\n') ||
            (data_array[index - 1] == '\r') ||
            (index >= m_ble_nus_max_data_len)) {
          if (index > 1) {
            NRF_LOG_INFO("uart send handle");
            NRF_LOG_DEBUG("Ready to send data over BLE NUS");
            NRF_LOG_HEXDUMP_DEBUG(data_array, index);
    
            do {
              uint16_t length = (uint16_t)index;
              err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
              NRF_LOG_INFO("%d", err_code);
    
              if ((err_code != NRF_ERROR_INVALID_STATE) &&
                  (err_code != NRF_ERROR_RESOURCES) &&
                  (err_code != NRF_ERROR_NOT_FOUND)) {
                APP_ERROR_CHECK(err_code);
              }
            } while (err_code == NRF_ERROR_RESOURCES);
          }
    
          index = 0;
        }
        break;
    
      case APP_UART_COMMUNICATION_ERROR:
        APP_ERROR_HANDLER(p_event->data.error_communication);
        break;
    
      case APP_UART_FIFO_ERROR:
        APP_ERROR_HANDLER(p_event->data.error_code);
        break;
    
      default:
        break;
      }
    }
    /**@snippet [Handling the data received over UART] */
    
    #define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */
    #define UART_RX_BUF_SIZE 256
    /**@brief  Function for initializing the UART module.
     */
    /**@snippet [UART Initialization] */
    static void uart_init(void) {
      uint32_t err_code;
      app_uart_comm_params_t const comm_params =
      {
        .rx_pin_no = RX_PIN_NUMBER,
        .tx_pin_no = TX_PIN_NUMBER,
        .rts_pin_no = RTS_PIN_NUMBER,
        .cts_pin_no = CTS_PIN_NUMBER,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity = false,
    #if defined(UART_PRESENT)
        .baud_rate = NRF_UART_BAUDRATE_115200
    #else
        .baud_rate = NRF_UARTE_BAUDRATE_115200
    #endif
      };
    
      APP_UART_FIFO_INIT(&comm_params,
          UART_RX_BUF_SIZE,
          UART_TX_BUF_SIZE,
          uart_event_handle,
          APP_IRQ_PRIORITY_LOWEST,
          err_code);
      APP_ERROR_CHECK(err_code);
    }
    static void nrf_qwr_error_handler(uint32_t nrf_error) {
      APP_ERROR_HANDLER(nrf_error);
    }
    static void nus_data_handler(ble_nus_evt_t *p_evt) {
    
      if (p_evt->type == BLE_NUS_EVT_RX_DATA) {
        uint32_t err_code;
        nrf_delay_ms(10);
        NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
        NRF_LOG_INFO("rom BLE NUS. ");
        NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
    
        for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++) {
          NRF_LOG_INFO("1 ");
          do {
            err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
            if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY)) {
              NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
              APP_ERROR_CHECK(err_code);
            }
          } while (err_code == NRF_ERROR_BUSY);
        }
        if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r') {
          while (app_uart_put('\n') == NRF_ERROR_BUSY)
            ;
        }
      }
    }
    static void sleep_mode_enter(void) {
      uint32_t err_code = bsp_indication_set(BSP_INDICATE_IDLE);
      APP_ERROR_CHECK(err_code);
    
      // Prepare wakeup buttons.
      err_code = bsp_btn_ble_sleep_mode_prepare();
      APP_ERROR_CHECK(err_code);
    
      // Go to system-off mode (this function will not return; wakeup will cause a reset).
      err_code = sd_power_system_off();
      APP_ERROR_CHECK(err_code);
    }
    static void services_init(void) {
      uint32_t err_code;
      ble_nus_init_t nus_init;
      nrf_ble_qwr_init_t qwr_init = {0};
    
      // Initialize Queued Write Module.
      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 NUS.
      memset(&nus_init, 0, sizeof(nus_init));
    
      nus_init.data_handler = nus_data_handler;
    
      err_code = ble_nus_init(&m_nus, &nus_init);
      APP_ERROR_CHECK(err_code);
    }
    static void on_adv_evt(ble_adv_evt_t ble_adv_evt) {
      uint32_t err_code;
    
      switch (ble_adv_evt) {
      case BLE_ADV_EVT_FAST:
        err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
        APP_ERROR_CHECK(err_code);
        break;
      case BLE_ADV_EVT_IDLE:
        sleep_mode_enter();
        break;
      default:
        break;
      }
    }
    
    static void advertising_init(void) {
      uint32_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 = false;
      init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
    
      init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
      init.srdata.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);
    }
    
    static void advertising_start(void) {
      uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
      APP_ERROR_CHECK(err_code);
    }
    uint8_t gyrox=0;
    uint8_t gyroy=0;
    uint8_t gyroz=0;
    uint8_t accelx=1,accely=1,accelz=1;
    uint8_t magx=2,magy=2,magz=2;
    uint8_t counter=0;
    static uint8_t data_array2[135];
    
    int main(void) {
      bool erase_bonds;
      uint32_t time_ms = 10; //Time(in miliseconds) between consecutive compare events.
      uint32_t time_ticks;
      
      uint8_t gyrox=0;
      uint8_t gyroy=1;
      uint8_t gyroz=2;
      uint8_t accelx=3,accely=4,accelz=5;
      uint8_t magx=2,magy=2,magz=2;
      uint8_t counter=0;
      uint8_t lat=6,lon=7,alt=8;
      static uint8_t data_array2[135];
      
    
      uint32_t err_code = NRF_SUCCESS;
       uart_init();
      log_init();
    
      timer_init();
      power_management_init();
      buttons_leds_init(&erase_bonds);
      ble_stack_init();
      gatt_init();
      peer_manager_init();
      db_discovery_init();
      hrs_c_init();
      bas_c_init();
      scan_init();
      gap_params_init();
      services_init();
      advertising_init();
      conn_params_init();
      nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
      err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler);
      APP_ERROR_CHECK(err_code);
      time_ticks = nrf_drv_timer_ms_to_ticks(&TIMER_LED, time_ms);
      nrf_drv_timer_extended_compare(&TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
      nrf_drv_timer_enable(&TIMER_LED);
      gpio_init();
      
    
      // advertising_start();
      //// Start execution.
      NRF_LOG_INFO("Heart Rate collector example started.");
    
      // fatfs_example();
    
      //NRF_LOG_INFO("1");
      //Enter main loop.
      int first_row = 1;
      if (erase_bonds == true) {
        // Scanning and advertising is done upon PM_EVT_PEERS_DELETE_SUCCEEDED event.
        delete_bonds();
      } else {
        adv_scan_start();
      }
      nrf_delay_ms(5000);
      // Enter main loop.
      for (;;) {
        idle_state_handle();
        if(peristat==1)
        {
        //for live data
        if(timer_call==1)
        {
         if(counter<200)
            {
                       if(BLE_GATTS_EVT_SYS_ATTR_MISSING)
                       {
                       err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0,0);
                       APP_ERROR_CHECK(err_code);
                       }
    
    
                        NRF_LOG_INFO("%d",counter);
                        sprintf(data_array2,"{\"g_x\":%d,\"g_y\":%d,\"g_z\":%d,\"a_x\":%d,\"a_y\":%d,\"a_z\":%d,\"m_x\":%d,\"m_y\":%d,\"m_z\":%d,\"lat\":%d,\"lon\":%d,\"alt\":%d,\"hb\":%d}",gyrox,gyroy,gyroz,accelx,accely,accelz,magx,magy,magz,lat,lon,alt,heart_rate);
                        NRF_LOG_DEBUG("Ready to send data over BLE NUS");
                        NRF_LOG_HEXDUMP_DEBUG(data_array2, 1);
    
                        do
                        {
                            uint16_t length = (uint16_t)135;
                            err_code = ble_nus_data_send(&m_nus, data_array2, &length, m_conn_handle);
                           
                            
                            
                            if ((err_code != NRF_ERROR_INVALID_STATE) &&
                                (err_code != NRF_ERROR_BUSY) &&
                                (err_code != NRF_ERROR_NOT_FOUND))
                            {
                              NRF_LOG_INFO("%d",err_code);
                                
                               APP_ERROR_CHECK(err_code);
                            }
                        } while (err_code == NRF_ERROR_BUSY);
                        //index = 0;
                        nrf_delay_ms(100);
                        NRF_LOG_INFO("done");
                          gyrox++;
                          gyroy++;
                          gyroz++;
                          accelx++;
                          accely++;
                          accelz++;
                          magx++;
                          magy++;
                          magz++;
                          counter++;
                          lon++;
                          lat++;
                          alt++;
                          }
        }
        }
    
    }
    
      //while(1)
      //{
    
      //if(button_pressed)
      //{
    
      //if(timer_call==1)
      //{
      ////write code
      //ff_result = f_open(&file, FILE_NAME, FA_READ | FA_WRITE | FA_OPEN_APPEND);
      //if (ff_result != FR_OK)
      //{
      //    NRF_LOG_INFO("Unable to open or create file: " FILE_NAME ".");
      //}
      //if(first_row==1)
      //{
      //ff_result = f_write(&file, init , sizeof(init), (UINT *) &bytes_written);
      //if (ff_result != FR_OK)
      //{
      //    NRF_LOG_INFO("read failed\r\n.");
      //}
      //ff_result = f_write(&file, "\n",1,&bytes_written);
      //first_row=0;
      //}
      // for (int b=1 ;b<=14;b++)
      //{
      //if(b==13)
      //{
      //itoa(heart_rate,s_heartrate,10);
      //ff_result = f_write(&file, s_heartrate, sizeof(s_heartrate), (UINT *) &bytes_written);
      //}
      //else
      //{
      //ff_result = f_write(&file, data, sizeof(data), (UINT *) &bytes_written);
    
      //if (ff_result != FR_OK)
      //{
      //    NRF_LOG_INFO("Write failed\r\n.");
      //}
      //else
      //{
      //   // NRF_LOG_INFO("%d bytes written.", bytes_written);
      //}
      //}
      //ff_result = f_write(&file, ",",1,&bytes_written);
      //}
      //ff_result = f_write(&file, "\n",1,&bytes_written);
      //timer_call=0;
      //(void) f_close(&file);
      ////nrf_delay_ms(100);
      //}
    
      //   //idle_state_handle();
      //   //bsp_board_led_invert(3);
      //}
      //}// if(button_pressed)
    
      // uint32_t time_ms = 100; //Time(in miliseconds) between consecutive compare events.
      //uint32_t time_ticks ;
      // uint32_t err_code = NRF_SUCCESS;
      //bsp_board_init(BSP_INIT_LEDS);
      // uint32_t time_ms2 ;
    
      // nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    
      // NRF_LOG_INFO("FATFS example started.");
    
      // fatfs_example();
    
      // err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler);
      // APP_ERROR_CHECK(err_code);
    }

    So basically thats my code sorry for last one  . In this code when i try to test the same code it works well but when i try to reduce timer delay or nrf_Delay on 1601 line then its giving me error 

    <error> app: ERROR 19 [NRF_ERROR_RESOURCES] at D:\nrf5\nRF5_SDK_17.0.2_d674dde\examples\ble_central\ble_app_hrs_c\main.c:1597
    PC at: 0x0002DB39

    Yes i have successfully include nus service in ble_hrs_c . 

    Basically i want to send data with good data rate but when i try to change delay then its giving me error . 

  • Mohsin khan said:
    Yes i have successfully include nus service in ble_hrs_c . 

    Thank you for confirming this.

    Mohsin khan said:
    Basically i want to send data with good data rate but when i try to change delay then its giving me error . 

    ble_nus_data_send propagates the error codes returned by sd_ble_gatts_hvx, which returns NRF_ERROR_RESOURCES if you attempt to queue more notifications than you have configured the queue to hold.

    Relevant exempts from the documentation reads:

    NRF_ERROR_RESOURCES Too many notifications queued. Wait for a BLE_GATTS_EVT_HVN_TX_COMPLETE event and retry.

    The number of Handle Value Notifications that can be queued is configured by ble_gatts_conn_cfg_t::hvn_tx_queue_size When the queue is full, the function call will return NRF_ERROR_RESOURCES. A BLE_GATTS_EVT_HVN_TX_COMPLETE event will be issued as soon as the transmission of the notification is complete.

    To alleviate this you could try to increase your HVN queue size, and/or make sure to queue notifications in accordance with the count variable of the BLE_GATTS_EVT_HVN_TX_COMPLETE event.
    If the NRF_ERROR_RESOURCES is returned after a while of normal function this often means that there is a gradual buildup of unsent notifications over time - i.e that you are queueing notifications faster than you are sending them.
    This could either be due to too frequent queueing by the application, or poor signal strength / noise in the environment causing packets to have to be resent in a later connection event.

    Best regards,
    Karl

    Best regards,
    Karl

  • Hello ,

    How to increase HVN queue size as i have not find any declaration of ble_gatts_conn_cfg_t just definition of ble_gatts_conn_cfg_t in ble_gatts.h . I am little confused about it . 

  • The default queue size is configured by BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT.
    You may change this to increase or decrease the HVN queue size.

    Best regards,
    Karl

  • I have increased it to 10 but it was still giving me error no 19 . 

Reply Children
No Data
Related