十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Auracast蓝牙广播开发实战:从BT2106C发射端到手机接收全链路

Auracast蓝牙广播开发实战:从BT2106C发射端到手机接收全链路 做嵌入式音频开发的朋友应该都有这种感觉LE Audio这两年已经不是PPT里的概念了而是实打实要落地的方案。年初我拿到一颗BT2106C蓝牙广播模块项目目标很明确把它做成一个Auracast广播发射端手机打开蓝牙就能直接收广播音频。前后折腾了三周左右从系统接线到SDK配置再到手机实测和反复调参中间踩的坑比想象中多最后总算把整条链路完整跑通。这篇文章就把整个开发过程、核心参数调法和排障经验写下来给正在搞Auracast、蓝牙广播模块或者相关嵌入式开发的同学做个参考。先交代一下背景。我要做的这套东西本质上是一个“广播音频源”也就是把一路音频信号通过蓝牙以广播形式持续发出去周围任何支持Auracast的设备都能在免配对的情况下直接收听。和传统蓝牙耳机那种一对一A2DP连接完全不同广播模式天然支持一对多想象一下候机厅广播、多语言导览、健身房电视声音分享这些场景一套发射端就能覆盖全场。下面我从技术原理开始讲一直讲到实际调试验收每一步都是我自己验证过的。1. 先搞清楚Auracast和BT2106C能干什么1.1 广播音频和传统蓝牙音频的本质区别在LE Audio出来之前蓝牙音频基本就是A2DP的天下它的工作模型是“配对-连接-传输”一个音源同一时刻只能服务一个接收端。即使有蓝牙耳机支持多设备连接那也是时分复用在多个设备之间切换本质上还是一对一。Auracast则完全换了一套思路它基于BLE的等时通道Isochronous Channel用广播的方式把音频数据打包发出去周围所有兼容设备都能同时接收。用个生活化的类比经典蓝牙类似“打电话”要先拨号、要对方接听、只能两个人聊Auracast更像“收音机广播”电台一直在发射信号谁把频率调对了谁就能听而且听的人数没有上限。这种“免配对、一对多”的特性才是Auracast最大的价值所在。从技术栈上看Auracast有两个关键承载一个是周期广播Periodic Advertising简称PA它相当于“电台频率和节目单”接收端靠它发现附近有什么广播流另一个是广播等时流Broadcast Isochronous Stream简称BIS这是真正承载音频数据的主通道。后面第3章我会详细讲这两个怎么配。1.2 BT2106C在项目中承担的角色我拿到的这颗BT2106C是一颗集成了蓝牙射频、协议栈和音频编解码能力的SoC模组板上带了晶振、匹配网络和PCB天线外围电路比较简单。它支持蓝牙5.3以上的协议栈重点就是LE Audio这一套包含Auracast广播源、广播接收端和广播助手等角色。在我的项目里BT2106C的工作链路是音频输入模拟麦克风或者I2S数字音频→ 采样 → LC3编码 → 协议栈封装成BIS流 → 射频持续广播。其中LC3编码通常在芯片内部完成有的芯片走硬件加速有的走CPU软编码BT2106C实测跑48kHz、128kbps的LC3编码负载很轻松CPU余量还够做点用户逻辑。另外要提一句这颗模块不是只能当发射端。同一套硬件刷不同的固件就能切换成接收端把广播音频解出来送给DAC或耳机功放这在我们开发调试阶段特别有用。我会在第6章展开讲。1.3 能落地的场景正因为Auracast是“一对多免配对”它解锁的场景比传统蓝牙音频丰富得多。我项目做完之后陆续想到这几个实际用途机场、车站、候诊室的公共广播旅客用手机或助听器就能直接收听到对应区域的广播内容。博物馆、景区多语言导览不同语种走不同的广播流游客自由选择收听不需要借讲解器。健身房、餐厅共享电视电视音频通过Auracast广播客人用耳机订阅不影响其他人。助听辅助电影院、剧场里听障人士可以把自己的助听器或耳机直接连到现场的Auracast音频流音量、增强都能自己调。会议室同传、课堂多语言教学一个教室开多个BIS每种语言一个通道听众自己切。如果只是做个蓝牙耳机那确实用不上Auracast但如果你的产品要面对“一群人听同一路声音”的场景这套方案就是目前成本和技术成熟度最平衡的路线之一。2. 开发环境搭建先把编译、烧录和日志跑通2.1 硬件准备与接线BT2106C模组本身外围不复杂但为了调试方便我还是建议准备一套完整的最小系统物料。下面是我实际用的清单物料规格/型号示例用途BT2106C模组板载PCB天线版本核心SoC与射频USB转TTLCP2102或CH340串口日志、命令下发、烧录音频输入板模拟MIC小板或I2S Codec子板如ES8311提供音频信号稳压供电低噪声LDO如ME6211/RT9013稳定3.3V供电测试手机支持LE Audio的安卓旗舰机Auracast接收验证接线方面我强烈建议至少把VCC、GND、UART_TX、UART_RX、音频输入这五组信号引出来。VCC接3.3V千万不要图省事直接怼USB的5VGND要共地UART用来打印日志和跑命令音频输入根据你选的方案接模拟MIC或者I2S信号。如果板子把SPI/I2C调试口也引出来了最好一并留着后面排查问题的时候可能就是救命线。一个很重要的提醒天线区域周围尽量留空不要走地线、不要铺铜、不要放金属螺丝。嵌入式开发里射频问题最烦人与其后面返工不如一开始按Layout规范来。2.2 SDK、编译工具链与烧录方式BT2106C这类模块的开发套路和STM32很相似厂商提供一个SDK包里面带协议栈库、外设驱动、例程和构建脚本。我用的方案是VSCode加命令行编译工具链是GCC交叉编译器整个流程和你在VSCode里开发STM32的体验差不多只是芯片寄存器配置和启动文件不一样。SDK的目录结构一般是这样的sdk/ ├── apps/ │ └── auracast_demo/ # 例程工程 ├── libs/ │ ├── ble/ # 蓝牙协议栈库 │ ├── lc3/ # LC3编解码 │ └── drivers/ # 外设驱动 ├── tools/ │ └── flash_tool.py # 烧录脚本/工具 └── Makefile拿到SDK之后先别急着改功能第一件事是编译原厂demo烧进去确认板子能跑。用厂商烧录工具通过UART把固件灌进去方法跟STM32串口ISP类似。烧完看串口日志一般波特率是921600或者115200。如果日志能正常打印说明环境没问题后面改代码才有基础。2.3 工程模板与广播功能开关目前的SDK例程一般都会带几个角色模板广播源Source、接收端Sink、广播助手Assistant。我们直接新建一个Source工程或者把Demo工程复制一份改名字。这里我想说一个经验很多第一次做Auracast的同学会在工程配置阶段就被“广播开关”搞晕。因为SDK里往往有各种宏开关比如音频通道选择、是否启用加密、LC3帧长、是否开启Debug日志等等。我踩过的坑是宏开多了编译报错开少了又没声音。建议按最小集来先只开“Auracast广播源”和“LC3编码”其他功能全部关掉跑通了再逐步加。下面是一个典型SDK里广播源配置结构体的示意代码接口名不同SDK有差异但语义基本一致/* Auracast广播参数配置示意代码 */ auracast_adv_cfg_t adv_cfg { .broadcast_name BT2106C_Demo, /* 手机上显示的名字 */ .public_broadcast true, /* 先做公共广播方便直接发现 */ .broadcast_code NULL, /* 公共广播不需要广播码 */ .pa_interval_slots 240, /* PA间隔单位1.25ms240约等于150ms */ }; auracast_audio_cfg_t audio_cfg { .sample_rate_hz 48000, .frame_duration_ms 10, .bitrate_kbps 128, .channels 1, /* 先把单声道跑通再扩展双BIS */ .lc3_frame_size 160, /* 128kbps 10ms 单帧约160字节 */ };配置好之后主流程的代码逻辑也很直白初始化音频输入、初始化LC3编码器、启动Auracast广播、然后在一个大循环里等待音频回调把PCM数据编码成LC3包再发出去。3. 广播链路核心参数LC3、BIS和周期广播3.1 LC3编码参数怎么选LC3是LE Audio的强制编解码器它的特点是低码率下音质明显好于SBC同时计算复杂度也低。LC3支持多种采样率和帧长帧长主要是7.5ms和10ms两种采样率覆盖8kHz到48kHz。实际开发中参数怎么选要看场景。我这里直接给一组我自己测过的推荐配置用途采样率帧时长码率单帧编码后字节数语音播报/提示音16kHz10ms32kbps40字节语音轻音乐32kHz10ms64kbps80字节音乐广播48kHz10ms128kbps160字节HiFi级广播48kHz10ms192kbps240字节字节数怎么算出来的很简单单帧字节数 码率(kbps) × 帧时长(ms) ÷ 8。以128kbps、10ms为例128 × 10 ÷ 8 160字节。这个公式在配置LC3缓冲区大小的时候直接能用上不用查文档。我测试下来公共广播场景用48kHz、10ms、128kbps是比较均衡的选择手机端听感已经不错了。如果做的是语音导览码率压到32kbps能大幅降低射频占用还能省电音质也足够听清人声。3.2 BIS和周期广播的关系这是整个Auracast里最容易绕晕的部分我说细一点。所谓“广播”其实分两层第一层叫周期广播PA它不传音频只传“这里有一个广播流”的公告信息包括广播名、支持哪些BIS、参数是什么。接收端先扫描到PA才能知道后面该怎么去同步音频流。PA的发送间隔一般建议100ms到500ms之间我工程里用的150ms手机在几十米外基本秒搜到。第二层叫BIS它才是真正传LC3音频数据的通道。一个广播组BIG里可以有多个BIS比如BIS0传中文语音、BIS1传英文语音、BIS2传背景音乐。接收端通过PA拿到BIG的时间规划然后在对应的时隙去接收BIS数据。调参时候有个关键点BIS事件间隔和LC3帧长要匹配。比如LC3帧长10ms那BIS事件间隔通常会设成10ms的整数倍如果你把PA间隔调得特别长接收端首次发现广播的速度就会变慢如果BIS的重传次数设得太少接收端在复杂环境下就容易丢包。我这边测试环境比较干净的办公室重传次数设2就够到人流密集的展会现场建议开到3到4。3.3 公共广播与加密广播Auracast广播分两种公共广播Public Broadcast和加密广播Private Broadcast/Encrypted Broadcast。公共广播谁都能直接收听适合开放场景加密广播则要求在接收端输入16字节128bit的广播码Broadcast Code相当于一把钥匙适合付费内容、会员场所等受限场景。开发调试阶段我强烈建议先用公共广播原因很简单手机端的Auracast入口本来就藏得比较深你还加一道密码排查问题的时候会多一个变量。等公共广播链路完全稳定了再切到加密广播测试广播码校验逻辑。另外广播名字段也要注意它支持UTF-8编码但这个名称会放进PA里反复广播影响空中占用。名称别起太长我一般控制在20字节以内够辨识就行。英文名兼容性最好中文名部分老手机可能显示乱码。4. 实操让BT2106C把第一声广播发出去4.1 上电确认与串口日志检查无论改什么功能第一步永远是先确认固件在跑。烧录完成、模组上电后串口助手打开对应波特率正常情况下会看到类似下面的日志[00:00:02.311] [INFO] BT2106C Auracast demo: build 2025-03-18 [00:00:02.512] [INFO] LC3 encoder init: 48kHz/10ms/128kbps, broadcastBIS0 [00:00:02.630] [INFO] PA started, interval150ms, nameBT2106C_Demo [00:00:03.001] [INFO] BIS[0] open, retrans2, sync_delay10ms [00:00:03.205] [INFO] audio in opened via I2S, 48000Hz/16bit [00:00:03.310] [INFO] auracast broadcast source running如果你的日志看不到PA和BIS启动那问题多半出在工程配置阶段要么广播源角色没开要么SDK里某个依赖模块没使能。日志是调试的第一道窗口建议每一层关键的初始化都打印出来后面定位问题能省一大半时间。4.2 音频采集链路的配置我这边音频输入用了两种方式分别验证过。第一种是板载模拟麦克风接线简单但模拟链路对电源噪声敏感底噪不容易压干净。第二种是I2S数字音频输入我用了一块ES8311 Codec小板接48kHz、16bit的I2S信号音质干净很多。做产品的话我建议直接用I2S或者PDM数字麦模拟方案只适合原型验证。音频采集初始化的示意代码#include audio_in.h static void on_audio_pcm_ready(int16_t *pcm, uint32_t samples) { uint8_t lc3_buf[256]; uint32_t len lc3_enc_process(g_lc3_enc, pcm, samples, lc3_buf); if (len 0) { auracast_send_bis_frame(0, lc3_buf, len); } } void audio_source_init(void) { audio_in_cfg_t cfg { .type AUDIO_IN_I2S, .sample_rate 48000, .bits 16, .callback on_audio_pcm_ready, }; audio_in_open(cfg); }这里最需要注意的就是音量/增益。如果输入增益拉太高PCM信号削顶失真LC3编码之后会出现明显的金属声增益太低信噪比又不够。我调模拟MIC的时候用手机接收端做对比反复听每个增益档位的表现最后把MIC增益也做成了运行时命令可调方便现场校准。4.3 编译、烧录和运行时命令调试代码改完之后在SDK根目录跑make生成固件然后用烧录工具通过UART下载。烧录成功后我习惯在代码里加一个极简的串口命令解析器用来做运行时调试。这个思路帮了大忙改广播名、切码率、开关加密广播都不用重新编译烧录直接往串口敲命令就行。/* 简单的运行时命令示例 */ static void cmd_set_bitrate(const char *kbps_str) { int kbps atoi(kbps_str); lc3_enc_set_bitrate(g_lc3_enc, kbps); LOG_INFO(bitrate set to %d kbps, kbps); } static const cmd_t cmd_table[] { { name, cmd_set_name }, { bitrate,cmd_set_bitrate }, { enc, cmd_set_encrypt }, { info, cmd_dump_info }, };有条件的同学还可以写一个简单的上位机把串口数据解析成参数面板。我这次调试期就直接用串口助手加脚本虽然简陋但完全够用。每次调完参数手机端重新搜一次广播、听一段音频就能快速判断是否达到预期。4.4 手机端接收验证手机是Auracast开发最方便的接收设备。目前主流安卓旗舰在Android 13之后的系统版本基本都支持LE Audio具体Auracast入口在各家设置里位置不太一样一般是蓝牙设置 → “广播音频”或者“浏览附近广播”之类。打开之后手机会列出附近所有Auracast广播源点进去就能直接收听。我的验证步骤一般是这样的手机开启蓝牙进入广播浏览页面搜到BT2106C_Demo。点进去观察是否能直接出声。播放一段测试音或音乐人耳判断音质、延迟和断续情况。走远一点测试有效距离同时观察串口日志里有没有丢包计数。除了手机我还用第二块BT2106C刷了接收端固件做交叉验证。这样能确认问题出在发射端还是接收端。如果手机和模块都收不到那发射端大概率有问题如果只有一方收不到问题很可能在那一侧的协议栈兼容性上。5. 调试路上踩过的坑5.1 手机搜不到广播这是第一个大坑我一开始折腾了整整半天。现象是模块日志显示PA和BIS都起来了但手机广播列表里空空如也。排查下来有几个原因第一是PA间隔设置太长。我第一次把间隔设成了2秒手机扫描窗口就那么点时间经常扫不到广播包。后来把PA间隔改到150ms问题立刻缓解。第二是广播名称为空或者格式异常。手机端的Auracast列表里如果一个广播没有名称很多手机会直接过滤不显示。所以无论如何先把广播名带上哪怕随便叫个test也行。第三是手机本身把广播入口藏起来了。部分机型需要先打开“正在收听附近设备”之类的权限或者要到开发者选项里开“BLE广播”。建议先搜索一下你的测试机型的Auracast入口路径别一上来就怀疑模块。5.2 能搜到但连不上能搜到说明PA链路通了连不上问题多半出在BIG参数上。我遇到的是把BIS起始时延sync_delay配错了接收端跟着PA去同步BIS时对不上时间就一直卡在“连接中”。遇到这种情况我的排查思路是先把广播加密关掉保证是最简单的公共广播然后把重传次数调大一点给接收端更多同步机会最后检查PA里声明的BIS数量和实际开启的BIS数量是否一致。这三步基本能覆盖绝大多数连不上的情况。5.3 连上之后没有声音能连上但没声音问题基本锁定在LC3参数匹配上。发射端和接收端必须使用相同的采样率、帧长、声道数和码率哪怕有一项不一致接收端的LC3解码器就会吐不出数据。我踩过一次特别隐蔽的坑发射端配置是48kHz单声道接收端固件默认按48kHz双声道解析结果实际出来的BIS只有一路另一路一直空着声音就丢了。后来在两边统一成单声道才正常。所以做交叉验证的时候接收端和发射端的参数配置一定要对照检查不要只看“连上了”就以为万事大吉。5.4 声音一卡一卡或频繁断续这种问题在空旷环境几乎不出现一旦到了干扰多的场合就暴露出来。卡顿的根源是射频丢包接收端收不到BIS帧音频自然就断了。处理优先级我建议这样先把BIS重传次数往上调比如从2调到4代价是功耗和空中占用上升但可靠性提升明显然后看PA间隔如果PA和BIS在时间上排得太密同频干扰也会互相打架。再有就是环境因素钢筋水泥墙、金属货架、人流密集都会衰减信号。展会现场实测下来重传次数4比2的卡顿率低很多代价是整机电流多了几毫安可以接受。5.5 有效距离拉不开Auracast广播的覆盖范围核心影响因素是射频发射功率和天线环境。我在初期测试时模组放在桌面上、底下铺了金属散热片结果十米外就断断续续后来把天线区域悬空、周围清空同样功率下距离翻了一倍不止。先确认发射功率配置我这边10dBm是比较常用的档位空旷环境下稳定覆盖三五十米没问题。如果距离确实不行重点检查PCB天线净空区有没有被遮挡、馈线匹配网络有没有焊对。软件上能做的就是调PA间隔和BIS重传但这些对距离提升有限硬件层面的问题还是得回到Layout上解决。5.6 底噪大、电流声明显这是模拟音频输入的常见问题尤其是在使用模拟麦克风时。主要原因有三个电源纹波、地环路干扰、MIC增益过高。我排查时的做法是先用I2S数字输入替代模拟MIC如果底噪消失说明问题在模拟前端而不是蓝牙链路。解决模拟底噪的经验供电用低噪声LDO别用开关电源直接供模拟地与数字地单点连接避免地环路MIC供电加RC滤波降低偏置噪声增益控制在不过载的前提下尽量调低。把这几步做了底噪基本能压到可接受范围。为了方便快速定位我把常见问题整理成了下面这个速查表现象大概率原因快速处理手机搜不到广播PA间隔太长或广播名为空PA间隔降到150ms左右设置清晰名称能搜到但连不上BIG参数异常或加密未处理先关加密核对BIS数量与参数连上后没有声音LC3参数不匹配两端统一采样率、帧长、声道、码率声音断续卡顿射频丢包、重传不足增大BIS重传次数检查射频环境有效距离短天线被遮挡或TX功率低清理天线净空核对发射功率配置底噪电流声电源或模拟前端问题换低噪声LDO调整增益和滤波6. 继续往下走接收端、广播助手与多路音频6.1 接收端固件的开发差异Auracast的价值不止在发射端接收端同样能玩出花。同一块BT2106C模组刷上Sink固件后就会变成一台“广播收音机”。接收端需要额外处理几个事情先扫描并锁定特定PA解析BIG参数然后去同步BIS数据流数据收下来之后要过LC3解码器再送给I2S DAC或功放输出。从开发量上看接收端比发射端略微复杂因为要处理信号丢失后的同步恢复、抖动缓冲、广播码解密等逻辑。如果产品要做低成本“听广播”的设备一个模块加一个耳机驱动就够物料成本非常可控。6.2 Broadcast Assistant广播助手角色除了Source和SinkAuracast还有第三个角色叫广播助手Assistant。它的职责是替那些能力受限的接收设备“找广播”。最典型的例子是助听器助听器本身蓝牙扫描能力弱手机作为助手扫描到Auracast广播后再把BIG参数转发给助听器由助听器直接同步音频流。开发Assistant角色时要注意它本身不接收音频只负责传递发现信息和用户交互。手机上的系统设置界面其实就承担了这个助手功能。如果你要做App辅助配网或者远程控制广播频道就绕不开对这个角色的理解。调试这个角色最好准备两台设备一台当广播源一台当助手第三台当Sink去接收链路中任何一环的状态都要能独立观察。6.3 多路BIS与加密广播的扩展玩法当单路广播跑通之后自然就想做多路。前面说了一个BIG里可以挂多个BIS这就给“多语言同传”这类需求提供了天然的通道设计BIS0放中文、BIS1放英文、BIS2放现场音乐接收端按需选择。切换方式有两种一种是一进广播就锁定固定BIS另一种是通过手机端广播助手去切换后者体验更好。加密广播的扩展价值也很大。比如付费导览内容可以通过动态下发广播码的方式控制收听权限。16字节的广播码每场活动换一次配合后台管理系统就是一个很轻量的内容分发方案。我个人的计划是下一步把这套多路BIS和加密广播都移植到现有固件里做成一个可配置的多语言户外导览终端。总的来说Auracast这个方向的技术壁垒不算深真正花时间的是把每一层参数调到“刚刚好”。最后再分享一个经验做Auracast开发别一上来就追求最好的音质和最大的覆盖。先把公共广播链路用最简配置跑通让手机能搜到、能连上、能出声然后像剥洋葱一样一层层加参数、加功能。音频链路的问题排查看似繁琐但只要把采集、编码、组帧、射频每一层的日志和状态分开看定位起来其实很有章法。这套思路放到以后做任何LE Audio相关产品上都能帮你少走不少弯路。
返回列表