
做杰理63系列的双机互联尤其是主从连接和数据传输这套东西在蓝牙透传、无线工装、临时组网场景里太常用了。我手头这块基于AC630N的模组前阵子就为了这个需求折腾了好几天一台设备配成主机一台配成从机让两边把串口数据通过蓝牙互相传。今天把整个过程和配置思路完整写出来给正在啃杰理SDK的兄弟做个参考。先说结论杰理63系列本身同时支持经典蓝牙和BLE做双设备主从连接你有两条路可以走——SPP透传或者BLE GATT。两条路线的配置思路完全不一样选错方向很容易在SDK里转晕。这篇文章会把两条路的原理、配置点、坑全部捋一遍也会附上我在实际调试中踩过的坑和排查思路。1. 项目全貌与主从连接方案选型1.1 63系列芯片在双设备连接场景中的定位杰理63系列是一个大统称实际型号很多常见的有AC6302N、AC6303N、AC6306N、AC6308N等还有对应Flash方案的AC636N系列。这些芯片一颗几块钱内部集成了蓝牙射频、基带、MCU、音频编解码封装小外围电路简单所以大量被用在蓝牙音箱、蓝牙耳机、串口透传模块、智能小家电上。从我实际用下来的感受63系列的定位是低成本蓝牙SoC里的万金油它不像高通那样追求极致音质和复杂协议栈也不像ESP32那样什么外设都给你堆满但它有个非常突出的优势——BLE、SPP、A2DP、HFP都在一颗芯片里SDK里通过宏开关就能切换做产品原型验证特别快。回到双设备主从连接这个需求。什么叫主从连接简单说就是两台设备各自承担不同角色一台主动发起连接、管理链路另一台被动等待连接、响应数据。典型场景有两类一类是透传设备比如两个串口转蓝牙模块一个插在电脑端一个插在设备端两边开机后自动配对然后串口数据就变成无线数据另一类是控制类设备比如手机APP连一个蓝牙模块模块再通过串口或IO去控制内部器件这个场景里手机是主模块是从。63系列这两种角色都能承担但有一点要提前说清楚63系列的硬件资源比较紧张特别是RAM版本协议栈开多了就放不下业务代码。所以做项目第一步不是写代码而是先想清楚你只需要哪套协议栈、哪种角色把用不到的功能宏全部关掉。1.2 方案对比SPP透传还是BLE GATT很多第一次接触杰理63系列的人都会卡在这里到底是走SPP还是走BLE这两个东西在蓝牙体系里的位置完全不同。SPP是经典蓝牙BR/EDR之上的一个串口仿真协议它把蓝牙链路抽象成一根虚拟串口线两个设备连接之后你往A设备的串口发数据B设备的串口就收到同样的数据反过来也一样。它的特点是开发最简单、几乎不需要写协议逻辑适合做双向透传。BLE GATT则是低功耗蓝牙里用来描述数据交互的一种抽象模型。你得自己去定义服务、特征、属性比如一个服务里有几个读特征、几个写特征、几个通知特征。它比SPP灵活得多可以做复杂的自定义协议也能精确控制功耗但相应的代码量、调试成本都上去了。我整理了一张对比表方便你直接对照自己的需求来选对比项SPP透传BLE GATT实时性高链路建立后近乎全双工串口取决于连接间隔一般几十毫秒开发难度很低几乎零协议开发较高需要设计服务与特征功耗较高经典蓝牙持续连接较低配合从设备延迟可省电对端兼容性电脑、手机基本都支持手机支持好老式设备不一定适合场景串口透传、数据采集、工装低功耗传感器、APP交互、OTAMTU限制无特殊限制按串口流控处理默认23字节协商后可达几百字节在这个项目里如果只是两个杰理设备互相传数据我的建议是优先考虑SPP。原因很简单杰理63系列做双机互联时两边都是同厂商芯片协议兼容性没有任何障碍SPP的代码量最小调试也最直接。BLE GATT虽然可以让手机参与进来但会引入服务设计、MTU协商、分包黏包一堆额外工作除非你有明确的低功耗需求否则没必要第一版就上。1.3 主从设备通信模型与数据流向选好方案之后你还得在脑子里把通信模型画清楚。所谓主从在蓝牙协议栈里是有严格区分的。在经典蓝牙SPP场景下角色分配通常是这样一个设备作为SPP Server它打开一个服务进入可发现、可连接状态另一个设备作为SPP Client主动去扫描周围的设备找到Server后发起连接。连接建立之后理论上数据是双向的但在产品逻辑里通常是Client主动发起请求Server响应结果。在BLE场景下角色对应到GAP层的Central和Peripheral。Peripheral是广播方相当于我在这里快来连我Central是扫描方发现广播后发出连接请求。连接建立后在GATT里通常还有一层主从关系GATT Server负责提供数据比如传感器数据放在Notify特征里GATT Client负责读取数据或向Server写入控制指令。这里有一个非常容易搞混的地方很多做杰理开发的人以为主设备一定要主动发数据从设备只能被动收。其实不是的。建立起连接后双向数据都可以发送区别只在于谁负责建立链路、谁负责提供服务。我画信号流向的时候通常这样看阶段主机角色从机角色建链前扫描、发连接请求广播、监听连接请求建链后主动发起数据请求响应请求、推送通知数据交互往往作为控制端往往作为服务端把这层关系搞明白后面配置就不会糊涂了。2. 开发环境与SDK工程骨架2.1 工具链准备SDK、编译器与烧录工具杰理63系列的开发环境和主流蓝牙芯片不太一样它没有那种傻瓜式的IDE你得自己组装一条工具链。我建议准备以下东西第一是SDK。杰理的SDK通常是通过代理商或官方渠道拿到的以压缩包形式发布里面包含蓝牙协议栈库、外设驱动、应用层示例代码。不同批次、不同代理商给的SDK可能存在差异所以一定要用来源清晰的版本并且保存好对应的版本记录。第二是编译器。63系列用的是杰理自家的工具链工程里一般自带编译脚本支持Windows下直接跑。热词里提到的杰理2.5编译器指的就是这套工具链的版本号从我用过的经验看不同编译器版本生成的固件大小、兼容性确实有差异同一个SDK换编译器版本后最好重新全量编译测试。第三是烧录工具。杰理芯片的烧录方式比较特殊它支持串口下载也可以通过专门的下载工具。这里要注意一个区分正常量产烧录用官方下载工具但如果你手头只有裸片或者芯片里固件坏了就需要强制下载——网上有个用STC15F104单片机制作杰理强制下载工具的方案很多DIY爱好者都在做本质上就是用一个便宜的MCU模拟杰理芯片的下载时序把固件通过UART灌进去。我自己也做过一个实测几十片板子都能稳定烧录但需要注意MCU的晶振精度要足够否则时序不对会一直下载失败。2.2 工程目录与关键模块说明拿到SDK之后第一件事不是急着改代码而是把工程目录结构摸清楚。杰理63系列SDK的目录划分不同版本略有不同但大体上都有这么几块apps应用层代码你主要在这里改。lib预编译的协议栈和驱动库里面是你不需要去碰的部分。include所有对外暴露的头文件API声明基本都在这里。tools编译脚本、烧录配置文件、升级工具。doc文档和说明虽然经常不全但值得翻一翻。应用层里和你这次主从连接需求直接相关的通常是三个文件蓝牙初始化文件、BLE配置文件、SPP配置文件。BLE配置里能看到角色定义、广播数据、连接参数这些关键内容SPP配置里能看到SPP模式是否开启、服务通道号等。我见过太多人一上来就全局搜索master、slave这种关键词想直接找到角色配置的地方结果搜出一堆看不懂的库代码。正确做法是先从应用层初始化入口看起找到协议栈初始化函数再顺着它往下看到BLE或SPP的配置文件。2.3 编译前必须确认的配置项在改任何业务代码之前有几个全局配置项必须要先过一遍不然编译出来的固件根本跑不出你想要的角色。第一个是协议栈宏开关。63系列通常通过宏来裁剪协议比如BLE_ENABLE、SPP_ENABLE、A2DP_ENABLE、HFP_ENABLE。你这次项目只做主从传输建议把音频相关宏全部关掉只保留蓝牙数据相关的部分。每关掉一个模块固件体积能小不少RAM占用也会降低系统跑起来更稳。第二个是芯片型号和Flash配置。不同63系列型号的Flash容量、RAM大小不一样工程里通常有一份配置文件专门管这些必须和实际使用的芯片对上不然编译可能不通过或者烧录后启动不了。第三个是串口参数。因为最终数据要经过串口进出串口号、波特率、数据位、停止位这些需要提前约定好两边设备保持一致。我习惯把波特率放在一个单独的头文件里统一管理避免一个设备改了一个设备没改联调时莫名其妙数据乱码。这些配置项确认完编译一个原始固件烧进板子确保基础环境没问题再开始动蓝牙连接相关的逻辑。这一步能帮你把环境问题和代码问题区分开后续调试会省很多心。3. 主从连接的核心配置3.1 从设备配置广播、服务与连接参数先来配从设备。无论走BLE还是SPP从设备的职责都是等待被连接并且要尽量让主设备容易发现自己。BLE场景下从设备就是Peripheral配置点集中在广播参数和GATT服务上。广播数据包要定义好广播内容比如设备名、是否有扫描响应包广播间隔也需要设置太短会费电太长会导致主设备扫描变慢我一般单设备场景用50ms左右的广播间隔设备数量多再适当拉长。连接参数方面连接间隔建议用20~40ms从设备延迟可以设成0把链路的实时性放到第一位。MTU最好在连接后尽快协商到247字节这样单包能传的数据更多传输效率明显提升。如果走的是SPP场景从设备要做的设置其实更简单打开SPP Server功能配置好Server通道然后配合经典蓝牙的可发现和可连接开关一起使能。从设备这边最重要的是保证开机后进入可配对状态并且设置一个容易识别的蓝牙名称方便主设备在扫描结果里一眼找到。这里有个细节值得注意杰理63系列的设备名称通常写死在广播数据或者蓝牙classic名称缓存里改完之后要clean编译一次再烧录。有些工程师改了名字但烧录后手机里看到的还是旧名字就是因为缓存没有清掉。3.2 主设备配置扫描、发起连接与连接维护主设备的配置比从设备稍微复杂一点因为它要主动做事发现目标、发起连接、维护链路。BLE场景下主设备作为Central主要做三件事。第一周期启动扫描把周围在广播的设备扫出来再通过设备名或者设备地址来筛选出目标从机。第二找到目标后调用连接接口发起连接。第三连接成功后迅速做三件事禁止从机再进广播态、发现主机的GATT服务、根据业务需要找到对应的服务UUID和数据通道UUID。SPP场景下主设备要做的其实是类似的扫描附近可连接的经典蓝牙设备根据名称或地址匹配目标匹配到后调用SPP连接接口发起连接。如果从设备开了简单配对主设备还需处理配对请求通常是一个固定PIN码两边的PIN保持一致即可。连接维护这块很容易被忽略。蓝牙连接不是建立之后就一劳永逸的了信号变差、干扰增强、设备掉电都会导致链路断开。合理的做法是主设备里加一个连接状态回调检测到断链后要么自动回连要么通知业务层做异常处理。我见过很多原型的逻辑是开机连一次断了就再也连不回来这种产品一旦出货处理售后会非常痛苦。3.3 回连与配对绑定配置配对和绑定是主从连接里最容易出幺蛾子的部分。配对解决的是这次连接是不是可信的问题绑定解决的是以后连接时能不能跳过配对确认的问题。在杰理63系列上如果你希望两个设备之间开机自动回连、不需要每次重新配对的就必须把绑定信息保存下来。芯片会把配对产生的链路密钥写入Flash存储区下次两台设备再相遇时协议栈自动用存储的密钥恢复链路。但这个机制有一个隐藏的坑如果主设备和从设备的地址不是固定的比如芯片每次开机从Flash读到的蓝牙地址变了那么绑定密钥就完全对不上回连永远不会成功只会一遍遍触发重新配对。这个问题在热词里也有人提到过杰理701芯片mac地址为什么会改变其实就是MAC地址管理策略的问题。63系列处理MAC地址时要确保在产品量产阶段就把蓝牙地址固定下来并且存储区域在固件升级时不会被擦掉。此外回连失败还有一个常见原因是主设备回连时不会发起广播而从设备自己也会主动回连主设备两边都有回连逻辑时容易发生冲突。我的建议是统一约定主设备负责回连从设备只被动等待避免两端同时扫、同时连反而把链路搞乱了。4. 数据传输通道搭建与验证4.1 SPP透传主从串口无缝对接选定SPP方案后数据传输的实现是最直接的。从设备侧打开SPP Server后链路建立时会产生一个SPP连接打开的事件此时芯片内部会把蓝牙过来的数据和串口收发打通。主设备侧连接成功后打开SPP Client通道两边就形成一条虚拟串口链路。工程上杰理SDK在SPP透传模式下通常提供一个透传回调蓝牙收到数据后会调用你注册的数据接收函数。你在这个函数里把数据直接原封不动地转发给UART发送接口反过来UART收到的数据通过SPP发送接口发出去这就完成了双向透传。我用伪代码描述一下这个链路的结构// 伪代码接口名以实际SDK为准 static void ble_spp_rx_handler(uint8_t *buf, uint16_t len) { // 蓝牙数据到达转发到串口 uart_send_data(buf, len); } void uart_rx_isr(uint8_t byte) { // 串口收到数据通过SPP通道发到对端 spp_send_data(byte, 1); }这个逻辑看起来简单实际联调时最容易出问题的不是收发函数本身而是串口中断和蓝牙协议栈之间的资源竞争。蓝牙收发一般在协议栈上下文里运行串口数据往往在中断里到达如果两边同时访问同一个缓冲区就会出现覆盖丢包。我的做法是给串口到蓝牙的方向加一个环形缓冲区串口中断只负责写环形缓冲蓝牙发送任务周期性取出缓冲数据去发送从根源上避免竞争。另一个要注意的是流控。SPP的底层是可靠传输但如果你把蓝牙发给串口的数据直接塞进串口发送寄存器串口波特率不够高时照样会丢数据。稳妥的做法是打开UART的TX FIFO并把蓝牙下行数据的速率限制在串口波特率的80%以内留出余量。4.2 BLE自定义服务读写与通知的实现如果项目需求需要手机参与或者需要精细控制功耗那就必须走BLE GATT了。这时候你需要设计一套自定义服务Service和特征Characteristic。比如我要做一个简单的双向数据通道服务UUID定义一个下面放两个特征一个Write特征主设备用来向下写数据一个Notify特征从设备用来向上推送数据。这个结构很简单但已经能满足绝大多数的数据传输需求。从设备侧要做的配置包括注册自定义服务、把特征UUID和读写权限设置好、把Notify属性的CCCD客户端特征配置描述符开放出来。这里有一个细节CCCD是手机端能否收到Notify的前提有些SDK在变量表里没有默认配置需要你手动开启。主设备侧连上后第一步是服务发现找到你要的Write特征句柄和Notify特征句柄然后做两件事往Notify特征写入一个使能通知的开关值通常是0x0001之后就可以通过Write特征下发数据并从Notify特征的回调里接收从设备上报的数据。MTU协商也很关键。BLE默认MTU只有23字节去掉协议头后有效负载才20字节做业务数据包根本不够用。连接建立后主设备应该发起MTU交换请求把MTU提到247甚至更高。杰理63系列在BLE上实测协商到247之后单包能传接近240字节效率提升还是很明显的。如果单包确实超过MTU传输层就需要自己处理分包和重组了。我一般会在业务协议里加一个1字节的包类型和2字节的序列号接收端根据序列号拼包这样即使底层分包了上层也能正确还原。4.3 双设备双向数据收发的完整验证流程配置完成、代码写完最终还是要回到数据能不能通这个问题上。我的验证流程一般是四步走。第一步先做自环测试。每个设备单独跑一个自环程序把串口收到的数据原路发回串口确认板子的串口硬件和中断没问题。第二步做单端测试。用手机蓝牙APP连从设备手机往Write特征写一串固定数据看从设备的串口是否收到同样内容再从从设备串口发数据看手机APP是否收到Notify。这一步能确认从设备自身的数据通道是否正常。第三步双机联调。两台杰理设备按规划的角色配置好先做从设备串口发数据给主设备串口收反过来也验证一遍。如果数据有乱码优先排查串口波特率和数据位是否一致如果有丢包就得回到缓冲区竞争和流控上去查。第四步压力测试。用电脑串口工具或脚本以一定的频率持续发数据跑上几个小时看有没有卡死、断链、漏包。蓝牙做数据传输最容易在长时间运行后出问题这不一定是代码Bug也可能是内存碎片、协议栈异常但都必须在量产前暴露出来。我实测下来63系列在SPP模式下做双向透传波特率115200不丢包的稳定性还是可以的。BLE模式下如果MTU协商到位、包大小控制在合理范围内做几百字节一次的命令交互也绰绰有余。5. 调试实录与问题排查5.1 扫描不到从机的常见原因这是双设备联调时出现频率最高的问题。主设备一直说扫描不到设备或者从机自己打开手机蓝牙也看不到。先查从机是否进入广播状态。很多SDK里广播开关是需要显式调用的如果初始化流程少了一步从机实际上没有在空口广播自然谁也扫不到。可以在从机上用电流表看电流如果广播正常会看到规律的脉冲电流那是射频在周期打点发出的典型特征。再查广播名称和设备地址。如果从机的设备名设置成空或者地址全零扫描端即使收到广播包也无法正确显示看起来就像扫不到。另外很多模组出厂时的蓝牙地址是一样的两台设备一起上电时可能互相把对方误认为是自己这也是我建议量产时一定要烧写独立MAC地址的原因。最后查频点问题。63系列在经典蓝牙和BLE模式下的广播频点不同如果主设备配置成了经典蓝牙扫描而目标从机在发BLE广播两边自然永远碰不上。这种低级错误很常见排查时先确认两边的协议类型一致。5.2 连接成功但数据不通的处理思路连接建立成功说明底层链路没问题数据不通多数是应用层配置写错了这类问题排查起来比扫描不到还要繁琐。先从最基础的通路查。SPP模式下检查连接事件的回调是否真的收到了连接成功通知没有收到的话PDF通道根本没有打开。BLE模式下检查服务发现是否完成——很多时候主从机的服务UUID没有对齐主设备虽然连上了却找不到对应的特征后续所有读写操作都会返回失败。再查MTU和权限。BLE下如果MTU没协商上去你写超过20字节的数据就会被协议栈拆包或者直接丢弃如果特征权限里没有打开写属性或者CCCD没使能写和通知就都不通。接着查回调接收。很多SDK的数据接收回调是在协议栈上下文中执行的如果你在这个回调里做了耗时操作会影响蓝牙栈处理后续数据包表现就是偶发丢包。我把处理逻辑都放到任务队列里回调只负责拷贝数据和发信号这个调整之后丢包率明显下降。SPP模式下数据乱码还和串口有关。我排查时会把上位机的串口助手和芯片的UART直接对接绕过蓝牙做一次串口回环测试先把蓝牙链路本身没问题证明出来再回头查透传代码。5.3 频繁断连与回连失败的排查要点最后说断连。断连比扫描不到更让人头疼因为它是间歇性的很难复现。首先看连接参数是不是太激进。连接间隔设得太短空口拥挤时碰撞概率大容易断开间隔设得太长双方又容易因为短暂失步而超时。我会建议在调试阶段用一个保守的连接间隔等链路稳定了再逐步调优。其次是天线问题。63系列芯片对天线匹配很敏感如果天线区域铺铜不合理、匹配网络元件贴错近距离通信看不出问题稍微拉开距离或中间有遮挡就立刻断连。做双设备互联时两个设备可能挨得很近也可能隔着墙壁我建议在正式测试前先用频谱仪或至少用手机蓝牙信号强度测一下模组的射频性能。回连失败的问题在3.3节已经提到大部分和绑定信息、MAC地址相关。另一个比较隐蔽的原因是从机在回连过程中没有恢复到可连接状态。有些SDK在断链后会重新进入广播但广播类型可能变成了不可连接广播或者是进入回连等待超时后直接休眠了主设备的回连请求自然没响应。排查这类问题我通常会让主设备不断打印连接状态把从机的状态切换逻辑和协议栈行为对照着看。我个人做杰理63系列的体会是做双设备主从连接难度其实不在连上而在配置的统筹上。协议栈、角色、参数、地址、绑定、数据通道每一环都息息相关任何一环的信息不对齐最终都会表现为连不上或传不通。所以拿到这类项目别急着写代码先把方案、角色、协议、参数像画电路图一样在纸上捋清楚后面的路会顺很多。如果只是做两个同型号模组的透传我更倾向于SPP方案简单直接稳定性也最容易保证。最后提醒一句SDK版本一定要固定编译器也尽量用官方推荐的版本遇到奇怪的偶发问题可以先怀疑工具链版本再怀疑自己的代码。