- 基于NRF52832 SDK15.3 uart工程实现BLE的三种绑定功能
- 无密码方式绑定
- 动态密码方式绑定
- 静态密码方式绑定
为了解决手机绑定后,设备如果再次复位造成双方保存的信息不一致,导致绑定的设备无法连接,使用内部flash将绑定成功能信息保存起来,上电复位后从flash中恢复配置信息
如何实现上述三种方式绑定呢?看下Nordic API定义的IO能力,如果实现无密码绑定,则要将IO能力选择为BLE_GAP_IO_CAPS_NONE
/**@defgroup BLE_GAP_IO_CAPS GAP IO Capabilities
* @{ */
#define BLE_GAP_IO_CAPS_DISPLAY_ONLY 0x00 /**< Display Only. */
#define BLE_GAP_IO_CAPS_DISPLAY_YESNO 0x01 /**< Display and Yes/No entry. */
#define BLE_GAP_IO_CAPS_KEYBOARD_ONLY 0x02 /**< Keyboard Only. */
#define BLE_GAP_IO_CAPS_NONE 0x03 /**< No I/O capabilities. */
#define BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY 0x04 /**< Keyboard and Display. */
/**@} */
绑定安全参数定义
/**@brief GAP security parameters. */
typedef struct
{
uint8_t bond : 1; /**< Perform bonding. */
uint8_t mitm : 1; /**< Enable Man In The Middle protection. */
uint8_t lesc : 1; /**< Enable LE Secure Connection pairing. */
uint8_t keypress : 1; /**< Enable generation of keypress notifications. */
uint8_t io_caps : 3; /**< IO capabilities, see @ref BLE_GAP_IO_CAPS. */
uint8_t oob : 1; /**< The OOB data flag.
- In LE legacy pairing, this flag is set if a device has out of band authentication data.
The OOB method is used if both of the devices have out of band authentication data.
- In LE Secure Connections pairing, this flag is set if a device has the peer device's out of band authentication data.
The OOB method is used if at least one device has the peer device's OOB data available. */
uint8_t min_key_size; /**< Minimum encryption key size in octets between 7 and 16. If 0 then not applicable in this instance. */
uint8_t max_key_size; /**< Maximum encryption key size in octets between min_key_size and 16. */
ble_gap_sec_kdist_t kdist_own; /**< Key distribution bitmap: keys that the local device will distribute. */
ble_gap_sec_kdist_t kdist_peer; /**< Key distribution bitmap: keys that the remote device will distribute. */
} ble_gap_sec_params_t;
先定义几个变量来保存相关的绑定信息
static ble_gap_sec_params_t m_sec_params;
static ble_gap_sec_keyset_t m_sec_keyset;
static ble_gap_enc_key_t m_own_enc_key;
static ble_gap_enc_key_t m_peer_enc_key;
static ble_gap_sec_params_t sec_params_auth;
static uint8_t PasswordOk;
绑定安全参数初时化
static void sec_params_init()
{
memset(&m_sec_params, 0, sizeof(ble_gap_sec_params_t));
m_sec_params.bond = SEC_PARAM_BOND;
m_sec_params.mitm = SEC_PARAM_MITM;
m_sec_params.io_caps = BLE_GAP_IO_CAPS_NONE;
m_sec_params.oob = SEC_PARAM_OOB;
m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
m_sec_params.kdist_own.enc = 1;
m_sec_params.kdist_own.id = 0;
m_sec_params.kdist_own.sign = 0;
m_sec_params.kdist_peer.enc = 1;
m_sec_params.kdist_peer.id = 0;
m_sec_params.kdist_peer.sign = 0;
m_sec_keyset.keys_own.p_enc_key = &m_own_enc_key;
m_sec_keyset.keys_own.p_id_key = NULL;
m_sec_keyset.keys_own.p_pk = NULL;
m_sec_keyset.keys_own.p_sign_key = NULL;
m_sec_keyset.keys_peer.p_enc_key = &m_peer_enc_key;
m_sec_keyset.keys_peer.p_id_key = NULL;
m_sec_keyset.keys_peer.p_pk = NULL;
m_sec_keyset.keys_peer.p_sign_key = NULL;
}
请求绑定函数实现
static void requestBondToCenterBle(void)
{
sec_params_auth.bond = 0;
sec_params_auth.mitm = 0;
sd_ble_gap_authenticate(m_conn_handle, &sec_params_auth);//向中心设备发送配对请求
}
- 在ble_evt_handler回调函数的BLE_GAP_EVT_CONNECTED蓝牙连接事件中触发绑定请求
- 在BLE_GAP_EVT_SEC_PARAMS_REQUEST事件中交换绑定参数信息
- 绑定成功或失败都会触发BLE_GAP_EVT_AUTH_STATUS事件
- 绑定成功后,手机再次连接设备会触发BLE_GAP_EVT_SEC_INFO_REQUEST事件
- 如果有密码请求,会触发BLE_GAP_EVT_PASSKEY_DISPLAY事件,此事件中能够产生密码信息
- 将绑定成功中的ble_gap_enc_key_t信息存入内部flash
BLE_GAP_EVT_CONNECTED事件中调用requestBondToCenterBle函数
step 2:
BLE_GAP_EVT_PHY_UPDATE_REQUEST事件中,调用绑定初时化函数,及参数回复主机函数
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
sec_params_init();
err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &m_sec_params, &m_sec_keyset);
APP_ERROR_CHECK(err_code);
break;
参数回复函数原型说明
/**@brief Reply with GAP security parameters.
*
* @details This function is only used to reply to a @ref BLE_GAP_EVT_SEC_PARAMS_REQUEST, calling it at other times will result in an @ref NRF_ERROR_INVALID_STATE.
* @note If the call returns an error code, the request is still pending, and the reply call may be repeated with corrected parameters.
*
* @events
* @event{This function is used during authentication procedures\, see the list of events in the documentation of @ref sd_ble_gap_authenticate.}
* @endevents
*
* @mscs
* @mmsc{@ref BLE_GAP_PERIPH_PAIRING_JW_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_BONDING_JW_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_BONDING_PK_PERIPH_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_BONDING_PK_CENTRAL_OOB_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_BONDING_STATIC_PK_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_PAIRING_CONFIRM_FAIL_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_LESC_PAIRING_JW_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_LESC_BONDING_NC_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_LESC_BONDING_PKE_PD_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_LESC_BONDING_PKE_CD_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_LESC_BONDING_OOB_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_PAIRING_KS_TOO_SMALL_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_PAIRING_APP_ERROR_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_PAIRING_REMOTE_PAIRING_FAIL_MSC}
* @mmsc{@ref BLE_GAP_PERIPH_PAIRING_TIMEOUT_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_PAIRING_JW_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_BONDING_JW_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_BONDING_PK_PERIPH_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_BONDING_PK_PERIPH_OOB_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_LESC_PAIRING_JW_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_LESC_BONDING_NC_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_LESC_BONDING_PKE_PD_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_LESC_BONDING_PKE_CD_MSC}
* @mmsc{@ref BLE_GAP_CENTRAL_LESC_BONDING_OOB_MSC}
* @endmscs
*
* @param[in] conn_handle Connection handle.
* @param[in] sec_status Security status, see @ref BLE_GAP_SEC_STATUS.
* @param[in] p_sec_params Pointer to a @ref ble_gap_sec_params_t security parameters structure. In the central role this must be set to NULL, as the parameters have
* already been provided during a previous call to @ref sd_ble_gap_authenticate.
* @param[in,out] p_sec_keyset Pointer to a @ref ble_gap_sec_keyset_t security keyset structure. Any keys generated and/or distributed as a result of the ongoing security procedure
* will be stored into the memory referenced by the pointers inside this structure. The keys will be stored and available to the application
* upon reception of a @ref BLE_GAP_EVT_AUTH_STATUS event.
* Note that the SoftDevice expects the application to provide memory for storing the
* peer's keys. So it must be ensured that the relevant pointers inside this structure are not NULL. The pointers to the local key
* can, however, be NULL, in which case, the local key data will not be available to the application upon reception of the
* @ref BLE_GAP_EVT_AUTH_STATUS event.
*
* @retval ::NRF_SUCCESS Successfully accepted security parameter from the application.
* @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
* @retval ::NRF_ERROR_BUSY The stack is busy, process pending events and retry.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied.
* @retval ::NRF_ERROR_INVALID_STATE Security parameters has not been requested.
* @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid connection handle supplied.
* @retval ::NRF_ERROR_NOT_SUPPORTED Setting of sign or link fields in @ref ble_gap_sec_kdist_t not supported.
*/
SVCALL(SD_BLE_GAP_SEC_PARAMS_REPLY, uint32_t, sd_ble_gap_sec_params_reply(uint16_t conn_handle, uint8_t sec_status, ble_gap_sec_params_t const *p_sec_params, ble_gap_sec_keyset_t const *p_sec_keyset));
step 3:
BLE_GAP_EVT_SEC_INFO_REQUEST事件中回复配对info信息
case BLE_GAP_EVT_SEC_INFO_REQUEST:
if(memcmp(p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.rand, m_own_enc_key.master_id.rand,8) != 0) //配对信息不一致
{
err_code = sd_ble_gap_sec_info_reply(m_conn_handle,NULL, NULL, NULL);
APP_ERROR_CHECK(err_code);
}
else // 第一次配对成功
{
err_code = sd_ble_gap_sec_info_reply(m_conn_handle, &(m_own_enc_key.enc_info), NULL,NULL);
APP_ERROR_CHECK(err_code);
}
break;
step 4:
BLE_GAP_EVT_AUTH_STATUS事件中获取配对的结果
case BLE_GAP_EVT_AUTH_STATUS:
if(p_ble_evt->evt.gap_evt.params.auth_status.auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
{
NRF_LOG_INFO("pair success\n");
PasswordOk = 1;
}
else
{
NRF_LOG_INFO("pair error\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);
PasswordOk = 0;
}
上述完整代码如下
/**@brief Function for handling BLE events.
*
* @param[in] p_ble_evt Bluetooth stack event.
* @param[in] p_context Unused.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
uint32_t err_code;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected");
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("MAC ADDRESS = %02x:%02x:%02x:%02x:%02x:%02x",p_ble_evt->evt.gap_evt.params.connected.peer_addr.addr[0],\
p_ble_evt->evt.gap_evt.params.connected.peer_addr.addr[1],\
p_ble_evt->evt.gap_evt.params.connected.peer_addr.addr[2],\
p_ble_evt->evt.gap_evt.params.connected.peer_addr.addr[3],\
p_ble_evt->evt.gap_evt.params.connected.peer_addr.addr[4],\
p_ble_evt->evt.gap_evt.params.connected.peer_addr.addr[5]);
requestBondToCenterBle();
break;
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected");
// 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_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_GAP_EVT_SEC_INFO_REQUEST:
NRF_LOG_INFO("enc need: %d id need:%d sign need:%d\r\n",
p_ble_evt->evt.gap_evt.params.sec_info_request.enc_info,
p_ble_evt->evt.gap_evt.params.sec_info_request.id_info,
p_ble_evt->evt.gap_evt.params.sec_info_request.sign_info);
NRF_LOG_INFO("RSP: LTK :");
for(int i = 0; i < m_own_enc_key.enc_info.ltk_len; i++){
NRF_LOG_INFO("%x",m_own_enc_key.enc_info.ltk[i]);
}
NRF_LOG_INFO("\r\nEDIV :%x RANDOM:",
p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.ediv);
for(int i = 0; i< 8; i++){
NRF_LOG_INFO("%x",
m_own_enc_key.master_id.rand[i]);
}
NRF_LOG_INFO("key");
for(int i = 0; i< 8; i++){
NRF_LOG_INFO("%x",
p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.rand[i]);
}
if(memcmp(p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.rand,m_own_enc_key.master_id.rand,8) != 0)
{
err_code = sd_ble_gap_sec_info_reply(m_conn_handle,NULL, NULL, NULL);
APP_ERROR_CHECK(err_code);
}
else
{
err_code = sd_ble_gap_sec_info_reply(m_conn_handle, &(m_own_enc_key.enc_info), NULL, NULL);
APP_ERROR_CHECK(err_code);
}
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
sec_params_init();
err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &m_sec_params, &m_sec_keyset);
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_PASSKEY_DISPLAY:
NRF_LOG_INFO("passkey: %s\n",p_ble_evt->evt.gap_evt.params.passkey_display.passkey);
break;
case BLE_GAP_EVT_AUTH_STATUS:
NRF_LOG_INFO("LTK :");
for(int i = 0; i < m_own_enc_key.enc_info.ltk_len; i++){
NRF_LOG_INFO("%x",m_own_enc_key.enc_info.ltk[i]);
}
NRF_LOG_INFO(" AUTH: %d ",m_own_enc_key.enc_info.auth);
NRF_LOG_INFO(" LTK length: %d \r\n",m_own_enc_key.enc_info.ltk_len);
NRF_LOG_INFO("EDIV: %x ",m_own_enc_key.master_id.ediv);
NRF_LOG_INFO("rand:");
for(int i = 0; i < 8; i++){
NRF_LOG_INFO("%x",m_own_enc_key.master_id.rand[i]);
}
if(p_ble_evt->evt.gap_evt.params.auth_status.auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
{
NRF_LOG_INFO("pair success\n");
PasswordOk = 1;
}
else
{
NRF_LOG_INFO("pair error\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);
PasswordOk = 0;
}
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);
NRF_LOG_INFO("23333333333333\n");
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);
PasswordOk = 0;
NRF_LOG_INFO("BLE_GATTC_EVT_TIMEOUT");
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);
PasswordOk = 0;
NRF_LOG_INFO("BLE_GATTS_EVT_TIMEOUT");
break;
default:
// No implementation needed.
break;
}
}
step 5:
保存配对信息
if(PasswordOk)
{
PasswordOk = 0;
App_RunNote_SaveData(&s_note);
}
step 6:
恢复配置信息
BSP_InnerFlash_Init();
App_RunNote_ReadData(&s_note);
在上述的基础上只需修改两个地方即可实现,首先修m_sec_params.io_caps = BLE_GAP_IO_CAPS_NONE;
static void sec_params_init()
{
memset(&m_sec_params, 0, sizeof(ble_gap_sec_params_t));
m_sec_params.bond = SEC_PARAM_BOND;
m_sec_params.mitm = SEC_PARAM_MITM;
m_sec_params.io_caps = BLE_GAP_IO_CAPS_NONE;
m_sec_params.oob = SEC_PARAM_OOB;
m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
m_sec_params.kdist_own.enc = 1;
m_sec_params.kdist_own.id = 0;
m_sec_params.kdist_own.sign = 0;
m_sec_params.kdist_peer.enc = 1;
m_sec_params.kdist_peer.id = 0;
m_sec_params.kdist_peer.sign = 0;
m_sec_keyset.keys_own.p_enc_key = &m_own_enc_key;
m_sec_keyset.keys_own.p_id_key = NULL;
m_sec_keyset.keys_own.p_pk = NULL;
m_sec_keyset.keys_own.p_sign_key = NULL;
m_sec_keyset.keys_peer.p_enc_key = &m_peer_enc_key;
m_sec_keyset.keys_peer.p_id_key = NULL;
m_sec_keyset.keys_peer.p_pk = NULL;
m_sec_keyset.keys_peer.p_sign_key = NULL;
}
其次在BLE_GAP_EVT_PASSKEY_DISPLAY事件中获取配对密码,由于uart例程没有显示功能,只能通过RTT打印密码
case BLE_GAP_EVT_PASSKEY_DISPLAY:
NRF_LOG_INFO("passkey: %s\n",p_ble_evt->evt.gap_evt.params.passkey_display.passkey);
break;
静态密码在动态密码的基础上,增加如下代码,此代码在gap_params_init中添加
/**@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_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);
//-----静态配对代码如下---------------------------
ble_opt_t passkey_opt;
uint8_t passkey[] = "123456";
passkey_opt.gap_opt.passkey.p_passkey = passkey;
err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &passkey_opt);
APP_ERROR_CHECK(err_code);
}
三种方式配对DEMO下载链接:https://download.csdn.net/download/mygod2008ok/11980301