LE Peripheral
ble_peripheral 示例工程可以作为开发基于 Peripheral 角色的 APP 的框架。
Peripheral 角色特征:
可以发送可连接广播。
可以作为 Peripheral 角色与其他设备建立 LE 连线。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
为快速搭建起开发环境,可参考 快速入门 中提供的详细指导。
硬件连线
具体的接线说明请参考 快速入门 中的 RTL8752H EVB 接口和模块。
配置
配置选项
该示例工程全部配置选项在
src\sample\ble_peripheral\app_flags.h
中,
开发者可以根据实际需求配置。
/** @brief Config APP LE link number */
#define APP_MAX_LINKS 1
/** @brief Config DLPS: 0-Disable DLPS, 1-Enable DLPS */
#define F_BT_DLPS_EN 0
/** @brief Config ANCS Client: 0-Not built in, 1-Open ANCS client function */
#define F_BT_ANCS_CLIENT_SUPPORT 0
#define F_BT_ANCS_APP_FILTER (F_BT_ANCS_CLIENT_SUPPORT & 1)
#define F_BT_ANCS_GET_APP_ATTR (F_BT_ANCS_CLIENT_SUPPORT & 0)
/** @brief Config ANCS Client debug log: 0-close, 1-open */
#define F_BT_ANCS_CLIENT_DEBUG (F_BT_ANCS_CLIENT_SUPPORT & 0)
开发者可以通过 F_BT_ANCS_CLIENT_SUPPORT
宏打开或关闭 ANCS Client 模块。更多信息请参考 ANCS Client 。
生成系统配置文件
开发者可以通过 MP Tool 配置以下项目:
Configurable Item |
Value |
---|---|
LE Slave Link Num |
≥ APP_MAX_LINKS |
更多有关于 MP Tool 配置的信息请参考 快速入门 中的 生成 System Config File。
编译和下载
该示例可以在 SDK 文件夹中找到:
Project file: board\evb\ble_peripheral\mdk
Project file: board\evb\ble_peripheral\gcc
编译和运行该示例请遵循以下步骤:
打开项目文件。
要编译目标文件,请参考 快速入门 的 编译 APP Image 中列出的步骤。
编译成功后,目录
board\evb\ble_peripheral\mdk\bin
下会生成 app bin 文件app_MP_sdk_xxx.bin
。在 EVB 上按下 reset 键,程序将开始运行。
测试验证
将示例工程烧录到 EVB 后,开发者可以使用另一个运行 LE Central 工程的开发板或者装有 LightBlue
之类 LE 应用的手机来进行测试。
与另一块开发板对测
请参考 LE Central 中的 与另一块开发板对测。
与手机对测
代码介绍
本章的主要目的是帮助开发者熟悉 Peripheral Role 相关的开发流程。本章将按照以下几个部分进行介绍:
源码路径 中介绍项目目录和源代码文件。
Bluetooth Host 介绍 中介绍了 Bluetooth Host 相关信息。
GAP 消息处理 中介绍 Peripheral 相关的 GAP 消息处理。
Profile 消息回调函数 中介绍了 Peripheral 相关的 Profile 回调函数处理。
ANCS Client 介绍了如何使用 ANCS Client 模块。
源码路径
工程目录:
board\evb\ble_peripheral
。源码目录:
src\sample\ble_peripheral
。
ble_peripheral 示例工程中的源文件当前被分为几个组,如下所示。
└── Project: peripheral
└── peripheral
└── include
├── lib Includes all binary symbol files that user application is built on.
├── ROM.lib
├── gap_utils.lib
├── lowerstack.lib
└── rtl8752h_sdk.lib
├── cmsis Includes startup code.
├── peripheral Includes all peripheral drivers and module code used by the application.
├── profile Includes LE profiles or services used by the sample application.
└── app Includes the ble_peripheral user application implementation.
├── ancs.c
├── app_task.c
├── main.c
├── peripheral_app.c
└── overlay_mgr.c
示例工程使用与 upperstack_0_0
匹配的默认 GAP LIB,请参阅文档 Host Image 中的 GAP LIB 的使用方法 以获取更多信息。
Bluetooth Host 介绍
示例工程默认使用 Bluetooth Host image 版本 upperstack_0_0
,更多信息可参阅
Host Image。
关于 Bluetooth Host 所支持的蓝牙功能的详细信息,请参阅文件 bin\upperstack_img\upperstack_0_0\upperstack_config.h
。
初始化
当 EVB 启动并且芯片复位时, main()
函数将被调用,它执行以下初始化函数:
int main(void)
{
board_init();
le_gap_init(APP_MAX_LINKS);
gap_lib_init();
app_le_gap_init();
app_le_profile_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
le_gap_init()
用于初始化 GAP 并配置链接个数。app_le_profile_init()
用于初始化 profile。app_le_gap_init()
用于初始化 GAP 参数,开发者可以自行配置以下参数:Device name 和 device appearance,Advertising 参数,GAP Bond Manager 参数(请参考 LE Host 中的 Device Name 和 Device Appearance 的配置, Advertising 参数的配置, Pairing 参数的配置)。
void app_le_gap_init(void)
{
/* Device name and device appearance */
......
/* Advertising parameters */
......
/* GAP Bond Manager parameters */
......
/* Set advertising parameters */
le_adv_set_param(GAP_PARAM_ADV_EVENT_TYPE, sizeof(adv_evt_type), &adv_evt_type);
le_adv_set_param(GAP_PARAM_ADV_DIRECT_ADDR_TYPE, sizeof(adv_direct_type), &adv_direct_type);
le_adv_set_param(GAP_PARAM_ADV_DIRECT_ADDR, sizeof(adv_direct_addr), adv_direct_addr);
le_adv_set_param(GAP_PARAM_ADV_CHANNEL_MAP, sizeof(adv_chann_map), &adv_chann_map);
le_adv_set_param(GAP_PARAM_ADV_FILTER_POLICY, sizeof(adv_filter_policy), &adv_filter_policy);
le_adv_set_param(GAP_PARAM_ADV_INTERVAL_MIN, sizeof(adv_int_min), &adv_int_min);
le_adv_set_param(GAP_PARAM_ADV_INTERVAL_MAX, sizeof(adv_int_max), &adv_int_max);
le_adv_set_param(GAP_PARAM_ADV_DATA, sizeof(adv_data), (void *)adv_data);
le_adv_set_param(GAP_PARAM_SCAN_RSP_DATA, sizeof(scan_rsp_data), (void *)scan_rsp_data);
/* Setup the GAP Bond Manager */
gap_set_param(GAP_PARAM_BOND_PAIRING_MODE, sizeof(auth_pair_mode), &auth_pair_mode);
gap_set_param(GAP_PARAM_BOND_AUTHEN_REQUIREMENTS_FLAGS, sizeof(auth_flags), &auth_flags);
gap_set_param(GAP_PARAM_BOND_IO_CAPABILITIES, sizeof(auth_io_cap), &auth_io_cap);
gap_set_param(GAP_PARAM_BOND_OOB_ENABLED, sizeof(auth_oob), &auth_oob);
le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY, sizeof(auth_fix_passkey), &auth_fix_passkey);
le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY_ENABLE, sizeof(auth_use_fix_passkey),
&auth_use_fix_passkey);
le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_REQUIREMENT, sizeof(auth_sec_req_flags),
&auth_sec_req_flags);
/* register gap message callback */
le_register_app_cb(app_gap_callback);
}
peripheral 模式下的设备将发送可连接广播,因此 adv_evt_type
参数应被配置为 GAP_ADTYPE_ADV_IND
类型。
更多关于 LE GAP 初始化和启动流程的信息可以查阅 LE Host 中的 GAP 参数的初始化。
GAP 消息处理
当 APP 从 Bluetooth Host 接收到 GAP 消息时,函数 app_handle_gap_msg()
会被调用。
更多关于 GAP 消息的信息可以查阅 LE Host 中的 蓝牙状态消息。
void app_handle_gap_msg(T_IO_MSG *p_gap_msg)
{
T_LE_GAP_MSG gap_msg;
uint8_t conn_id;
memcpy(&gap_msg, &p_gap_msg->u.param, sizeof(p_gap_msg->u.param));
APP_PRINT_TRACE1("app_handle_gap_msg: subtype %d", p_gap_msg->subtype);
switch (p_gap_msg->subtype)
{
case GAP_MSG_LE_DEV_STATE_CHANGE:
{
app_handle_dev_state_evt(gap_msg.msg_data.gap_dev_state_change.new_state,
gap_msg.msg_data.gap_dev_state_change.cause);
}
break;
......
default:
APP_PRINT_ERROR1("app_handle_gap_msg: unknown subtype %d", p_gap_msg->subtype);
break;
}
}
ble_peripheral 示例工程会在收到 GAP_INIT_STATE_STACK_READY
时调用 le_adv_start()
来启动广播。
如果 le_adv_start()
返回 true
,说明 Bluetooth Host 已经接收了这个命令并准备执行,当这个命令执行完成后,Bluetooth Host 将发送
GAP_ADV_STATE_ADVERTISING
给 app_handle_dev_state_evt()
。
当 ble_peripheral 示例工程在开发板上运行时,设备将发送可连接广播,可以被对端设备扫描到并作为 Peripheral 角色建立连线。
void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
{
APP_PRINT_INFO3("app_handle_dev_state_evt: init state %d, adv state %d, cause 0x%x",
new_state.gap_init_state, new_state.gap_adv_state, cause);
if (gap_dev_state.gap_init_state != new_state.gap_init_state)
{
if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
{
APP_PRINT_INFO0("GAP stack ready");
/*stack ready*/
le_adv_start();
}
}
if (gap_dev_state.gap_adv_state != new_state.gap_adv_state)
{
if (new_state.gap_adv_state == GAP_ADV_STATE_IDLE)
{
APP_PRINT_INFO0("GAP adv stopped");
}
else if (new_state.gap_adv_state == GAP_ADV_STATE_ADVERTISING)
{
APP_PRINT_INFO0("GAP adv start");
}
}
gap_dev_state = new_state;
}
ble_peripheral 示例工程收到 GAP_CONN_STATE_DISCONNECTED
消息后,会再次调用 le_adv_start()
函数启动广播。
断连后, ble_peripheral 示例工程会被重置为可发现且可连接的状态。
void app_handle_conn_state_evt(uint8_t conn_id, T_GAP_CONN_STATE new_state, uint16_t disc_cause)
{
APP_PRINT_INFO4("app_handle_conn_state_evt: conn_id %d old_state %d new_state %d, disc_cause 0x%x",
conn_id, gap_conn_state, new_state, disc_cause);
switch (new_state)
{
case GAP_CONN_STATE_DISCONNECTED:
{
if ((disc_cause != (HCI_ERR | HCI_ERR_REMOTE_USER_TERMINATE))
&& (disc_cause != (HCI_ERR | HCI_ERR_LOCAL_HOST_TERMINATE)))
{
APP_PRINT_ERROR1("app_handle_conn_state_evt: connection lost cause 0x%x", disc_cause);
}
le_adv_start();
}
break;
......
}
}
Profile 消息回调函数
当 APP 使用 xxx_add_service 注册 specific service 时,APP 需要注册回调函数以处理 specific service 的消息。APP 需要调用 server_register_app_cb()
注册回调函数以处理 profile server layer 的信息。
APP 可以针对不同的 services 注册不同的回调函数,也可以注册通用回调函数以处理 specific services 和 profile server layer 的消息。
app_profile_callback()
是通用回调函数。app_profile_callback()
可以通过 service ID 识别不同的服务。
void app_le_profile_init(void)
{
server_init(2);
simp_srv_id = simp_ble_service_add_service(app_profile_callback);
bas_srv_id = bas_add_service(app_profile_callback);
server_register_app_cb(app_profile_callback);
}
更多信息可查阅 LE Host 中的 GATT Profile Server。
通用 profile server 回调函数
SERVICE_PROFILE_GENERAL_ID
是 profile server layer 使用的 service ID。Profile server layer 包含两种消息类型:-
该消息用于通知 GAP 启动流程中的服务注册流程已经结束。
PROFILE_EVT_SEND_DATA_COMPLETE
Profile server layer 使用该消息向 APP 通知发送 notification 或者 indication 的结果。
T_APP_RESULT app_profile_callback(T_SERVER_ID service_id, void *p_data) { T_APP_RESULT app_result = APP_RESULT_SUCCESS; if (service_id == SERVICE_PROFILE_GENERAL_ID) { T_SERVER_APP_CB_DATA *p_param = (T_SERVER_APP_CB_DATA *)p_data; switch (p_param->eventId) { case PROFILE_EVT_SRV_REG_COMPLETE:// srv register result event. APP_PRINT_INFO1("PROFILE_EVT_SRV_REG_COMPLETE: result %d", p_param->event_data.service_reg_result); break; case PROFILE_EVT_SEND_DATA_COMPLETE: ...... break; ...... }
-
Battery Service
bas_srv_id
是 battery service 的 service id。T_APP_RESULT app_profile_callback(T_SERVER_ID service_id, void *p_data) { T_APP_RESULT app_result = APP_RESULT_SUCCESS; ...... else if (service_id == bas_srv_id) { T_BAS_CALLBACK_DATA *p_bas_cb_data = (T_BAS_CALLBACK_DATA *)p_data; switch (p_bas_cb_data->msg_type) { case SERVICE_CALLBACK_TYPE_INDIFICATION_NOTIFICATION: ...... }
Simple LE Service
simp_srv_id
是 simple Bluetooth LE service 的 service id。T_APP_RESULT app_profile_callback(T_SERVER_ID service_id, void *p_data) { T_APP_RESULT app_result = APP_RESULT_SUCCESS; ...... else if (service_id == simp_srv_id) { TSIMP_CALLBACK_DATA *p_simp_cb_data = (TSIMP_CALLBACK_DATA *)p_data; switch (p_simp_cb_data->msg_type) { case SERVICE_CALLBACK_TYPE_INDIFICATION_NOTIFICATION: ...... }
ANCS Client
在 ancs.c
和 ancs.h
中定义 ANCS 程序。若其他 APP 需要使用该功能,流程如下:
将
ancs.h
和ancs.c
拷贝到 APP 目录中。将
auth_sec_req_enable
参数配置为true
。void app_le_gap_init(void) { #if F_BT_ANCS_CLIENT_SUPPORT uint8_t auth_sec_req_enable = true; #else uint8_t auth_sec_req_enable = false; #endif uint16_t auth_sec_req_flags = GAP_AUTHEN_BIT_BONDING_FLAG; ...... }
在
app_le_profile_init()
中初始化 ANCS Client。void app_le_profile_init(void) { ...... #if F_BT_ANCS_CLIENT_SUPPORT client_init(1); ancs_init(APP_MAX_LINKS); #endif }
当 authentication 流程完成后,启动 ANCS discovery 流程。
void app_handle_authen_state_evt(uint8_t conn_id, uint8_t new_state, uint16_t cause) { ...... case GAP_AUTHEN_STATE_COMPLETE: { if (cause == GAP_SUCCESS) { #if F_BT_ANCS_CLIENT_SUPPORT ancs_start_discovery(conn_id); #endif APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_COMPLETE pair success"); } else { APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_COMPLETE pair failed"); } } break; ...... }
在
ancs.c
中自定义 ANCS 程序。
常见问题
如何修改 Advertising Data 参数?
Advertising data 定义在数组 adv_data
中, 开发者可以直接修改数组的内容来修改广播数据。
static const uint8_t adv_data[] =
{
/* Flags */
0x02, /* length */
GAP_ADTYPE_FLAGS, /* type="Flags" */
GAP_ADTYPE_FLAGS_LIMITED | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
/* Service */
0x03, /* length */
GAP_ADTYPE_16BIT_COMPLETE,
LO_WORD(GATT_UUID_SIMPLE_PROFILE),
HI_WORD(GATT_UUID_SIMPLE_PROFILE),
/* Local name */
0x0F, /* length */
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'B', 'L', 'E', '_', 'P', 'E', 'R', 'I', 'P', 'H', 'E', 'R', 'A', 'L',
};