恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Auracast广播音频开发实践:基于BT2106C的一对多音频同传方案
首页
资讯中心
/
Auracast广播音频开发实践:基于BT2106C的一对多音频同传方案
Auracast广播音频开发实践:基于BT2106C的一对多音频同传方案
发布时间:2026/9/8 16:27:08
上个月我第一次把一套 Auracast 广播音频模块真机跑通。调试间里两台手机、一副 TWS 耳机外加协议栈日志窗口几乎同时开始输出同一段音频波形——这几个设备之间没有做任何配对也没有建立任何连接这在传统蓝牙音频链路里几乎不可能做到。音频来源是一块 BT2106C 蓝牙广播模块外部 codec 把模拟声音变成 I2S 数据送进模块模块内部完成 LC3 编码后以广播等时组BIG的方式发出去。这篇把这次开发的选型过程、工程落地、效果实测和踩坑记录完整写下来。为什么 Auracast 适合一对多的音频分发、BT2106C 怎么快速跑起来、空中接口参数怎么设以及最终同步性、延迟和穿墙能力的实测结果都在后面。还有几个不用真机根本看不出来的坑尤其是那些排查链路建议做同类项目的朋友认真看。1. 从“一拖二”到无上限同听为什么最终选了 Auracast 路线1.1 现场讲解和同传场景的真实痛点项目背景是做一套现场语音导览设备。一个讲解员、一堆游客游客用自己的手机或耳机收听同一路声音源。最初方案用的经典蓝牙音频发射器一个发射器最多稳定连一到副接收端而且很多耳机根本不支持同时连接常常一台设备刚连上另一台就被挤掉了。也试过 Wi-Fi 方案接收端要先配网、再打开 App 选择播放地址游客操作门槛太高现场基本跑不起来。Auracast 在这类场景里几乎是量身定做的。它走的是 BLE 广播通道发射端的音频数据通过周期性广播发出去任何支持 LE Audio 的接收端只要处于可发现状态就能直接收听。不需要配对、不需要输入密码、不需要连接路由器跟打开收音机听一个频段一样简单。这个特性对博物馆导览、会议室同传、影院辅助听力这类场景特别关键因为使用者大概率不是技术人员越少操作的方案越好。1.2 与经典蓝牙转发方案的差异把几种方案放一起对比就非常直观方案最大接收端数量配对要求部署复杂度延迟表现成本经典蓝牙音频转发通常 2部分芯片支持更多但稳定性差需要配对低低约 100~200ms中BLE Audio 单播CIS理论上多个受芯片连接资源限制需要配对/发现中低高Auracast 广播BIG几乎无上限不需要配对低中约 200ms 级别低Wi-Fi 音频无上限但依赖网络需要入网高较高高我一开始对 Auracast 也有疑问广播通道这么忙音频能被多个接收端稳定收到吗实际测试下来广播音频在接收端数量上确实有天然优势因为接收端只是被动监听不占用发射端的连接资源发射端完全不需要为接收端数量的增加付出额外的空中带宽。对比之下CIS 单播方案每增加一路收听设备发射端就要多维护一条逻辑链路连接数一旦上去调度复杂度就会指数级上升。1.3 BT2106C 的选型理由选 BT2106C 之前我评估过三类方案。第一类是用一颗高性能 MCU 外挂蓝牙射频芯片音频编码和协议栈全自己写灵活但开发量太大第二类是用手机做发射端配合 BLE Audio 广播 API功能够用但没法脱离手机不适合做成独立硬件第三类就是 BT2106C 这类集成了蓝牙协议栈和 LC3 编码的专用模块。最后选了第三类理由很直接模块内部已经跑好了 BIG 广播协议栈和 LC3 编码器我只需要通过 I2S/PCM 接口把音频数据喂给它等于把最复杂的部分黑盒化节省了大量开发时间。另外几个硬件细节也值得提。模块的 PCB 天线是板载的体积小方便直接贴到产品主板上音频输入支持 I2S 和 PCM 两种模式兼容常见的音频 codec功耗在可接受范围后续做电池供电产品还有优化空间。还有就是厂商 SDK 里带了一个 Auracast 发射端的参考例程虽然不是拿来就能量产但至少把 BIG 配置、广播启动、协议栈回调这些骨架都搭好了对照着改比从零开始容易得多。2. 先理解 Auracast 的广播模型BIG、BIS、事件间隔和重传预算2.1 一列火车理解 BIG 与 BISAuracast 的底层机制是 BISBroadcast Isochronous Stream和 BIGBroadcast Isochronous Group。用一列火车来类比会直观很多BIG 就是整列火车负责把一路或多路音频数据从一个发射端送到任意多个接收端BIS 是其中的一节车厢对应一路独立的音频流。一个 BIG 里可以挂多个 BIS比如同一场发布会BIS 0 放现场原声BIS 1 放中文同传BIS 2 放英文同传接收端根据自己的需要选择听哪一节车厢。火车每次停靠站台就是一个广播事件。事件到站的时刻是事先约定好的接收端不需要和发射端连接只要在正确的时间点打开射频窗口等这列火车经过取走属于自己的车厢数据即可。这个周期性到达的音频数据包在协议里叫 SDU里面装的就是经过 LC3 编码后的音频帧。接收端收集到足够的 SDU 后按时间顺序解码播放就形成了连续的声音。2.2 BIG 参数直接影响延迟和覆盖能力刚开始调参数时我对 BIG 周期、SDU 大小、事件间隔这些概念之间的关系很模糊。实际调过之后才明白它们其实是同一件事的不同侧面。LC3 编码器通常以 10ms 为一个音频帧所以发射端一般也把 BIG 周期的 SDU 间隔设成 10ms这样一个 SDU 就装一帧 LC3 数据。一个很实用的计算公式是SDU 大小 比特率 × 音频帧时长 / 8。比如 32kbps 比特率、10ms 音频帧每个 SDU 就是 40 字节。如果选择 64kbps 高音质SDU 就会变成 80 字节。SDU 越大单个事件里需要发送的空中数据越多射频占用时间越长功耗也越高但音质更好。下面是几组我实测过的参数配置参数组合SDU 大小延迟感受续航影响适用场景32kbps10ms重传 1 次40 字节约 180~220ms较低语音导览、同传48kbps10ms重传 2 次60 字节约 200~250ms中音质优先的室内场景32kbps20ms重传 3 次80 字节约 260~320ms较高强干扰环境有一点必须提醒SDU 间隔和 BIG 周期不是可以随便设的。如果 SDU 间隔是 10msBIG 周期最好也是 10ms 的整数倍。强行把 BG 事件间隔设得比 SDU 长接收端就会因为拿不到完整数据而频繁重同步声音断断续续。这种问题在日志里不一定有明显报错排查起来非常费劲。2.3 重传是 Auracast 覆盖的隐藏旋钮普通 BLE 广播包发一次就结束丢了就丢了。但 BIS 广播不一样一个 BIG 事件内部可以由多个子事件组成同一个 SDU 可以在不同子事件里重复发送接收端只要在任意一个子事件里成功收到完整数据就算这一帧没丢。这个机制对覆盖能力的帮助远大于单纯加大发射功率。我实测过一个场景发射功率从 0dBm 加到 6dBm隔一堵墙的丢包率只下降了几个百分点但把重传次数从 1 调到 3同样环境下丢包率直接降了一个数量级。原因也很简单室内的多径衰落和突发干扰是随机的同一包数据在稍后几十微秒再发一次大概率能绕开前一次遇到的干扰窗口。所以调覆盖时先看重传配置再决定要不要动功率。3. 工程搭建与 SDK 准备最容易拖慢开发节奏的几个环节3.1 先锁定 SDK 版本别直接追最新BT2106C 这类模块的 SD K大多把 Auracast 功能放在特定的发布分支里。我第一次拿到模组时习惯性地拉了厂商 SDK 的最新代码来编译结果一连串接口对不上编译报错几十行。后来才发现最新代码里 Auracast 相关组件正在重构接口和稳定分支完全不一样。正确做法是先找到模块厂商提供的 Auracast demo 工程确认它在哪个 SDK 版本下能正常编译通过然后把整个 SDK 锁定到那个版本。后续不要随便升级大版本除非有明确的功能修复需求。开发过程中如果看到文档里说“需要更新协议栈”也要谨慎评估协议栈升级往往会导致广播参数或回调接口变化牵一发动全身。3.2 跑通 demo 前的硬件细节确认软件之前先把硬件接对。我踩过电源纹波的问题模块发射瞬间的峰值电流会让电压跌落导致射频输出功率不稳定接收端就会出现随机卡顿。建议给模块单独加一颗低 ESR 的 10uF 陶瓷电容靠近供电引脚放置。天线区域同样不能马虎。BT2106C 板载天线周围要禁空不能铺铜不能走信号线否则天线失谐后辐射效率直线下降。我第一次测试时天线旁边贴了一颗电源芯片空旷距离直接缩短了三分之一重新改板后才恢复正常。I2S 信号线也尽量短串行时钟和数据线如果走太长高速翻转时容易产生振铃影响音频采样的稳定性。3.3 用 demo 验证接收端兼容性把 demo 工程编译烧录进去之后别急着改代码先做一次完整的接收端兼容性测试。用手机系统自带的音频广播功能扫描再用支持 LE Audio 的 TWS 耳机试听。接收端能搜到广播、能正常出声说明 SDK 的 Auracast 实现本身是符合规范的后面出了问题可以放心排查自己的业务逻辑。如果这一步就失败了优先怀疑硬件问题比如天线、电源、时钟精度而不是急着调协议栈参数。demo 是厂商验证过的在 demo 跑不通的情况下改业务代码会把 SDK bug 和自己的 bug 混在一起排查成本非常高。4. 代码落地广播组创建、LC3 音频注入与小状态机设计4.1 广播组参数怎么配置跑通 demo 之后第一件正事就是按自己的音频链路配置 BIG。核心参数是 SDU 间隔、SDU 大小、BIS 数量和重传次数。参考代码片段如下接口名以实际 SDK 为准/* 简化示意具体API按厂商SDK调整 */ #define AUDIO_SAMPLE_RATE 32000 #define LC3_BITRATE 32000 #define ISO_INTERVAL_MS 10 static const ble_audio_broadcast_cfg_t bt_cfg { .broadcast_name Tour_Audio, .big_period_ms ISO_INTERVAL_MS, /* 一个BIG周期 */ .num_bis 1, /* 单声道一路BIS */ .bis[0] { .sdu_interval_us 10000, /* 每10ms一个SDU */ .max_sdu_bytes LC3_BITRATE * ISO_INTERVAL_MS / 8, /* 40 */ .phy ERA_PHY_1M, .retransmissions 2, /* 子事件重传次数 */ }, };max_sdu_bytes的计算逻辑就是前面提到的公式32000 × 0.01 / 8 40 字节。如果音频源变化或者想提高音质只需要调整LC3_BITRATE和ISO_INTERVAL_MSSDU 大小会自动跟着变不用手动硬编码。4.2 启动流程与状态回调BIG 广播的启动不是一个同步动作而是一个异步流程。调用启动接口后协议栈需要时间完成广播参数的准备和空中链路的建立结果通过回调函数通知应用层。所以代码里必须有一个清晰的状态机至少包含初始化、启动中、运行中、停止中四个状态。static void device_event_handler(device_event_t event) { switch (event.type) { case BIG_STARTED: /* 协议栈已经完成BIG广播的建立可以开始写音频数据 */ audio_thread_start(); break; case BIG_STOPPED: audio_thread_stop(); break; default: break; } }很多初稿代码会在调用完启动接口后立即开始写入音频数据结果前几个 SDU 被协议栈丢掉了接收端播放时开头就缺一段。正确做法是等BIG_STARTED回调通知后再启动数据写入线程宁可晚几十毫秒开始也不能丢数据。4.3 音频注入任务的设计音频注入是整个系统里最容易出问题的部分。我的实现方式是用一个独立任务负责从环形缓冲区读取 I2S DMA 送进来的 PCM 数据每满 10ms 就做一次 LC3 编码然后把编码后的 SDU 写入协议栈提供的广播发送接口。void audio_processing_task(void* arg) { uint8_t pcm_buf[320 * 2]; /* 32kHz采样率10ms16bit单声道 */ uint8_t enc_buf[40]; /* LC3 32kbps10ms帧的编码输出 */ while (running) { if (ringbuffer_read(pcm_buf, sizeof(pcm_buf)) ! 0) { continue; } lc3_encode(pcm_buf, enc_buf); ble_audio_broadcast_write(big_handle, 0, enc_buf, sizeof(enc_buf)); } }这个任务的关键是时序稳定。如果任务被更低优先级的业务代码卡住SDU 写入就会出现缺口接收端检测到序列号跳变后要重新同步表现就是声音卡顿。优先级设置上建议音频任务优先级低于协议栈任务、高于普通业务任务给协议栈留出足够的调度空间。4.4 从 demo 到产品的状态机差异厂商 demo 例程通常只是循环播放一段内置音频完全没有处理真实的业务场景。切换到实际产品时我加了三类逻辑。第一是音频源健康检查外部 codec 没初始化好或者 I2S 信号丢失时不能直接把垃圾数据送进编码器。第二是静音帧填充即使当前没有有效音频也要持续发送编码后的静音帧保持 SDU 序列号连续接收端才不会因为长时间没收到数据而退出播放状态。第三是异常恢复。中途如果 BIG 广播意外终止比如协议栈复位或者射频异常必须能自动重新走一遍启动流程而不是停在错误状态。这个在开发测试阶段不一定触发但到了现场就会变成致命问题。我在整个状态机里加了一个看门狗式的定时检查超过 3 秒没有收到协议栈事件就主动重新初始化广播链路。5. 真机实测同步性、延迟和穿墙数据5.1 测试环境与接收端设备测试分两轮进行。一轮在办公楼室内测试穿墙能力和多设备共存另一轮在户外开阔广场测试极限距离。发射端用 BT2106C 模块天线是板载 PCB 天线发射功率分别测了 0dBm 和 6dBm 两组。接收端用了一台 Android 15 手机、一台支持 LE Audio 的 TWS 耳机以及一个厂商提供的测试接收棒。测试方法很简单发射端固定接收端拿着逐步远离在固定距离点停留 30 秒观察音频是否连续、是否出现爆音或卡顿。同时用协议栈日志抓取每个距离点的 RSSI 和丢包率作为后续调优的参考。5.2 距离与穿墙实测结果场景距离/遮挡0dBm重传1次6dBm重传2次户外开阔10m流畅流畅户外开阔20m流畅流畅户外开阔30m偶发卡顿流畅户外开阔40m几乎不可用偶发卡顿室内隔1堵轻墙约12m轻微卡顿流畅室内隔2堵墙约6m明显断续偶发卡顿从这个结果可以清楚看到加功率的效果远不如加重传。0dBm 重传 1 次在隔两堵墙时已经基本不可用但同样的发射功率下把重传次数提高到 2仍然能维持可用状态。实际产品我最后选择的是 6dBm、重传 2 次的组合覆盖和功耗相对平衡。5.3 延迟和多设备同步表现延迟实测结果在意料之中。从音频信号进入发射端 I2S 输入到 TWS 耳机真实发声整体延迟大约在 180~230ms 之间。这个延迟包含了 LC3 编码、BIG 广播、接收端缓冲、解码播放几个环节对于导览和同传场景完全可接受人耳感知不到明显的不同步。多设备同步性是我最关心的一个指标。用两台 Android 手机同时接收同一路广播人耳听不出先后差别用录音设备抓波形看两台手机之间大约相差 30~80ms。原因是每台手机接收端的缓冲策略不同有的为了抗丢包会把缓冲区开大一点。广播源只有一个所以只要接收端本地时钟同步机制正常就不会出现一台机器声音落后另一台太多的离谱情况。5.4 参数调优前后的数据对比我专门做了一组对照测试验证重传次数对无线性能的影响。测试场景是室内隔一堵轻墙固定发射功率 6dBm接收棒放在约 12 米处每轮测试播放 3 分钟统计丢包率重传次数平均RSSI丢包率主观听感0-78dBm18.6%频繁卡顿1-78dBm4.2%偶发爆音2-78dBm1.1%流畅3-78dBm0.5%流畅可以看到信号强度几乎没变但丢包率从 18.6% 降到了 0.5%。这就是子事件重传机制的价值。重传 3 次相比重传 2 次的提升已经不明显但功耗有一定增加所以最终量产配置选了重传 2 次。6. 踩坑链路复盘从“接收端搜不到”到十五分钟后的时钟漂移6.1 坑一接收端彻底搜不到广播这个坑出现在业务工程改造完成后。厂商 demo 在测试机上可以被搜到但我把代码迁移到自己的工程后手机端显示找不到任何 Auracast 广播源。第一反应是配置项丢了但逐个对比后广播参数完全一致这就很诡异。排查链路如下先用 BLE 抓包工具在空中抓广播包发现模块确实在周期性地发送广播数据说明物理层没问题进一步解析广播包内容发现广播地址类型被设置成了随机私有地址而系统级 Auracast 接收端只会列出使用公开广播地址或固定广播地址的音频广播源。最终的修复方式是修改广播地址类型配置并确保广播元数据里填充了正确的广播名称和可用的 BIS 列表。手机端才能正常识别。这个坑的教训是Auracast 广播能不能被系统级接收端发现不只取决于射频和 BIG 参数还取决于广播元数据是否符合规范尤其是广播地址类型和广播名称字段。单独用自定义接收端调试没问题但换到标准手机就失灵首先查这两项。6.2 坑二隔墙后声音断断续续第一次现场测试时空旷区域一切正常但人一走到柱子后面或隔墙位置声音就开始打磕巴。当时我第一反应是发射功率不够于是把功率从 0dBm 一路加到 6dBm结果改善非常有限。随后用日志定位 RSSI 数据发现隔墙场景下信号强度并没有特别差平均在 -75dBm 左右但丢包率却高达两位数。这说明问题不在链路预算而在多径衰落和突发干扰。多径衰落的特点是信号强度看似还行但某些子载波/时隙上会出现深度衰落单次发送大概率丢失。解决思路转向提高重传次数实测重传从 1 调到 2 后丢包率明显下降。现在遇到这类问题我的排查顺序是先看 RSSI如果信号强度高于 -80dBm 但仍然卡顿优先调重传次数如果 RSSI 很低再考虑加功率和天线优化。盲目加功率既增加功耗又在强干扰环境里帮助不大。6.3 坑三播放约十五分钟后延迟明显漂移这个坑最隐蔽。测试刚开始时一切正常但连续播放到十五分钟左右手机上看到的声音开始明显比现场声源的节奏慢而且这个偏差还在缓慢增大。这不是固定延迟而是一种持续漂移。排查链路先排除了发射端问题因为我同时用接收棒监听接收棒输出和现场声源同步正常。问题锁定在手机接收端。进一步分析是接收端的本地时钟和发射端时钟之间产生了累积偏差。BLE 广播音频虽然带有周期性同步机制但接收端如果长时间没有进行时钟校准或者校准任务被系统调度延迟累积的偏差就会反映成声音速度变慢。解决方式分两步。第一步把发射端的事件间隔和时钟精度相关配置调准确保发射端的广播时间轴尽可能稳定。第二步检查接收端协议栈线程优先级保证时钟校准任务不被低优先级任务阻塞。调完后再做一小时连续播放测试漂移控制在 20ms 以内基本感知不到。6.4 坑四I2S 主时钟精度导致音调轻微异常有段时间播放声音总觉得音调稍微偏高或偏低听感上很像磁带转速不对。使用音频分析仪抓输出波形才发现问题不在无线链路而在音频源本身I2S 总线的采样率标称 32kHz实际却因为主时钟精度不够导致数据速率与理论值有偏差。LC3 编码器对输入采样率非常敏感。如果实际采样率低于标称值单位时间内产生的 PCM 数据不足发射端为了保证 SDU 序列号连续会在编码前补帧补出来的帧会让接收端解码后的时长变长听起来就是声音拖慢。反之采样率偏高则声音偏快。最终解决方法是给 codec 使用独立的外部 MCLK 时钟源精确锁在 32kHz 的整数倍频率上然后由 codec 将主时钟分频产生 I2S 的位时钟和帧时钟彻底解决了漂移和音调问题。6.5 坑五调试串口日志与广播并发导致卡顿这个坑属于低级问题但很容易被忽略。开发阶段为了方便我把协议栈调试日志的串口波特率设得非常高而且日志等级开到了详细级别。结果在音频广播的同时串口持续输出大量日志占用了 CPU 和中断时间音频任务偶发得不到及时调度声音就开始一卡一卡。排查时一度以为是射频干扰后来发现拔掉调试串口线就正常。最终处理方式是把调试日志等级调到警告以上并降低串口打印频率。量产固件里调试日志必须关掉或只保留错误级别的输出这个建议看起来基本但确实能省掉很多现场的无线调试时间。7. 如果再来一次我会先用一整天专门测天线和时钟整套项目做完回头看最大的经验就是Auracast 开发里最耗时的不是写代码而是确认硬件基础和参数配置。协议栈接口再复杂也有文档可查但天线匹配、电源纹波、I2S 主时钟精度这些问题不实测根本发现不了而且发现得越晚改造成本越高。如果重新做一次选型和开发我会把验证顺序调整到先测模块在各种环境下的覆盖和丢包确认天线和电源没问题再测 I2S 音频链路的采样率精度确保 LC3 编码器输入是准的最后才开始写业务代码。按这个顺序走后面调协议栈参数时就不用怀疑底层硬件排查周期能缩短一半以上。这套基于 BT2106C 的 Auracast 广播音频方案最终交付时接收端包括手机、TWS 耳机和专用接收棒都能在同一时间稳定收到现场音频。对我个人来说最有成就感的瞬间反而不是跑通的那一下而是把十五分钟后时钟漂移问题定位到协议栈线程优先级的那一刻。这种坑文档不会写出来只有真机长时间测试才能遇到。