Zephyr 官方示例带你从零开始构建一个 LE Audio 应用

📅 2026/7/10 15:24:35
Zephyr 官方示例带你从零开始构建一个 LE Audio 应用
这份指南将基于 Zephyr 官方示例带你从零开始构建一个 LE Audio 应用。我们将从bap_unicast_server和bap_unicast_client这两个核心示例入手深入解析其代码实现。你可以在 Zephyr 源码树的samples/bluetooth/bap_unicast_server和samples/bluetooth/bap_unicast_client目录下找到这些示例。1. LE Audio 核心概念速览在深入代码前先理解几个核心概念会很有帮助BAP (Basic Audio Profile)LE Audio 的基础框架定义了如何发现、配置和流式传输音频。我们的两个示例就是基于此规范。ASCS (Audio Stream Control Service)BAP 的核心服务用于控制音频流的状态如配置、启动、停止。PACS (Published Audio Capabilities Service)一个服务用于向其他设备公布自己支持的音频能力如编解码器、采样率等。LC3 (Low Complexity Communication Codec)LE Audio 的强制编解码器提供高质量、低功耗的音频压缩。ISO (Isochronous) ChannelLE Audio 引入的等时通道用于传输对时间敏感的音频数据确保低延迟和同步。2. 示例一构建 Unicast Audio Server音频服务端bap_unicast_server示例的角色是“音频服务端”Acceptor它负责向外广播自己的能力并等待客户端来连接和配置音频流。2.1 关键代码解析配置广播数据服务端需要广播自己的音频能力以便客户端发现。代码中通过ad数组配置广播包。c// 服务端的广播数据 static const struct bt_data ad[] { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL)), BT_DATA(BT_DATA_SVC_DATA16, unicast_server_addata, ...), BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, ...), };BT_DATA_UUID16_ALL声明支持ASCS服务这是 LE Audio 的关键标识。BT_DATA_SVC_DATA16携带了更详细的音频能力信息如支持的上下文类型。注册 PACS (Published Audio Capabilities Service)服务端需要向蓝牙协议栈注册其音频能力以便客户端查询。// 定义 LC3 编解码能力 static const struct bt_audio_codec_cap lc3_codec_cap BT_AUDIO_CODEC_CAP_LC3(/* ... 参数 ... */); // 注册 Sink接收和 Source发送能力 bt_pacs_cap_register(BT_AUDIO_DIR_SINK, cap_sink); bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, cap_source);这里注册了 Sink下行音频如播放音乐和 Source上行音频如麦克风的能力。BT_AUDIO_CODEC_CAP_LC3宏定义了支持的 LC3 参数如采样率、帧时长等。实现 ASCS 回调 (unicast_server_cb)ASCS 服务的核心是一组回调函数当客户端请求改变音频流状态时这些回调会被触发。cstatic const struct bt_bap_unicast_server_cb unicast_server_cb { .config lc3_config, // 客户端请求配置流 .qos lc3_qos, // 客户端请求设置 QoS .enable lc3_enable, // 客户端请求使能流 .start lc3_start, // 客户端请求启动流 // ... 其他回调 };lc3_config当客户端发起流配置时此函数被调用。它负责分配一个bt_bap_stream对象并返回本地的 QoS 偏好。lc3_qos用于配置服务质量参数如数据包间隔、重传次数等。这是保证音频质量的关键步骤。lc3_enable流使能时可以在此初始化 LC3 解码器为接收音频数据做准备。lc3_start流启动后音频数据开始传输。对于 Source 方向可以在此注册发送线程。ISO 数据接收与 LC3 解码当音频数据通过 ISO 通道到达时stream_ops中的recv回调会被调用。static void stream_recv_lc3_codec(struct bt_bap_stream *stream, const struct bt_iso_recv_info *info, struct net_buf *buf) { // 1. 检查数据有效性 // 2. 调用 LC3 解码器将数据解码为 PCM // 3. 可选将 PCM 数据发送到音频输出设备如 DAC }这个回调是连接蓝牙音频和本地音频硬件的桥梁。你需要在此处集成 LC3 解码库并将解码后的音频数据送至扬声器或音频输出接口。2.2 关键配置 (prj.conf)kconfig# 启用蓝牙和 LE Audio 支持 CONFIG_BTy CONFIG_BT_PERIPHERALy CONFIG_BT_BAP_UNICAST_SERVERy # 配置 ASCS 流数量 CONFIG_BT_ASCS_ASE_SNK_COUNT1 # 支持 1 个音频接收流 CONFIG_BT_ASCS_ASE_SRC_COUNT1 # 支持 1 个音频发送流 # 启用 LC3 编解码器需要外部库 CONFIG_LIBLC3y3. 示例二构建 Unicast Audio Client音频客户端bap_unicast_client示例的角色是“音频发起者”Initiator它负责扫描并连接到服务端然后发起和控制音频流。3.1 关键代码解析扫描与发现客户端需要扫描并连接到提供 LE Audio 服务的设备。设备发现通过device_found回调处理。static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad) { // 1. 检查广播类型和 RSSI // 2. 解析广播数据查找 ASCS 服务 UUID // 3. 如果找到目标停止扫描并发起连接 }关键在于通过bt_data_parse解析广播数据寻找BT_UUID_ASCS这是识别 LE Audio 服务端的标准方法。发现对端能力PACS连接建立后客户端通过 PACS 服务读取服务端的音频能力。// 回调函数当发现一个 PAC 记录时被调用 static void pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir, const struct bt_audio_codec_cap *codec_cap) { // 打印或分析 codec_cap了解对端支持的音频参数 print_codec_cap(codec_cap); } // 注册回调并启动发现 bt_bap_unicast_client_register_cb(unicast_client_cbs); bt_bap_unicast_client_discover(default_conn, BT_AUDIO_DIR_SINK);pac_record_cb会为服务端支持的每个音频方向Sink/Source和编解码器配置调用一次。客户端需要根据这些信息选择一个双方都支持的配置。配置与启动音频流选择好配置后客户端通过一系列步骤启动音频流// 1. 配置流 (bt_bap_stream_config) // 2. 设置 QoS (bt_bap_stream_qos) // 3. 使能流 (bt_bap_stream_enable) // 4. 连接 ISO 通道 (bt_bap_stream_connect) // 5. 启动流 (bt_bap_stream_start)这些步骤对应着 ASCS 定义的状态机转换每个步骤通常都需要等待服务端的确认回调如stream_configured,stream_qos_set等。音频数据发送与服务端类似客户端也需要实现stream_ops回调。对于 Source发送方向你需要实现音频数据的生成和 LC3 编码并通过bt_bap_stream_send发送。// 在发送线程中 err bt_bap_stream_send(bap_stream, buf, seq_num);3.2 关键配置 (prj.conf)kconfig# 启用蓝牙和 LE Audio 支持 CONFIG_BTy CONFIG_BT_CENTRALy CONFIG_BT_BAP_UNICAST_CLIENTy # 配置客户端支持的流数量 CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT1 CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT1 # 启用 LC3 编解码器 CONFIG_LIBLC3y4. 构建与运行4.1 硬件要求你需要一块支持蓝牙 5.2及以上的开发板例如nRF52840 DK或nRF5340 DK。4.2 构建步骤进入示例目录bash# 以服务端为例 cd samples/bluetooth/bap_unicast_server构建应用以 nRF52840DK 为例bashwest build -b nrf52840dk/nrf52840 -- -DOVERLAY_CONFIGoverlay-bt_ll_sw_split.confoverlay-bt_ll_sw_split.conf是关键它为 Zephyr 的蓝牙控制器启用了 ISO 通道支持。烧录bashwest flash4.3 运行测试方式一两个开发板将一个板子烧录为bap_unicast_server另一个烧录为bap_unicast_client。上电后客户端会自动扫描、连接并建立音频流。方式二开发板 Linux 主机在 Linux 主机上使用bluetoothctl和bapctlBlueZ 工具作为客户端连接到开发板上的服务端进行音频收发测试。5. 扩展学习CCP Call Control Server除了 BAP 音频流LE Audio 应用还常涉及通话控制。ccp_call_control_server示例展示了如何实现一个通话控制服务端允许远程设备如手机控制通话。核心功能它注册了一个或多个TBS (Telephone Bearer Service)模拟了一个或多个电话线路如主卡、副卡。广播它会广播 TBS 的 UUID让客户端如耳机可以发现并连接。控制客户端连接后可以通过 GATT 特征值发送指令如接听、挂断、保持通话等。应用场景这是开发真无线立体声TWS耳机、车载免提系统等需要通话控制功能设备的核心参考。6. 总结通过bap_unicast_server和bap_unicast_client这两个官方示例你已经掌握了 Zephyr LE Audio 应用开发的完整脉络服务端通过PACS公布能力通过ASCS响应客户端请求并通过ISO 通道收发 LC3 音频数据。客户端扫描并连接服务端通过PACS协商能力通过ASCS控制音频流状态最终建立ISO 通道进行音频传输。关键步骤广播配置、服务注册、回调实现、LC3 编解码集成、ISO 数据收发以及状态机管理。你可以将此作为起点根据自己的应用需求进行定制和扩展例如添加通话控制功能参考 CCP 示例或支持广播音频参考bap_broadcast_source等示例