BLE HID协议规范是以USB HID协议规范为蓝本的,可以认为是USB HID的无线方式。那么作为人机交互设备(HID)。

可用于下面产品:
蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄、蓝牙自拍杆、靠近自动开锁等。

对于Android、Iso系统的手机,手机BLE会主动连接已配对的支持BLE HID服务协议的设备。

  1. 蓝牙自拍杆其实就是用的音量键作为快门快捷键的特性才产生作用的。比如如果不在拍照界面,点击蓝牙拍照按钮,会把调节音量的进度条调出来。这可以证明蓝牙自拍杆是操作音量键起到效果的。(需要在相机设置界面,将音量键设置为拍照)
定义了 report size 为 1 即 1 bit 。report count 为 1,即 1 个 1bit。

因为 report size 和 report count 为 global 项目,应用到 usage 中,也就是 report 3 中的 8个 usage 都是 1bit,8 个即凑成了 1 个字

节。每个位表示一个功能,所以实际传送 1 字节,通过设置其中的位就能实现不同的控制了。

bit 0 (0x01) :开始暂停

bit 1 (0x02 ) :一键启动应用

bit 2 (0x04 ) :下一首

bit 3 (0x08 ) :上一首

bit 4 (0x10 ) :音量 -

bit 5 (0x20 ) :音量 +

bit 6 (0x40 ) :应用控制

bit 7 (0x80 ) :返回键
void media_player_control(uint8_t control)
{
uint32_t err_code;
uint8_t buffer[1];
buffer[0] = control;
err_code = ble_hids_inp_rep_send( &m_hids, INPUT_REP_MPLAYER_INDEX, 1, buffer );

if(( err_code != NRF_SUCCESS ) &&
( err_code != NRF_ERROR_INVALID_STATE ) &&
( err_code != BLE_ERROR_NO_TX_PACKETS ) &&
( err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING ))
{
APP_ERROR_HANDLER( err_code );
}

buffer[0] = 0;
err_code = ble_hids_inp_rep_send( &m_hids, INPUT_REP_MPLAYER_INDEX, 1, buffer );
if(( err_code != NRF_SUCCESS ) &&
( err_code != NRF_ERROR_INVALID_STATE ) &&
( err_code != BLE_ERROR_NO_TX_PACKETS ) &&
( err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING ))
{
APP_ERROR_HANDLER( err_code );
}
}

//PS: 需要再发送一个0,作用类似于释放按键。
//如果不发送这个“释放”,安卓系统有的是没问题,但苹果系统中,音量+发送后相当于一直按住音量+键。
//所以发送这个“释放”控制就能达到按一次只增加一次音量的作用。

[嵌入式方案][nrf51822] BLE HID+NUS的组合_物联网


  1. 无需APP,自动回连手机
    对于Android、Iso系统的手机,手机BLE会主动连接已配对的支持BLE HID服务协议的设备,检测到BLE连接状态,则开启继电器,蓝牙断开,则关闭继电器。
  2. HID+NUS组合例子
/**
* Copyright (c) 2014 - 2017, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/** @file
*
* @defgroup ble_sdk_app_hids_mouse_main main.c
* @{
* @ingroup ble_sdk_app_hids_mouse
* @brief HID Mouse Sample Application main file.
*
* This file contains is the source code for a sample application using the HID, Battery and Device
* Information Service for implementing a simple mouse functionality. This application uses the
* @ref app_scheduler.
*
* Also it would accept pairing requests from any peer device. This implementation of the
* application will not know whether a connected central is a known device or not.
*/

#include <stdint.h>
#include <string.h>
#include "nordic_common.h"
#include "nrf.h"
#include "nrf_soc.h"
#include "nrf_sdm.h"
#include "app_error.h"
#include "nrf_gpio.h"
#include "ble.h"
#include "ble_hci.h"
#include "ble_srv_common.h"
#include "ble_advdata.h"
#include "ble_hids.h"
#include "ble_bas.h"
#include "ble_dis.h"
#include "ble_conn_params.h"
#include "bsp.h"
#include "sensorsim.h"
#include "bsp_btn_ble.h"
#include "app_scheduler.h"
#include "softdevice_handler_appsh.h"
#include "app_timer_appsh.h"
#include "peer_manager.h"
#include "app_button.h"
#include "ble_advertising.h"
#include "fds.h"
#include "fstorage.h"
#include "ble_conn_state.h"

#define NRF_LOG_MODULE_NAME "APP"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"

#include "ble_nus.h"

#if BUTTONS_NUMBER < 4
#error "Not enough resources on board to run example"
#endif

#if (NRF_SD_BLE_API_VERSION == 3)
#define NRF_BLE_MAX_MTU_SIZE GATT_MTU_SIZE_DEFAULT /**< MTU size used in the softdevice enabling and to reply to a BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST event. */
#endif

#define IS_SRVC_CHANGED_CHARACT_PRESENT 0 /**< Include or not the service_changed characteristic. if not enabled, the server's database cannot be changed for the lifetime of the device*/

#define CENTRAL_LINK_COUNT 0 /**< Number of central links used by the application. When changing this number remember to adjust the RAM settings*/
#define PERIPHERAL_LINK_COUNT 1 /**< Number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/

#define DEVICE_NAME "Nordic_Mouse123" /**< Name of device. Will be included in the advertising data. */
#define MANUFACTURER_NAME "NordicSemiconductor" /**< Manufacturer. Will be passed to Device Information Service. */

#define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */
#define APP_TIMER_OP_QUEUE_SIZE 4 /**< Size of timer operation queues. */

#define BATTERY_LEVEL_MEAS_INTERVAL APP_TIMER_TICKS(2000, APP_TIMER_PRESCALER) /**< Battery level measurement interval (ticks). */
#define MIN_BATTERY_LEVEL 81 /**< Minimum simulated battery level. */
#define MAX_BATTERY_LEVEL 100 /**< Maximum simulated battery level. */
#define BATTERY_LEVEL_INCREMENT 1 /**< Increment between each simulated battery level measurement. */

#define PNP_ID_VENDOR_ID_SOURCE 0x02 /**< Vendor ID Source. */
#define PNP_ID_VENDOR_ID 0x1915 /**< Vendor ID. */
#define PNP_ID_PRODUCT_ID 0xEEEE /**< Product ID. */
#define PNP_ID_PRODUCT_VERSION 0x0001 /**< Product Version. */

/*lint -emacro(524, MIN_CONN_INTERVAL) // Loss of precision */
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) /**< Minimum connection interval (7.5 ms). */
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_1_25_MS) /**< Maximum connection interval (15 ms). */
#define SLAVE_LATENCY 20 /**< Slave latency. */
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(3000, UNIT_10_MS) /**< Connection supervisory timeout (3000 ms). */

#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000, APP_TIMER_PRESCALER) /**< 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, APP_TIMER_PRESCALER) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAM_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */

#define SEC_PARAM_BOND 1 /**< Perform bonding. */
#define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */
#define SEC_PARAM_LESC 0 /**< LE Secure Connections not enabled. */
#define SEC_PARAM_KEYPRESS 0 /**< Keypress notifications not enabled. */
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */
#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */

#define MOVEMENT_SPEED 5 /**< Number of pixels by which the cursor is moved each time a button is pushed. */
#define INPUT_REPORT_COUNT 1 /**< Number of input reports in this application. */
#define INPUT_REP_BUTTONS_LEN 3 /**< Length of Mouse Input Report containing button data. */
#define INPUT_REP_MOVEMENT_LEN 3 /**< Length of Mouse Input Report containing movement data. */
#define INPUT_REP_MEDIA_PLAYER_LEN 1 /**< Length of Mouse Input Report containing media player data. */
#define INPUT_REP_BUTTONS_INDEX 0 /**< Index of Mouse Input Report containing button data. */
#define INPUT_REP_MOVEMENT_INDEX 1 /**< Index of Mouse Input Report containing movement data. */
#define INPUT_REP_MPLAYER_INDEX 2 /**< Index of Mouse Input Report containing media player data. */
#define INPUT_REP_REF_BUTTONS_ID 1 /**< Id of reference to Mouse Input Report containing button data. */
#define INPUT_REP_REF_MOVEMENT_ID 2 /**< Id of reference to Mouse Input Report containing movement data. */
#define INPUT_REP_REF_MPLAYER_ID 3 /**< Id of reference to Mouse Input Report containing media player data. */

#define BASE_USB_HID_SPEC_VERSION 0x0101 /**< Version number of base USB HID Specification implemented by this application. */

#define SCHED_MAX_EVENT_DATA_SIZE MAX(APP_TIMER_SCHED_EVT_SIZE, \
BLE_STACK_HANDLER_SCHED_EVT_SIZE) /**< Maximum size of scheduler events. */
#ifdef SVCALL_AS_NORMAL_FUNCTION
#define SCHED_QUEUE_SIZE 20 /**< Maximum number of events in the scheduler queue. More is needed in case of Serialization. */
#else
#define SCHED_QUEUE_SIZE 10 /**< Maximum number of events in the scheduler queue. */
#endif

#define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

#define APP_FEATURE_NOT_SUPPORTED BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 /**< Reply when unsupported features are requested. */

#define APP_ADV_FAST_INTERVAL 0x0028 /**< Fast advertising interval (in units of 0.625 ms. This value corresponds to 25 ms.). */
#define APP_ADV_SLOW_INTERVAL 0x0C80 /**< Slow advertising interval (in units of 0.625 ms. This value corrsponds to 2 seconds). */

#define APP_ADV_FAST_TIMEOUT 30 /**< The duration of the fast advertising period (in seconds). */
#define APP_ADV_SLOW_TIMEOUT 180 /**< The duration of the slow advertising period (in seconds). */

static ble_hids_t m_hids; /**< Structure used to identify the HID service. */
static ble_bas_t m_bas; /**< Structure used to identify the battery service. */
static bool m_in_boot_mode = false; /**< Current protocol mode. */
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */

static sensorsim_cfg_t m_battery_sim_cfg; /**< Battery Level sensor simulator configuration. */
static sensorsim_state_t m_battery_sim_state; /**< Battery Level sensor simulator state. */

APP_TIMER_DEF(m_battery_timer_id); /**< Battery timer. */

static pm_peer_id_t m_peer_id; /**< Device reference handle to the current bonded central. */

static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_NUS_SERVICE, BLE_UUID_TYPE_BLE},
{BLE_UUID_HUMAN_INTERFACE_DEVICE_SERVICE, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers. */

static pm_peer_id_t m_whitelist_peers[BLE_GAP_WHITELIST_ADDR_MAX_COUNT]; /**< List of peers currently in the whitelist. */
static uint32_t m_whitelist_peer_cnt; /**< Number of peers currently in the whitelist. */
static bool m_is_wl_changed; /**< Indicates if the whitelist has been changed since last time it has been updated in the Peer Manager. */

static void on_hids_evt(ble_hids_t * p_hids, ble_hids_evt_t * p_evt);

static ble_nus_t m_nus; /**< Structure to identify the Nordic UART Service. */
/**@brief Callback 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] 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(DEAD_BEEF, line_num, p_file_name);
}


/**@brief Fetch the 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 for starting advertising.
*/
static void advertising_start(void)
{
ret_code_t ret;

memset(m_whitelist_peers, PM_PEER_ID_INVALID, sizeof(m_whitelist_peers));
m_whitelist_peer_cnt = (sizeof(m_whitelist_peers) / sizeof(pm_peer_id_t));

peer_list_get(m_whitelist_peers, &m_whitelist_peer_cnt);

ret = pm_whitelist_set(m_whitelist_peers, m_whitelist_peer_cnt);
APP_ERROR_CHECK(ret);

// Setup the device identies list.
// Some SoftDevices do not support this feature.
ret = pm_device_identities_list_set(m_whitelist_peers, m_whitelist_peer_cnt);
if (ret != NRF_ERROR_NOT_SUPPORTED)
{
APP_ERROR_CHECK(ret);
}

m_is_wl_changed = false;

ret = ble_advertising_start(BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(ret);
}


/**@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)
{
ret_code_t err_code;

switch (p_evt->evt_id)
{
case PM_EVT_BONDED_PEER_CONNECTED:
{
NRF_LOG_INFO("Connected to a previously bonded device.\r\n");
} break;

case PM_EVT_CONN_SEC_SUCCEEDED:
{
NRF_LOG_INFO("Connection secured. Role: %d. conn_handle: %d, Procedure: %d\r\n",
ble_conn_state_role(p_evt->conn_handle),
p_evt->conn_handle,
p_evt->params.conn_sec_succeeded.procedure);

m_peer_id = p_evt->peer_id;

// Note: You should check on what kind of white list policy your application should use.
if (p_evt->params.conn_sec_succeeded.procedure == PM_LINK_SECURED_PROCEDURE_BONDING)
{
NRF_LOG_INFO("New Bond, add the peer to the whitelist if possible\r\n");
NRF_LOG_INFO("\tm_whitelist_peer_cnt %d, MAX_PEERS_WLIST %d\r\n",
m_whitelist_peer_cnt + 1,
BLE_GAP_WHITELIST_ADDR_MAX_COUNT);

if (m_whitelist_peer_cnt < BLE_GAP_WHITELIST_ADDR_MAX_COUNT)
{
// Bonded to a new peer, add it to the whitelist.
m_whitelist_peers[m_whitelist_peer_cnt++] = m_peer_id;
m_is_wl_changed = true;
}
}
} break;

case PM_EVT_CONN_SEC_FAILED:
{
/* Often, when securing fails, it shouldn't be restarted, for security reasons.
* Other times, it can be restarted directly.
* Sometimes it can be restarted, but only after changing some Security Parameters.
* Sometimes, it cannot be restarted until the link is disconnected and reconnected.
* Sometimes it is impossible, to secure the link, or the peer device does not support it.
* How to handle this error is highly application dependent. */
} break;

case PM_EVT_CONN_SEC_CONFIG_REQ:
{
// Reject pairing request from an already bonded peer.
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
} break;

case PM_EVT_STORAGE_FULL:
{
// Run garbage collection on the flash.
err_code = fds_gc();
if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
{
// Retry.
}
else
{
APP_ERROR_CHECK(err_code);
}
} break;

case PM_EVT_PEERS_DELETE_SUCCEEDED:
{
advertising_start();
} break;

case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
{
// The local database has likely changed, send service changed indications.
pm_local_database_has_changed();
} break;

case PM_EVT_PEER_DATA_UPDATE_FAILED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error);
} break;

case PM_EVT_PEER_DELETE_FAILED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);
} break;

case PM_EVT_PEERS_DELETE_FAILED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);
} break;

case PM_EVT_ERROR_UNEXPECTED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.error_unexpected.error);
} break;

case PM_EVT_CONN_SEC_START:
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
case PM_EVT_PEER_DELETE_SUCCEEDED:
case PM_EVT_LOCAL_DB_CACHE_APPLIED:
case PM_EVT_SERVICE_CHANGED_IND_SENT:
case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
default:
break;
}
}


/**@brief Function for handling Service errors.
*
* @details A pointer to this function will be passed to each service which may need to inform the
* application about an error.
*
* @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 advertising errors.
*
* @param[in] nrf_error Error code containing information about what went wrong.
*/
static void ble_advertising_error_handler(uint32_t nrf_error)
{
APP_ERROR_HANDLER(nrf_error);
}


/**@brief Function for performing a battery measurement, and update the Battery Level characteristic in the Battery Service.
*/
static void battery_level_update(void)
{
// uint32_t err_code;
// uint8_t battery_level;

// battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg);

// err_code = ble_bas_battery_level_update(&m_bas, battery_level);
// NRF_LOG_INFO("battery_level:%d\r\n", battery_level);
// if ((err_code != NRF_SUCCESS) &&
// (err_code != NRF_ERROR_INVALID_STATE) &&
// (err_code != BLE_ERROR_NO_TX_PACKETS) &&
// (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
// )
// {
// APP_ERROR_HANDLER(err_code);
// }
}


/**@brief Function for handling the Battery measurement timer timeout.
*
* @details This function will be called each time the battery level measurement timer expires.
*
* @param[in] p_context Pointer used for passing some arbitrary information (context) from the
* app_start_timer() call to the timeout handler.
*/
static void battery_level_meas_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
// battery_level_update();
}


/**@brief Function for the Timer initialization.
*
* @details Initializes the timer module.
*/
static void timers_init(void)
{
uint32_t err_code;

// Initialize timer module.
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);

// Create battery timer.
err_code = app_timer_create(&m_battery_timer_id,
APP_TIMER_MODE_REPEATED,
battery_level_meas_timeout_handler);
APP_ERROR_CHECK(err_code);
}


/**@brief Function for the GAP initialization.
*
* @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)
{
uint32_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);

err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HID_MOUSE);
APP_ERROR_CHECK(err_code);

memset(&gap_conn_params, 0, sizeof(gap_conn_params));

gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
gap_conn_params.slave_latency = SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;

err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing Device Information Service.
*/
static void dis_init(void)
{
// uint32_t err_code;
// ble_dis_init_t dis_init_obj;
// ble_dis_pnp_id_t pnp_id;

// pnp_id.vendor_id_source = PNP_ID_VENDOR_ID_SOURCE;
// pnp_id.vendor_id = PNP_ID_VENDOR_ID;
// pnp_id.product_id = PNP_ID_PRODUCT_ID;
// pnp_id.product_version = PNP_ID_PRODUCT_VERSION;

// memset(&dis_init_obj, 0, sizeof(dis_init_obj));

// ble_srv_ascii_to_utf8(&dis_init_obj.manufact_name_str, MANUFACTURER_NAME);
// dis_init_obj.p_pnp_id = &pnp_id;

// BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&dis_init_obj.dis_attr_md.read_perm);
// BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&dis_init_obj.dis_attr_md.write_perm);

// err_code = ble_dis_init(&dis_init_obj);
// APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing Battery Service.
*/
static void bas_init(void)
{
// uint32_t err_code;
// ble_bas_init_t bas_init_obj;

// memset(&bas_init_obj, 0, sizeof(bas_init_obj));

// bas_init_obj.evt_handler = NULL;
// bas_init_obj.support_notification = true;
// bas_init_obj.p_report_ref = NULL;
// bas_init_obj.initial_batt_level = 100;

// BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&bas_init_obj.battery_level_char_attr_md.cccd_write_perm);
// BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&bas_init_obj.battery_level_char_attr_md.read_perm);
// BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&bas_init_obj.battery_level_char_attr_md.write_perm);

// BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&bas_init_obj.battery_level_report_read_perm);

// err_code = ble_bas_init(&m_bas, &bas_init_obj);
// APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing HID Service.
*/
static void hids_init(void)
{
uint32_t err_code;
ble_hids_init_t hids_init_obj;
ble_hids_inp_rep_init_t inp_rep_array[INPUT_REPORT_COUNT];
ble_hids_inp_rep_init_t * p_input_report;
uint8_t hid_info_flags;

static uint8_t rep_map_data[] =
{
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)

0xA1, 0x01, // Collection (Application)

// Report ID 1: Mouse buttons + scroll/pan
0x85, 0x01, // Report Id 1
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x95, 0x05, // Report Count (1)
0x75, 0x01, // Report Size (1)
0x05, 0x09, // Usage Page (Buttons)
0x19, 0x01, // Usage Minimum (01)
0x29, 0x05, // Usage Maximum (05)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x81, 0x01, // Input (Constant) for padding
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x38, // Usage (Wheel)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x81, 0x06, // Input (Data, Variable, Relative)
0x05, 0x0C, // Usage Page (Consumer)
0x0A, 0x38, 0x02, // Usage (AC Pan)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0xC0, // End Collection (Physical)

// Report ID 2: Mouse motion
0x85, 0x02, // Report Id 2
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x75, 0x0C, // Report Size (12)
0x95, 0x02, // Report Count (2)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x16, 0x01, 0xF8, // Logical maximum (2047)
0x26, 0xFF, 0x07, // Logical minimum (-2047)
0x81, 0x06, // Input (Data, Variable, Relative)
0xC0, // End Collection (Physical)
0xC0, // End Collection (Application)

// Report ID 3: Advanced buttons
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report Id (3)
0x15, 0x00, // Logical minimum (0)
0x25, 0x01, // Logical maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x01, // Report Count (1)

0x09, 0xCD, // Usage (Play/Pause)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x0A, 0x83, 0x01, // Usage (AL Consumer Control Configuration)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x09, 0xB5, // Usage (Scan Next Track)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x09, 0xB6, // Usage (Scan Previous Track)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)

0x09, 0xEA, // Usage (Volume Down)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x09, 0xE9, // Usage (Volume Up)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x0A, 0x25, 0x02, // Usage (AC Forward)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x0A, 0x24, 0x02, // Usage (AC Back)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0xC0 // End Collection
};

memset(inp_rep_array, 0, sizeof(inp_rep_array));
// Initialize HID Service.
p_input_report = &inp_rep_array[INPUT_REP_BUTTONS_INDEX];
p_input_report->max_len = INPUT_REP_BUTTONS_LEN;
p_input_report->rep_ref.report_id = INPUT_REP_REF_BUTTONS_ID;
p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;

BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.write_perm);

p_input_report = &inp_rep_array[INPUT_REP_MOVEMENT_INDEX];
p_input_report->max_len = INPUT_REP_MOVEMENT_LEN;
p_input_report->rep_ref.report_id = INPUT_REP_REF_MOVEMENT_ID;
p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;

BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.write_perm);

p_input_report = &inp_rep_array[INPUT_REP_MPLAYER_INDEX];
p_input_report->max_len = INPUT_REP_MEDIA_PLAYER_LEN;
p_input_report->rep_ref.report_id = INPUT_REP_REF_MPLAYER_ID;
p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;

BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.write_perm);

hid_info_flags = HID_INFO_FLAG_REMOTE_WAKE_MSK | HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK;

memset(&hids_init_obj, 0, sizeof(hids_init_obj));

hids_init_obj.evt_handler = on_hids_evt;
hids_init_obj.error_handler = service_error_handler;
hids_init_obj.is_kb = false;
hids_init_obj.is_mouse = true;
hids_init_obj.inp_rep_count = INPUT_REPORT_COUNT;
hids_init_obj.p_inp_rep_array = inp_rep_array;
hids_init_obj.outp_rep_count = 0;
hids_init_obj.p_outp_rep_array = NULL;
hids_init_obj.feature_rep_count = 0;
hids_init_obj.p_feature_rep_array = NULL;
hids_init_obj.rep_map.data_len = sizeof(rep_map_data);
hids_init_obj.rep_map.p_data = rep_map_data;
hids_init_obj.hid_information.bcd_hid = BASE_USB_HID_SPEC_VERSION;
hids_init_obj.hid_information.b_country_code = 0;
hids_init_obj.hid_information.flags = hid_info_flags;
hids_init_obj.included_services_count = 0;
hids_init_obj.p_included_services_array = NULL;

BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.rep_map.security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.rep_map.security_mode.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.hid_information.security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.hid_information.security_mode.write_perm);

BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(
&hids_init_obj.security_mode_boot_mouse_inp_rep.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(
&hids_init_obj.security_mode_boot_mouse_inp_rep.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(
&hids_init_obj.security_mode_boot_mouse_inp_rep.write_perm);

BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_protocol.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_protocol.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.security_mode_ctrl_point.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_ctrl_point.write_perm);

err_code = ble_hids_init(&m_hids, &hids_init_obj);
APP_ERROR_CHECK(err_code);
}

/**@brief Function for handling the data from the Nordic UART Service.
*
* @details This function will process the data received from the Nordic UART BLE Service and send
* it to the UART module.
*
* @param[in] p_nus Nordic UART Service structure.
* @param[in] p_data Data to be send to UART module.
* @param[in] length Length of the data.
*/
/**@snippet [Handling the data received over BLE] */
static void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length)
{
// for (uint32_t i = 0; i < length; i++)
// {
// while (app_uart_put(p_data[i]) != NRF_SUCCESS);
// }
// while (app_uart_put('\r') != NRF_SUCCESS);
// while (app_uart_put('\n') != NRF_SUCCESS);
}
/**@brief Function for initializing services that will be used by the application.
*/
static void services_init(void)
{
uint32_t err_code;
ble_nus_init_t nus_init;

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);
// dis_init();
// bas_init();
hids_init();

}


/**@brief Function for initializing the battery sensor simulator.
*/
static void sensor_simulator_init(void)
{
m_battery_sim_cfg.min = MIN_BATTERY_LEVEL;
m_battery_sim_cfg.max = MAX_BATTERY_LEVEL;
m_battery_sim_cfg.incr = BATTERY_LEVEL_INCREMENT;
m_battery_sim_cfg.start_at_max = true;

sensorsim_init(&m_battery_sim_state, &m_battery_sim_cfg);
}


/**@brief Function for handling a Connection Parameters error.
*
* @param[in] nrf_error Error code containing information about what went wrong.
*/
static void conn_params_error_handler(uint32_t nrf_error)
{
APP_ERROR_HANDLER(nrf_error);
}


/**@brief Function for initializing the Connection Parameters module.
*/
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_PARAM_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail = false;
cp_init.evt_handler = NULL;
cp_init.error_handler = conn_params_error_handler;

err_code = ble_conn_params_init(&cp_init);
APP_ERROR_CHECK(err_code);
}


/**@brief Function for starting timers.
*/
static void timers_start(void)
{
uint32_t err_code;

err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
}


/**@brief Function for putting the chip into sleep mode.
*
* @note This function will not return.
*/
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);
}


/**@brief Function for handling HID events.
*
* @details This function will be called for all HID events which are passed to the application.
*
* @param[in] p_hids HID service structure.
* @param[in] p_evt Event received from the HID service.
*/
static void on_hids_evt(ble_hids_t * p_hids, ble_hids_evt_t * p_evt)
{
switch (p_evt->evt_type)
{
case BLE_HIDS_EVT_BOOT_MODE_ENTERED:
m_in_boot_mode = true;
break;

case BLE_HIDS_EVT_REPORT_MODE_ENTERED:
m_in_boot_mode = false;
break;

case BLE_HIDS_EVT_NOTIF_ENABLED:
break;

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


/**@brief Function for handling advertising events.
*
* @details This function will be called for advertising events which are passed to the application.
*
* @param[in] ble_adv_evt Advertising event.
*/
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
uint32_t err_code;

switch (ble_adv_evt)
{
case BLE_ADV_EVT_DIRECTED:
NRF_LOG_INFO("BLE_ADV_EVT_DIRECTED\r\n");
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_DIRECTED);
APP_ERROR_CHECK(err_code);
break;

case BLE_ADV_EVT_FAST:
NRF_LOG_INFO("BLE_ADV_EVT_FAST\r\n");
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
APP_ERROR_CHECK(err_code);
break;

case BLE_ADV_EVT_SLOW:
NRF_LOG_INFO("BLE_ADV_EVT_SLOW\r\n");
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_SLOW);
APP_ERROR_CHECK(err_code);
break;

case BLE_ADV_EVT_FAST_WHITELIST:
NRF_LOG_INFO("BLE_ADV_EVT_FAST_WHITELIST\r\n");
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_WHITELIST);
APP_ERROR_CHECK(err_code);
break;

case BLE_ADV_EVT_SLOW_WHITELIST:
NRF_LOG_INFO("BLE_ADV_EVT_SLOW_WHITELIST\r\n");
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_WHITELIST);
APP_ERROR_CHECK(err_code);
err_code = ble_advertising_restart_without_whitelist();
APP_ERROR_CHECK(err_code);
break;

case BLE_ADV_EVT_IDLE:
err_code = bsp_indication_set(BSP_INDICATE_IDLE);
APP_ERROR_CHECK(err_code);
sleep_mode_enter();
break;

case BLE_ADV_EVT_WHITELIST_REQUEST:
{
ble_gap_addr_t whitelist_addrs[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
ble_gap_irk_t whitelist_irks[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
uint32_t addr_cnt = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;
uint32_t irk_cnt = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;

err_code = pm_whitelist_get(whitelist_addrs, &addr_cnt,
whitelist_irks, &irk_cnt);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEBUG("pm_whitelist_get returns %d addr in whitelist and %d irk whitelist\r\n",
addr_cnt,
irk_cnt);

// Apply the whitelist.
err_code = ble_advertising_whitelist_reply(whitelist_addrs, addr_cnt,
whitelist_irks, irk_cnt);
APP_ERROR_CHECK(err_code);
}
break;

case BLE_ADV_EVT_PEER_ADDR_REQUEST:
{
pm_peer_data_bonding_t peer_bonding_data;

// Only Give peer address if we have a handle to the bonded peer.
if (m_peer_id != PM_PEER_ID_INVALID)
{

err_code = pm_peer_data_bonding_load(m_peer_id, &peer_bonding_data);
if (err_code != NRF_ERROR_NOT_FOUND)
{
APP_ERROR_CHECK(err_code);

ble_gap_addr_t * p_peer_addr = &(peer_bonding_data.peer_ble_id.id_addr_info);
err_code = ble_advertising_peer_addr_reply(p_peer_addr);
APP_ERROR_CHECK(err_code);
}

}
break;
}

default:
break;
}
}


/**@brief Function for handling the Application's BLE Stack events.
*
* @param[in] p_ble_evt Bluetooth stack event.
*/
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
uint32_t err_code;

switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected\r\n");
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
APP_ERROR_CHECK(err_code);

m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
break; // BLE_GAP_EVT_CONNECTED

case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected\r\n");
err_code = bsp_indication_set(BSP_INDICATE_IDLE);
APP_ERROR_CHECK(err_code);

m_conn_handle = BLE_CONN_HANDLE_INVALID;

if (m_is_wl_changed)
{
// The whitelist has been modified, update it in the Peer Manager.
err_code = pm_whitelist_set(m_whitelist_peers, m_whitelist_peer_cnt);
APP_ERROR_CHECK(err_code);

err_code = pm_device_identities_list_set(m_whitelist_peers, m_whitelist_peer_cnt);
if (err_code != NRF_ERROR_NOT_SUPPORTED)
{
APP_ERROR_CHECK(err_code);
}

m_is_wl_changed = false;
}
break; // BLE_GAP_EVT_DISCONNECTED

case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
NRF_LOG_DEBUG("GATT Client Timeout.\r\n");
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; // BLE_GATTC_EVT_TIMEOUT

case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server timeout event.
NRF_LOG_DEBUG("GATT Server Timeout.\r\n");
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; // BLE_GATTS_EVT_TIMEOUT

case BLE_EVT_USER_MEM_REQUEST:
err_code = sd_ble_user_mem_reply(m_conn_handle, NULL);
APP_ERROR_CHECK(err_code);
break; // BLE_EVT_USER_MEM_REQUEST

case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
{
ble_gatts_evt_rw_authorize_request_t req;
ble_gatts_rw_authorize_reply_params_t auth_reply;

req = p_ble_evt->evt.gatts_evt.params.authorize_request;

if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
{
if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ) ||
(req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
(req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
{
if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
{
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
}
else
{
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
}
auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
&auth_reply);
APP_ERROR_CHECK(err_code);
}
}
} break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST

#if (NRF_SD_BLE_API_VERSION == 3)
case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
err_code = sd_ble_gatts_exchange_mtu_reply(p_ble_evt->evt.gatts_evt.conn_handle,
NRF_BLE_MAX_MTU_SIZE);
APP_ERROR_CHECK(err_code);
break; // BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST
#endif

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


/**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
*
* @details This function is called from the scheduler in the main loop after a BLE stack
* event has been received.
*
* @param[in] p_ble_evt Bluetooth stack event.
*/
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
/** The Connection state module has to be fed BLE events in order to function correctly
* Remember to call ble_conn_state_on_ble_evt before calling any ble_conns_state_* functions. */
ble_conn_state_on_ble_evt(p_ble_evt);
pm_on_ble_evt(p_ble_evt);
bsp_btn_ble_on_ble_evt(p_ble_evt);
on_ble_evt(p_ble_evt);
ble_advertising_on_ble_evt(p_ble_evt);
ble_conn_params_on_ble_evt(p_ble_evt);
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
ble_hids_on_ble_evt(&m_hids, p_ble_evt);
// ble_bas_on_ble_evt(&m_bas, p_ble_evt);
NRF_LOG_INFO("ble_evt_dispatch!\r\n");
}


/**@brief Function for dispatching a system event to interested modules.
*
* @details This function is called from the System event interrupt handler after a system
* event has been received.
*
* @param[in] sys_evt System stack event.
*/
static void sys_evt_dispatch(uint32_t sys_evt)
{
// Dispatch the system event to the fstorage module, where it will be
// dispatched to the Flash Data Storage (FDS) module.
fs_sys_event_handler(sys_evt);

// Dispatch to the Advertising module last, since it will check if there are any
// pending flash operations in fstorage. Let fstorage process system events first,
// so that it can report correctly to the Advertising module.
ble_advertising_on_sys_evt(sys_evt);
}


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

nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

// Initialize the SoftDevice handler module.
SOFTDEVICE_HANDLER_APPSH_INIT(&clock_lf_cfg, true);

ble_enable_params_t ble_enable_params;
err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
PERIPHERAL_LINK_COUNT,
&ble_enable_params);
APP_ERROR_CHECK(err_code);

// Check the ram settings against the used number of links
CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT, PERIPHERAL_LINK_COUNT);

// Enable BLE stack.
#if (NRF_SD_BLE_API_VERSION == 3)
ble_enable_params.gatt_enable_params.att_mtu = NRF_BLE_MAX_MTU_SIZE;
#endif
err_code = softdevice_enable(&ble_enable_params);
APP_ERROR_CHECK(err_code);

// Register with the SoftDevice handler module for BLE events.
err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
APP_ERROR_CHECK(err_code);

// Register with the SoftDevice handler module for BLE events.
err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
APP_ERROR_CHECK(err_code);
}


/**@brief Function for the Peer Manager initialization.
*
* @param[in] erase_bonds Indicates whether bonding information should be cleared from
* persistent storage during initialization of the Peer Manager.
*/
static void peer_manager_init(bool erase_bonds)
{
ble_gap_sec_params_t sec_param;
ret_code_t err_code;

err_code = pm_init();
APP_ERROR_CHECK(err_code);

if (erase_bonds)
{
err_code = pm_peers_delete();
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 Function for initializing the Advertising functionality.
*/
static void advertising_init(void)
{
uint32_t err_code;
uint8_t adv_flags;
ble_advdata_t advdata;
ble_adv_modes_config_t options;

// Build and set advertising data
memset(&advdata, 0, sizeof(advdata));

adv_flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
advdata.name_type = BLE_ADVDATA_FULL_NAME;
advdata.include_appearance = true;
advdata.flags = adv_flags;
advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
advdata.uuids_complete.p_uuids = m_adv_uuids;

memset(&options, 0, sizeof(options));
options.ble_adv_whitelist_enabled = true;
options.ble_adv_directed_enabled = true;
options.ble_adv_directed_slow_enabled = false;
options.ble_adv_directed_slow_interval = 0;
options.ble_adv_directed_slow_timeout = 0;
options.ble_adv_fast_enabled = true;
options.ble_adv_fast_interval = APP_ADV_FAST_INTERVAL;
options.ble_adv_fast_timeout = APP_ADV_FAST_TIMEOUT;
options.ble_adv_slow_enabled = true;
options.ble_adv_slow_interval = APP_ADV_SLOW_INTERVAL;
options.ble_adv_slow_timeout = APP_ADV_SLOW_TIMEOUT;

err_code = ble_advertising_init(&advdata,
NULL,
&options,
on_adv_evt,
ble_advertising_error_handler);
APP_ERROR_CHECK(err_code);
}


/**@brief Function for the Event Scheduler initialization.
*/
static void scheduler_init(void)
{
APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}


/**@brief Function for sending a Mouse Movement.
*
* @param[in] x_delta Horizontal movement.
* @param[in] y_delta Vertical movement.
*/
static void mouse_movement_send(int16_t x_delta, int16_t y_delta)
{
// uint32_t err_code;

// if (m_in_boot_mode)
// {
// x_delta = MIN(x_delta, 0x00ff);
// y_delta = MIN(y_delta, 0x00ff);

// err_code = ble_hids_boot_mouse_inp_rep_send(&m_hids,
// 0x00,
// (int8_t)x_delta,
// (int8_t)y_delta,
// 0,
// NULL);
// }
// else
// {
// uint8_t buffer[INPUT_REP_MOVEMENT_LEN];

// APP_ERROR_CHECK_BOOL(INPUT_REP_MOVEMENT_LEN == 3);

// x_delta = MIN(x_delta, 0x0fff);
// y_delta = MIN(y_delta, 0x0fff);

// buffer[0] = x_delta & 0x00ff;
// buffer[1] = ((y_delta & 0x000f) << 4) | ((x_delta & 0x0f00) >> 8);
// buffer[2] = (y_delta & 0x0ff0) >> 4;

// err_code = ble_hids_inp_rep_send(&m_hids,
// INPUT_REP_MOVEMENT_INDEX,
// INPUT_REP_MOVEMENT_LEN,
// buffer);
// }

// if ((err_code != NRF_SUCCESS) &&
// (err_code != NRF_ERROR_INVALID_STATE) &&
// (err_code != BLE_ERROR_NO_TX_PACKETS) &&
// (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
// )
// {
// APP_ERROR_HANDLER(err_code);
// }
}


/**@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)
{
uint32_t err_code;

switch (event)
{
case BSP_EVENT_SLEEP:
// sleep_mode_enter();
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:
if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
{
err_code = ble_advertising_restart_without_whitelist();
if (err_code != NRF_ERROR_INVALID_STATE)
{
APP_ERROR_CHECK(err_code);
}
}
break;

// case BSP_EVENT_KEY_0:
// if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
// {
// mouse_movement_send(-MOVEMENT_SPEED, 0);
// }
// break;

// case BSP_EVENT_KEY_1:
// if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
// {
// mouse_movement_send(0, -MOVEMENT_SPEED);
// }
// break;

// case BSP_EVENT_KEY_2:
// if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
// {
// mouse_movement_send(MOVEMENT_SPEED, 0);
// }
// break;

// case BSP_EVENT_KEY_3:
// if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
// {
// mouse_movement_send(0, MOVEMENT_SPEED);
// }
// break;

default:
break;
}
}


/**@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)
{
bsp_event_t startup_event;

uint32_t err_code = bsp_init(BSP_INIT_LED | BSP_INIT_BUTTONS,
APP_TIMER_TICKS(100, APP_TIMER_PRESCALER),
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 the Power manager.
*/
static void power_manage(void)
{
// uint32_t err_code = sd_app_evt_wait();

// APP_ERROR_CHECK(err_code);
}


/**@brief Function for application main entry.
*/
int main(void)
{
bool erase_bonds;
uint32_t err_code;

// Initialize.
err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);

timers_init();
buttons_leds_init(&erase_bonds);
ble_stack_init();
scheduler_init();
peer_manager_init(erase_bonds);
if (erase_bonds == true)
{
NRF_LOG_INFO("Bonds erased!\r\n");
}
gap_params_init();
advertising_init();
services_init();
// sensor_simulator_init();
conn_params_init();

// Start execution.
NRF_LOG_INFO("HID Mouse Start!\r\n");
timers_start();
advertising_start();

// Enter main loop.
for (;;)
{
app_sched_execute();
if (NRF_LOG_PROCESS() == false)
{
power_manage();
}
}
}


/**
* @}
*/

​​工程例子​​