应用开发
蓝牙应用的开发使用文档中 应用开发 一节介绍的通用基础设施和方法。
本页提供仅与蓝牙应用相关的其他信息。
线程安全
调用蓝牙 API 应当是线程安全的,除非 API 函数的文档中另有说明。确保所有 API 调用都满足这一要求的工作仍在持续进行,但总体目标已在本段中正式说明。欢迎提交朝着该目标推进子系统的 Bug 报告和 Pull Request。
硬件设置
本节介绍使用 Zephyr 构建和调试蓝牙应用时可用的各种选项。您可以根据手头的硬件、自身需求以及偏好的开发方式,选择适合自己的设置。
有以下 3 种可能的设置:
嵌入式
这种设置要求所有软件都直接运行在应用所面向的嵌入式平台上。支持 配置 和 构建类型 中描述的所有配置与构建类型,但如果使用双芯片配置,或者 SoC 中有多个核心且各核心运行不同的构建类型(例如一个运行主机,另一个运行控制器),则可能需要多次构建 Zephyr。
要使用此设置开始开发,请按照 入门指南 操作,选择一块支持蓝牙的开发板(如果使用双芯片方案,则可选择多块),然后 运行应用)。
即使没有物理传输,也有办法访问主机与控制器之间的 HCI 流量。有关操作方法,请参见 嵌入式 HCI 跟踪。
Linux 上带外部控制器的主机
备注
目前仅在 GNU/Linux 上可用
这种设置依赖于“双芯片” 配置,它由以下设备组成:
一个在 QEMU 模拟器或 Zephyr 的 native_sim 本机移植上运行的 仅主机 应用
一个控制器,可以是以下类型之一:
警告
某些外部控制器要么无法接受 Zephyr 默认设置的主机到控制器流控参数(Qualcomm),要么不会从控制器向主机传输任何数据(Realtek)。如果看到类似以下内容的消息:
<wrn> bt_hci_core: opcode 0x0c33 status 0x12
在启动所选示例时(请确保在运行示例前已在 prj.conf 中启用 CONFIG_LOG),或者如果从控制器到主机没有数据流动,则需要禁用主机到控制器的流控。为此,请在 prj.conf 中设置 CONFIG_BT_HCI_ACL_FLOW_CONTROL=n。
QEMU
您可以在 QEMU 模拟器 上运行 Zephyr 主机,并让它与物理外部蓝牙控制器交互。
有关在此设置中构建和运行应用的完整说明,请参见 在 QEMU 或 native_sim 上运行。
native_sim
备注
目前仅在 GNU/Linux 上可用
native_sim 目标会将您的 Zephyr 应用连同 Zephyr 内核以及一些最小限度的硬件仿真一起构建为原生 Linux 可执行文件。
该可执行文件是一个普通的 Linux 程序,可以像其他程序一样进行调试和插桩,并与物理或虚拟外部控制器通信。请参见:
物理控制器请参见 在 QEMU 或 native_sim 上运行
虚拟控制器请参见 在虚拟控制器和 native_sim 上运行
使用 BabbleSim 仿真的 nRF5x
备注
目前仅在 GNU/Linux 上可用
nrf52_bsim 和 nrf5340bsim 开发板是仿真目标开发板,它们仿真 nRF52/53 SoC 的必要外设,以便开发和测试低功耗蓝牙应用。这些开发板使用:
BabbleSim 用于仿真 nRF5x 调制解调器和射频环境。
POSIX 架构和原生模拟器,用于仿真处理器并在主机上原生运行。
与 native_sim 目标一样,构建结果是普通的 Linux 可执行文件。有关如何使用一个或多个设备运行仿真的更多信息,请参见 这些开发板的文档。
使用 nrf52_bsim 时,通常会进行 组合构建,但也可以在一个仿真设备中使用 HCI UART 示例之一构建控制器,并在另一个仿真设备中使用 H4 驱动而不是集成控制器来构建主机。
使用 nrf5340bsim 时,可以采用两种构建方式:控制器和主机都运行在其网络核心上;或者网络核心只运行控制器,应用核心运行主机和您的应用,并通过 IPC 传输 HCI。
初始化
蓝牙子系统使用 bt_enable() 函数初始化。调用者应检查返回码中是否有错误,以确保该函数成功执行。如果将函数指针传递给 bt_enable(),则初始化会异步进行,并通过所给的函数通知完成。
蓝牙应用示例
下面展示了一个简单的蓝牙信标应用。该应用初始化蓝牙子系统并启用不可连接广播,实际上充当低功耗蓝牙广播者。
1
2/*
3 * Set Advertisement data. Based on the Eddystone specification:
4 * https://github.com/google/eddystone/blob/master/protocol-specification.md
5 * https://github.com/google/eddystone/tree/master/eddystone-url
6 */
7static const struct bt_data ad[] = {
8 BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
9 BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
10 BT_DATA_BYTES(BT_DATA_SVC_DATA16,
11 0xaa, 0xfe, /* Eddystone UUID */
12 0x10, /* Eddystone-URL frame type */
13 0x00, /* Calibrated Tx power at 0m */
14 0x00, /* URL Scheme Prefix http://www. */
15 'z', 'e', 'p', 'h', 'y', 'r',
16 'p', 'r', 'o', 'j', 'e', 'c', 't',
17 0x08) /* .org */
18};
19
20/* Set Scan Response data */
21static const struct bt_data sd[] = {
22 BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
23};
24
25static void bt_ready(int err)
26{
27 bt_addr_le_t addr = {0};
28 size_t count = 1;
29
30 if (err) {
31 printk("Bluetooth init failed (err %d)\n", err);
32 return;
33 }
34
35 printk("Bluetooth initialized\n");
36
37 /* Start advertising */
38 err = bt_le_adv_start(BT_LE_ADV_NCONN_IDENTITY, ad, ARRAY_SIZE(ad),
39 sd, ARRAY_SIZE(sd));
40 if (err) {
41 printk("Advertising failed to start (err %d)\n", err);
42 return;
43 }
44
45
46 /* For connectable advertising you would use
47 * bt_le_oob_get_local(). For non-connectable non-identity
48 * advertising an non-resolvable private address is used;
49 * there is no API to retrieve that.
50 */
51
52 bt_id_get(&addr, &count);
53
54 printk("Beacon started, advertising as %s\n", bt_addr_le_str(&addr));
55}
56
57int main(void)
58{
59 int err;
60
61 printk("Starting Beacon Demo\n");
62
63 /* Initialize the Bluetooth Subsystem */
64 err = bt_enable(bt_ready);
65 if (err) {
66 printk("Bluetooth init failed (err %d)\n", err);
67 }
68 return 0;
69}
信标示例所用的关键 API 是 bt_enable(),用于初始化蓝牙,以及 bt_le_adv_start(),用于开始广播特定的广播数据与扫描响应数据组合。
更多示例
更多 蓝牙应用示例 可在 samples/bluetooth/ 中找到。