先说结论:E104-BT02这类BLE串口透传模块,是现阶段快速落地蓝牙应用性价比最高的方案之一。芯片原厂和模组厂商把射频匹配、协议栈、天线都给你封装好了,你不需要懂蓝牙底层的跳频、重传、功耗管理,只需把它当成一个“无线串口”用就行。
但如果你想让它真正跑得稳、功耗低、不被调试折腾到怀疑人生,光会发AT指令是不够的。这篇文章我会把从选型思路、电路设计、驱动代码到实际联调踩坑的完整过程拆开讲,开源资料放文末,建议收藏后跟着一步步做。
1. 项目到底要解决什么问题
1.1 为什么选E104-BT02而不是其他BLE方案
做嵌入式产品的人应该都有体会:蓝牙方案选型是个大坑。用ESP32自带的BLE,功能很强大,但射频天线的阻抗匹配、协议栈配置、低功耗策略这些全都得自己调,调不好就是信号差、功耗高,还很占主控的Flash和RAM资源。用一颗独立的BLE SoC,比如nRF52832,灵活是灵活,但对于很多团队来说,学习成本和研发周期根本兜不住。
E104-BT02这颗模块用的是国产的BLE 5.0芯片,走的路线和Nordic不太一样,它更接近TI的CC2541那种经典透传方案,但成本和功耗表现更好。模组厂商亿佰特把它做成标准封装,内置了完整的BLE协议栈,对外暴露UART串口和几个GPIO引脚。你只需要通过串口发AT指令,模块就会自动完成广播、连接、数据收发这些操作。对于只需要在传感器数据采集、设备参数配置、手机App控制这类场景里加一个蓝牙通道的需求来说,它可能是最快能跑通方案的模块了。
我最早接触这款模块是被同事拉去评估一个穿戴设备的通信方案,当时对比了JDY-23、CC2541、E104-BT02三款,最后选E104-BT02的原因很简单:一是模块自带PCB天线,增益曲线在2.4GHz频段比较平坦,做产品不需要额外做天线匹配;二是它支持主从一体,也就是说一个模块既能当主机也能当从机,做手机直连和做设备互连都可以覆盖;三是最重要的——它的串口透传模式用起来真的像操作一个串口设备一样简单。
1.2 这个项目适合谁,能省下什么事情
如果你正在做下面任意一类项目,这篇文章应该比较契合:
- 手头有一个MCU项目,想增加手机App控制功能,但不想把主控换成带蓝牙的SoC;
- 想做一个穿戴式传感器节点,对功耗有要求,需要在几年纽扣电池续航的范围内实现低功耗蓝牙连接;
- 需要一个能实现手机和设备双向数据透传的硬件模块,做调试工具、数据采集器、智能家居设备;
- 想搞懂BLE到底怎么工作,希望有一个上手快速、资料透明的模块作为学习对象。
这篇文章能帮你省下最花时间的三件事:第一,不用自己啃协议栈开发文档;第二,不用调试射频匹配和天线阻抗;第三,不用花大价钱买开发板。开源电路和驱动代码直接可以用,剩下的精力可以全部集中在应用层逻辑上。
2. 5分钟快速体验:串口透传模式
2.1 硬件连接和准备工作
第一件事是拿到模块后怎么给它通电。E104-BT02的引脚不多,核心引脚就这几个:
- VCC:1.8V~3.6V,推荐3.3V
- GND:地
- TXD/RXD:串口发送接收,3.3V电平逻辑
- SET:配置模式的使能引脚
- AUX/IO口:状态指示和数据流控制
我习惯用ST-Link或者CH340的USB转串口模块,给它供电并接到电脑串口。注意模块是3.3V电平,如果你的USB转串口是5V输出的,务必先确认有没有电平转换能力,否则大概率烧模块。
接线表可以先对照着操作:
| 模块引脚 | 接USB转串口或MCU |
|---|---|
| VCC | 3.3V(注意电流需求) |
| GND | GND |
| TXD | RXD |
| RXD | TXD |
| SET | 悬空或接高电平 |
接线完成后,在电脑上打开串口调试助手,波特率和模块默认一致,一般是115200,数据格式8N1,也就是8位数据、无校验、1位停止位。
2.2 透传测试:让数据飞起来
给模块上电后,串口调试助手里发送任意十六进制或ASCII数据,只要手机连上模块的蓝牙,数据就会实时出现在BLE调试App上。
这里有一个容易踩的坑:刚开始测试的时候,我在串口助手输入字符串"hello",手机App上收到的却是乱码或者缺字节。后来检查发现,串口助手和模块之间需要先把模块的发包延迟参数调好。E104-BT02默认的串口分包时间大约是10ms,如果连续发送的数据超过缓冲区就会强制打包发出。当你发送较长的数据时,建议把“串口分包字节数”调到最大,比如200字节,这样模块就会等缓冲区满或者空闲超时之后一次性发给手机,而不是发一个字节就发一个包,导致手机端数据看起来断断续续。
测试通过后,一个最简单的BLE串口透传链路就通了。整个过程不超过5分钟,这也是这个模块最核心的价值所在:不需要写一行蓝牙代码,就能把数据从MCU搬运到手机。
3. 原理拆解:它到底是怎么工作的
3.1 广播、扫描、连接——BLE通信的三步曲
BLE(Bluetooth Low Energy)的工作模式和经典蓝牙BR有本质区别。经典蓝牙从连接开始就是持续占用射频资源,而BLE采用的是“广播-扫描-连接-休眠-唤醒”的间歇式工作模式,这也是它低功耗的关键所在。
当模块上电后,第一件事是进入广播状态。它会周期性地在3个广播信道上发送数据包,这些数据包内容包含设备名称、MAC地址、服务UUID等信息。手机在扫描模式下,会在同样的信道上监听这些广播包,看到后把设备加入扫描列表。之后手机主动发起连接请求,模块回应后,双方就建立了连接。连接建立后,双方约定好连接间隔(Connection Interval),比如每20ms跳一次,空闲时芯片进入休眠状态,到时间点自动醒来收发数据。这样可以做到平均电流只有几十微安到几百微安。
E104-BT02把这一整套流程封装好了。你开机后它会自动广播,手机连上后它自动进入连接态,数据通过GATT服务的Write/Notify特征通道进行传输。整个过程不需要你去配置广播类型或者连接参数,但如果你想深度优化,它同样支持AT指令修改。
3.2 GATT、服务和特征值——数据通道的底层逻辑
BLE的数据通道不是一个简单的管道,它用的是GATT(Generic Attribute Profile)这套抽象模型。你可以把它理解为一个数据库,设备上的数据都组织成一个个服务(Service),每个服务下面又包含若干特征值(Characteristic),特征值就是真正承载数据的变量。
E104-BT02默认暴露了一个服务UUID,通常是FFE0或者其他厂家自定义的UUID服务,下面包含一个写特征Characteristic(0xFFE1),手机发数据其实是往这个特征值写数据;还有通知特征Characteristic,模块发数据给手机是通过这个特征值发Notify通知。你在BLE调试助手里看到的收发数据,实际就是读写这两个特征值的过程。
了解这点很重要,因为当你需要写App或者上位机的时候,就要扫描到这两个特征值,然后分别建立写和订阅的通道。很多开发者在用BLE调试助手里能正常收发,但自己写App却连不上,基本都是因为UUID匹配错误或者没有正确订阅Notify。
3.3 绑定(Bond)和加密——安全机制的取舍
热词里提到了“绑定(Bond)”,这个在BLE开发里是个绕不开的机制。没有绑定的时候,手机和设备每次连接都是“陌生人”,数据以明文方式传输,而且连接很容易被其他中心设备截获。绑定机制是BLE协议栈提供的一把钥匙——配对成功后,两个设备会交换并保存一组长期密钥(LTK),之后每次连接,双方通过快速验证这组密钥来确认身份,通信数据使用密钥进行加密和解密。
E104-BT02支持通过AT指令开启或禁止绑定。在默认不绑定模式下,手机连上就能使用,连接速度也更快;开启绑定模式后,首次连接时手机会弹出配对请求,输入配对码或确认后完成绑定,之后只有绑定的这台手机能访问数据,安全性显著提高。
具体的取舍建议是:
- 做低功耗传感器、广播类应用,数据本身敏感性不高时,可以不开启绑定,省去配对步骤;
- 做数字钥匙、门锁、支付或医疗数据设备,必须开启绑定和加密;
- 如果设备需要同时连接多个手机,绑定机制会有限制,需要评估业务模型。
我有个项目因为疏忽没开绑定,结果两台手机都能连上并控制设备,最后出现了一次误操作。所以安全机制不要等出了事才重视。
4. 开源电路与驱动代码的实战解析
4.1 硬件电路设计要点
既然标题里提到开源电路,那这部分是重点。模块本身很皮实,但要把电路画好、做进产品里,还是有几个细节需要注意。
电源设计是首要的。BLE模块在发射射频数据的瞬间,电流尖峰会达到几十甚至上百毫安,虽然时间只有几微秒,但如果供电电路扛不住这个瞬态跌落,模块就会复位或者射频误码。我在自己的项目里用的方案是:LDO后面放一个100μF的钽电容并联一个0.1μF的陶瓷电容,同时尽量把电容放置在模块VCC引脚旁边。对于电池供电的设备,还要考虑电池的等效内阻,如果内阻偏大,发射瞬间电压跌落会更明显。
天线净空区是另一个常见坑。E104-BT02板载PCB天线,天线走线区域需要保持“净空”,也就是说模块装在PCB上时,天线正下方的区域不要铺铜、不要走高速信号线、周边也不要放置金属外壳或者大面积的接地金属。否则天线辐射效率骤降,实际通讯距离可能从20米掉到5米。这点看起来简单,但很多工程师第一次画板都会在这里栽跟头。
串口电平匹配要注意。E104-BT02是3.3V电平,如果你的主控是5V系统,要么选5V兼容的引脚,要么加电平转换芯片。直接串电阻分压不是不行,但收发双向都得处理,容易出问题,建议直接用TXS0102这类电平转换芯片,稳定省心。
4.2 串口驱动状态机的设计
开源驱动代码里,核心部分是一个串口AT指令解析状态机和数据收发缓冲区管理。别看BLE模块负责了无线部分,主控端的串口驱动质量直接决定了数据流的稳定性。我用STM32 HAL库写的驱动,整体分为三层:
第一层是硬件抽象层,负责初始化UART、配置DMA接收、注册中断回调。这里我强烈建议用DMA+空闲中断的方式来接收模块返回的数据,而不是用逐字节中断接收。逐字节中断在波特率115200时CPU占用率很高,而且容易遗漏字节。DMA空闲中断可以把一整包数据自动收进缓冲区,收完再触发回调处理,效率高很多。
第二层是协议解析层,处理AT指令的响应。E104-BT02的AT指令集响应有两种类型:一种是无参数响应(OK),一种带返回参数(如+NAME: E104-BT02)。解析时要注意状态机的超时管理,防止模块无响应时程序卡死。一般可以设定200~500ms的超时时间,超时就重新发送或者报错。
第三层是数据分发层,负责把接收到的数据转发给用户应用程序。这里有一个设计模式上的建议:使用环形缓冲区。串口中断时只管往环形缓冲区里写数据,主循环或其他任务再从中按照FIFO顺序读取并处理,这样即使短时间内数据量很大,也不会丢数据。
4.3 HAL库下驱动OLED与BLE融合的实战经验
热词里出现了“hal库驱动oled代码”,这说明相当一部分人实际做项目时,BLE和OLED显示是绑定的。确实,一个调试设备或者手持仪器,几乎都有屏幕显示实时数据的需求。
我们在STM32F103上同时跑BLE驱动和OLED驱动时要注意一件事:OLED一般用I2C或SPI接口,两者都可能和串口共用中断资源或者DMA通道。如果你用HAL库的CubeMX生成代码,初始化顺序要保证UART先初始化,再初始化OLED,不然OLED初始化时如果触发了串口中断,可能会产生不可预期的错误。
驱动OLED的小技巧:如果用I2C接口,单次传输数据长度限制在32字节以内,否则某些型号的OLED控制器会丢数据。显示中文需要先做字库取模,取模方式可以选横向扫描或者纵向扫描,要和OLED控制器的扫描方式匹配,否则显示出来的文字会是错位的。
我后来做的调试器产品,就是把BLE收到的数据解析后直接在OLED上画波形,用了双缓冲机制——一帧画在后台缓冲区,画完之后一次性刷新到OLED,防止画面撕裂和闪烁。
4.4 无线数据与存储的配合:以SST25VF080B为例
驱动代码的开放范围里如果有Flash芯片操作代码,那通常会搭配SST25VF080B这类SPI NOR Flash做数据记录。这个芯片容量是8Mbit,即1MB,SPI接口,非常适合做传感器日志和升级文件的存储。
SPI Flash驱动要注意的坑是写前必须擦除。NOR Flash的物理特性决定了只能把1写成0,要把0写回1就必须先执行擦除操作,而且擦除的最小单位是扇区,常见的是4KB。所以如果你的代码是直接往某一地址写数据而没有提前擦除,就会出现写入数据和读取数据对不上的情况。
还有一个经验是在Flash里做数据管理时,要设计一个简单的文件系统或记录结构。最简单的方式是使用“日志式”追加写:每次写一条带时间戳的记录,写到扇区末尾后就切到下一个扇区,写满所有扇区后再从第一个扇区开始擦除重写。这样虽然做不到磨损均衡,但胜在代码量小、逻辑简单、调试容易。
5. 驱动代码核心模块的实现细节
5.1 STM32 HAL库下的串口初始化和DMA配置
我这里用STM32F103为例演示一个可用的驱动模板。硬件初始化最关键的是UART的DMA通道配置。使用CubeMX生成基础配置时,需要注意把UART的全局中断打开,同时配置对应DMA通道为循环模式,因为这样DMA才能在收到任意长度数据后持续工作,而不会因为缓冲区满了而停止接收。
一个经常被人忽视的点是DMA的buffer size设置。如果你设置的是256字节,但模块发来的数据包超过了256字节,那么DMA会先装满256字节,然后触发一次中断,但这时可能有数据已经丢失了。解决办法是设置缓冲区大小大于模块单包最大长度。E104-BT02的透传缓冲区是200字节,所以DMA缓冲区设为256字节就够用了。
配置好DMA接收,还要在UART空闲中断里判断一包数据的结束。UART的空闲中断触发时机是总线空闲,也就是一个字节和下一个字节之间超过了一个字符的时间。DMA接收加空闲中断的组合,是串口数据包最可靠的处理方式。
5.2 环形缓冲区:避免数据丢失的关键设计
直接给一个精简的环形缓冲区实现会很有用。核心数据结构是两个索引,读索引(read_index)和写索引(write_index)。每次DMA空闲中断发生时,把DMA当前接收到的数据长度和读索引之间的差值计算出来,这个差值就是有效数据长度,然后把它写入环形缓冲区。主程序循环不断从环形缓冲区中取出数据解析,这样即使主循环有短暂阻塞,串口数据也不会丢,因为DMA还在后台持续接收。
实现环形缓冲区时要注意一个巧妙的点:缓冲区大小必须是2的幂次方,这样可以用位运算index & (size - 1)代替取模运算,效率高很多。缓冲区处理函数里不需要加锁,因为单生产者单消费者的场景下,只要读索引和写索引各自只有一个线程修改,就不会发生竞争。
5.3 AT指令交互与状态机解析
E104-BT02有若干组常用AT指令:设置设备名称、设置广播间隔、设置串口波特率、查询MAC地址、设置绑定开关等。指令交互的流程一般是:主控发送一条AT指令,模块执行后返回OK或者错误码。
驱动代码里状态机的好处在异步交互场景下就会体现出来。比如你发送一条AT指令后,不能立刻发送下一条,必须等待模块的响应。如果不做状态机,用简单的延时函数死等,程序跑起来会很不流畅——尤其是在初始化时需要连续配置多项参数时。状态机只需要三个状态:空闲、等待回复、完成。每次发指令时把状态置为等待回复,同时开启一个超时定时器,收到匹配的响应后状态切回空闲并通知上层。
我实际项目里踩过一个坑:初始化时发送AT指令太密集,模块反应不过来,导致某条指令的响应和上一条指令的响应混在一起,解析出错。解决方案是每条指令发完后至少等50ms再发下一条,同时状态机加入“清空输入缓冲区”这个动作,确保上一包的残留数据不会影响后续解析。
6. 常见问题与排查技巧实录
6.1 连不上、配对失败、数据乱码的排查方向
这里把调试中常见的现象、原因和解决办法整理成一个速查表,方便你遇到问题时按图索骥:
| 现象 | 可能原因 | 解决与验证方法 |
|---|---|---|
| 手机扫描不到模块广播 | 模块可能未进入广播态;广播间隔设置过长;供电异常 | 用串口发送AT+RST重启模块;检查电源电流是否正常;缩短广播间隔到30~50ms |
| 能搜索到但连接失败 | 连接参数不一致;模块已绑定其他手机;信号过于微弱 | 检查是否开启了绑定模式;恢复出厂设置;靠近模块再连接 |
| 连接后数据无法接收 | 未订阅Notify特征;服务UUID不匹配;连接间隔太大导致数据延迟 | 确认使用FFE0/FFE1 UUID;在调试助手里手动点击订阅;适当缩短连接间隔 |
| 串口发送乱码或丢字节 | 波特率不匹配;发送节奏太快;缓冲区溢出 | 确认模块实际波特率;发送间隔大于20ms;增大串口缓冲区 |
| 数据传输极不稳定,偶尔断连 | 天线附近有金属遮挡;供电跌落;射频干扰 | 检查天线净空区域;加电容强化电源;尽量远离WiFi路由器和USB 3.0接口 |
6.2 功耗异常的排查
低功耗是BLE的卖点,但很多人测下来发现电流居高不下。常见原因有三个:
第一,模块没有实际进入睡眠状态。E104-BT02在连接状态下会按照连接间隔周期唤醒,如果连接间隔设得太短,比如7.5ms,功耗自然高。对于实时性要求不高的传感器上报场景,把连接间隔调到100ms以上,平均电流会有数量级的下降。
第二,MCU的外设没有关完。很多项目只注意模块的功耗,却忘了主控MCU的GPIO上拉电阻、传感器、LDO的静态功耗。一个简单的测量方法:把MCU程序烧成sleep模式,用万用表测整板电流,如果电流还是偏高,就一块块断开外设排查。
第三,广播时间太长。模块上电开始广播到连接建立的时间段内,广播功耗远高于连接状态的功耗。如果设备是长时间待机、按键唤醒后临时广播,建议把广播超时时间设置得短一点,比如30秒,超过就自动进入深度睡眠。
6.3 BLE和经典蓝牙BR的区别:为什么选择BLE
热词里专门提到了“蓝牙BR BLE区别”,这里一句讲透:BR/EDR(经典蓝牙)适合持续大流量传输,比如蓝牙耳机听歌、蓝牙音箱放音乐;BLE适合小数据量、低功耗、偶发通信的场景,比如传感器读数、设备控制、广播信标。E104-BT02这类模块走的就是BLE这条路,不要指望拿它传音频流。
在实际项目选型时,如果应用场景是每秒钟传好几KB的连续数据,那么BLE的吞吐量会成为一个瓶颈,E104-BT02这类透传模块不太适合;如果只是每秒几十个字节的控制指令或者状态信息,BLE就是最优解。
7. 从模块到产品:开源设计可以怎么改
开源电路和驱动代码的价值在于,你可以站在别人的肩膀上做二次开发。但要从demo变成产品,有几个改动点值得重点关注。
第一,把模块和主控集成到一块PCB上。开发板上模块是独立焊在底板上的,产品设计时可以直接把模块的引脚和主控画在同一块板上。这时注意热设计,如果产品有金属外壳,天线附近不要放螺丝柱和地平面。
第二,增加供电管理电路。开发阶段直接用USB供电没关系,产品阶段要设计电池充放电管理、低电量报警、电源路径切换。推荐用Torex或TI的低静态功耗LDO,待机电流能控制在微安级别。
第三,驱动代码做状态机强化。开发板的驱动代码往往是单线程循环结构,产品上如果有多个外设和通信协议同时工作,建议引入RTOS或者事件驱动的状态机框架。把BLE驱动封装成独立任务,通过消息队列和其他任务通信,这样可以避免中断竞争和软件死锁。
有一个项目跑在FreeRTOS上,BLE数据接收用的就是队列加信号量的方式。串口DMA中断接收数据后,判断一包完整数据,然后通过信号量通知BLE任务去读取数据;BLE任务处理完数据后,再通过队列把结果转发给显示任务或者存储任务。这样设计的好处是每个任务职责单一,出问题时定位也容易。
8. 写在最后的经验分享
E104-BT02是我用过的BLE模块里,上手曲线最平滑的一个。它把最难的无线部分藏起来了,让你能集中精力解决业务问题。但“藏起来”不等于“不存在”,理解背后的GATT、连接间隔、广播机制、绑定原理,才能真正把它用好,出问题时也知道往哪个方向查。
根据我个人的开发习惯,拿到这类模块后不要急着写完整产品代码,先花10分钟做一次裸机的AT指令配置和透传测试,确认模块本身工作正常。然后逐步加入MCU主控、OLED屏幕、传感器等外设,每加入一个外设就做一次验证,这样即使出了Bug也能快速定位到是哪一个环节的问题。
另外再分享一个调试小技巧:手机端的BLE调试助手不要只用一个。建议同时装两个,一个用于连接和收发数据,另一个用来扫描和观察广播报文。当连接遇到问题时,用扫描工具看模块的广播UUID和MAC是否正常,这能帮你快速判断是模块没有正常工作,还是手机连接参数配置有误。
开源资料里还有针对SST25VF080B的驱动和OLED驱动,这部分代码可以直接移植到其他项目里。核心代码写了详尽的注释,遇到看不懂的宏定义或者回调函数,对照本文的流程过一遍,基本就能看通了。
最后还是那句话,不要被“开源”两个字迷惑,开源给你的是一条快速的起点线,不是终点。真正让项目变得稳定可靠的,是你自己在调试中踩过的坑和不断完善的驱动代码。