LE Observer
ble_observer 示例工程可以作为开发基于 Observer 角色的 APP 的框架。
Observer 角色特征:
可以收到可扫描广播事件。
不能创建连线。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
为快速搭建起开发环境,可参考 快速入门 中提供的详细指导。
硬件连线
请参考 快速入门 中的 RTL8752H EVB 接口和模块。
配置
配置选项
该示例工程全部配置选项在
src\sample\ble_observer\app_flags.h
中,
开发者可以根据实际需求配置。
/** @brief Config DLPS: 0-Disable DLPS, 1-Enable DLPS */
#define F_BT_DLPS_EN 1
/** @} */ /* End of group OB_Config */
编译和下载
该示例可以在 SDK 文件夹中找到:
Project file: board\evb\ble_observer\mdk
Project file: board\evb\ble_observer\gcc
编译和运行该示例请遵循以下步骤:
打开项目文件。
要编译目标文件,请参考 快速入门 的 编译 APP Image 中列出的步骤。
编译成功后,目录
board\evb\ble_observer\mdk\bin
下会生成 app bin 文件app_MP_sdk_xxx.bin
。在 EVB 上按下 reset 键,程序将开始运行。
测试验证
将示例工程烧录到 EVB 后,开发者可以使用另一个运行 LE Broadcaster 工程的开发板来进行测试。
与另一块开发板对测
准备两块开发板分别命名为 DUT 和 Tester 。 开发者可以使用 Debug Analyzer
做 Log 验证 。
准备步骤
使用 MP Tool 将 DUT 地址设为 [00:11:22:33:44:85],然后编译 ble_observer 示例工程,并将 images 下载至 DUT。
使用 MP Tool 将 Tester 地址设为 [00:11:22:33:44:80],然后编译 ble_broadcaster 示例工程,并将 images 下载至 Tester。更多信息请参考 LE Broadcaster 文档中的 编译和下载。
更多关于如何修改 蓝牙地址 的细节请查阅 快速入门 中的 生成 System Config File。
测试步骤
按 Tester 上的 reset 键, Tester 将开始发送不可连接的非定向广播。
如果成功启动广播,会打印下面的 Debug Analyzer 日志。
如果没有看到下列日志,说明广播启动失败,请检查软件和硬件环境。
[APP] !**GAP adv start
按 DUT 上的 reset 键, DUT 将开始扫描广播。
在 DUT 的日志中,使用 Tester 的地址查找广播是否被扫描到。
如果 DUT 接收到广播数据和扫描响应数据,应用程序会接收到
GAP_MSG_LE_SCAN_INFO
。
下面是日志:
[APP] GAP_MSG_LE_SCAN_INFO: bd_addr 00::11::22::33::44::80, bdtype 0, event 0x3, rssi -58, len 20
代码介绍
本章的主要目的是帮助开发者熟悉 Observer Role 相关的开发流程。本章将按照以下几个部分进行介绍:
源码路径 中介绍项目目录和源代码文件。
Bluetooth Host 介绍 中介绍了 Bluetooth Host 相关信息。
GAP 消息处理 中介绍 Observer 相关的 GAP 消息处理。
GAP 回调函数处理 中介绍 Observer 相关的 GAP 回调函数处理。
源码路径
工程目录:
board\evb\ble_observer
。源码目录:
src\sample\ble_observer
。
ble_observer 示例工程中的源文件当前被分为几个组,如下所示。
└── Project: observer
└── ble_observer
└── 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.
└── app Includes the ble_observer user application implementation.
├── app_task.c
├── main.c
├── observer_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(0);
gap_lib_init();
app_le_gap_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
le_gap_init()
用于初始化 GAP 并配置链接个数。app_le_gap_init()
于初始化 GAP 参数,开发者可以自行配置 Scan 参数 (可参考 LE Host 中的 Scan 参数的配置)。
void app_le_gap_init(void)
{
/* Scan parameters */
uint8_t scan_mode = GAP_SCAN_MODE_PASSIVE;
uint16_t scan_interval = DEFAULT_SCAN_INTERVAL;
uint16_t scan_window = DEFAULT_SCAN_WINDOW;
uint8_t scan_filter_policy = GAP_SCAN_FILTER_ANY;
uint8_t scan_filter_duplicate = GAP_SCAN_FILTER_DUPLICATE_ENABLE;
/* Set Scan parameters */
le_scan_set_param(GAP_PARAM_SCAN_MODE, sizeof(scan_mode), &scan_mode);
le_scan_set_param(GAP_PARAM_SCAN_INTERVAL, sizeof(scan_interval), &scan_interval);
le_scan_set_param(GAP_PARAM_SCAN_WINDOW, sizeof(scan_window), &scan_window);
le_scan_set_param(GAP_PARAM_SCAN_FILTER_POLICY, sizeof(scan_filter_policy),
&scan_filter_policy);
le_scan_set_param(GAP_PARAM_SCAN_FILTER_DUPLICATES, sizeof(scan_filter_duplicate),
&scan_filter_duplicate);
/* register gap message callback */
le_register_app_cb(app_gap_callback);
}
Observe 模式下的设备不需要发送 scan request PDUs,就会收到可扫描广播事件。因此 scan_mode
参数应被配置为 GAP_SCAN_MODE_PASSIVE
类型。
le_register_app_cb()
注册 GAP 消息的 callback 函数 app_gap_callback()
,所有的 GAP callback 消息都将在这个函数中被处理。
更多关于 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;
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_observer 示例工程会在收到 GAP_INIT_STATE_STACK_READY
时调用 le_scan_start()
来扫描广播。
如果 le_scan_start()
返回 true
,说明 Bluetooth Host 已经接收了这个命令并准备执行,当这个命令执行完成后,
Bluetooth Host 将发送 GAP_SCAN_STATE_SCANNING
给 app_handle_dev_state_evt()
。
当 ble_observer 示例工程在开发板上运行时,设备将收到可扫描广播事件,Bluetooth Host 会发送 GAP_MSG_LE_SCAN_INFO
给 app_gap_callback()
。
ble_observer 示例工程会调用 app_parse_scan_info()
来处理广播数据和扫描响应数据。
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, scan state %d, cause 0x%x",
new_state.gap_init_state,
new_state.gap_scan_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_scan_start();
}
}
if (gap_dev_state.gap_scan_state != new_state.gap_scan_state)
{
if (new_state.gap_scan_state == GAP_SCAN_STATE_IDLE)
{
APP_PRINT_INFO0("GAP scan stop");
}
else if (new_state.gap_scan_state == GAP_SCAN_STATE_SCANNING)
{
APP_PRINT_INFO0("GAP scan start");
}
}
gap_dev_state = new_state;
}
GAP 回调函数处理
app_gap_callback()
函数用于处理 GAP 回调函数消息。
更多关于 GAP 回调函数的信息请参考 LE Host 中的 Bluetooth LE GAP 回调函数。
当设备处于 scanning 模式时,它会收到广播数据。当收到广播数据或扫描响应数据时,GAP 层会发送 GAP_MSG_LE_SCAN_INFO
给应用。
T_APP_RESULT app_gap_callback(uint8_t cb_type, void *p_cb_data)
{
T_APP_RESULT result = APP_RESULT_SUCCESS;
T_LE_CB_DATA *p_data = (T_LE_CB_DATA *)p_cb_data;
switch (cb_type)
{
case GAP_MSG_LE_SCAN_INFO:
APP_PRINT_TRACE5("GAP_MSG_LE_SCAN_INFO: bd_addr %s, bdtype %d, event 0x%x, rssi %d, len %d",
TRACE_BDADDR(p_data->p_le_scan_info->bd_addr),
p_data->p_le_scan_info->remote_addr_type,
p_data->p_le_scan_info->adv_type,
p_data->p_le_scan_info->rssi,
p_data->p_le_scan_info->data_len);
/* User can split interested information by using the function as follow. */
app_parse_scan_info(p_data->p_le_scan_info);
break;
default:
break;
}
return result;
}
备注
app_parse_scan_info()
是一个用于处理广播数据和扫描响应数据的示例函数。