我在实际项目里用过不少蓝牙模块,从早期蓝牙2.0的串口透传,到后来支持BLE 4.0的低功耗方案,再到如今各类带原子服务的SoC模组,绕了挺大一圈。如果你现在让我给一个新项目选BLE模块,E104-BT02仍然是一个非常值得优先考虑的选项。它不仅把BLE核心协议栈、射频匹配统统封装好了,还直接给出开源电路和驱动代码,这意味着你不需要纳斯达克级别的射频经验,也不用把蓝牙协议栈啃完,就能在几小时内让设备拥有低功耗蓝牙连接能力。
这篇文章我会从选型思路、硬件搭建、驱动代码资源、调试过程再到常见坑位排查,尽量用一个“做产品”的视角把E104-BT02的落地全过程讲透。无论你是刚入行的嵌入式工程师,还是被领导临时点将做无线方案的机械工程师,这篇文章都适合你,因为核心思路是:用最小成本把BLE跑起来,并且跑得稳。
1. 核心设计思路:为什么直接用E104-BT02而不是自研射频方案
1.1 选型背后的现实问题:射频没那么好搞
先聊一个绕不开的问题:BLE通信的本质难点在射频链路。你写驱动、跑协议栈、做应用层,这些大部分是经验问题,花时间总能有结果。但射频匹配不一样,天线阻抗匹配差一点,空旷距离可能从50米缩水到5米;PCB布局和参考地处理不好,辐射杂散直接过不了认证。市面上有很多国产BLE SoC芯片,比如某迅、某凌、某微,性能其实都不差,但要把芯片变成模块,你需要处理晶振、天线、匹配网络、走线阻抗等一系列问题。这些环节里任何一处翻车,定位起来都非常痛苦。
E104-BT02这类贴片式模块方案的本质,是把“射频硬骨头”提前替你啃干净。它基于一颗成熟BLE SoC(具体型号为某主流国产BLE 5.2芯片),模块上已经集成了晶振、电感电容匹配网络、PCB天线或IPEX座子,出厂前经过射频校准。我拿到的批次实测,空旷环境-0dBm发射功率下,手机在室内隔一堵墙仍能稳定连接,视距条件下传输距离大约在40到60米之间,完全可以满足大多数产品需求。
1.2 从“做芯片”到“做产品”:驱动代码和开源电路省了什么
我接触过不少工程师,第一次用这类模块时总会问:模块自带协议栈吗?需要自己写GATT服务吗?需要付费SDK吗?答案是:E104-BT02的ROM里已经固化好了BLE完整协议栈,你不需要关心Controller和Host层如何交互。它的对外接口非常简单——串口。你发什么数据,它就以Notify或者Write的方式发出去;对端发来数据,它通过串口吐出来。
但这并不代表你不需要写任何代码。你仍然需要完成三个层面的工作:模块初始化(波特率、广播参数、连接参数)、数据透传逻辑(封包、解析、分包)以及状态管理(连接、断开、休眠唤醒)。而E104-BT02官方最厚道的地方,是把这块内容以开源电路和驱动代码的方式直接放出。不仅仅是原理图PDF,还包括了可编译的STM32 HAL库工程、串口驱动代码以及注解详细的示例。这意味着你不需要从零研究寄存器手册,直接从示例代码改起就行。
1.3 适用场景与边界:不是所有项目都适合它
任何方案都有边界。E104-BT02最适合的典型场景是:传感器数据采集上报、智能门锁/门禁、便携医疗设备、Beacon定位标签、简单遥控器。这些产品的共同点是数据量小、对实时性要求不苛刻、从机(模块端)需要低功耗。
如果你的项目对数据吞吐量有极高要求(比如持续音频流),或者需要模块作为BLE Central主动扫描并连接多个从机,E104-BT02这种透传模块就不太合适了。这类模块的设计定位是“外设/从机”,Central角色虽然固件里也支持,但从稳定性和功耗来说,远不如直接用ESP32这类双模芯片来的灵活。选型阶段要认清楚:这是低功耗BLE透传模块,不是万能的无线SoC开放平台。
2. 硬件设计与开源电路解析:一次点亮的关键细节
2.1 模块引脚与最小系统接线表
在动手画板之前,先看模块引脚定义。E104-BT02通常采用LCC封装,引脚间距2.0mm或1.27mm,不同批次可能有差异,以官方数据手册为准。核心引脚就那么几个:VCC、GND、TXD、RXD、RST、SET(或称之为连接/唤醒引脚)。我惯用的接线方式如下表所示:
| 模块引脚 | 功能说明 | 接MCU端 | 备注 |
|---|---|---|---|
| VCC | 电源输入(典型3.3V,范围2.0~3.6V) | 3.3V | 滤波电容100nF+10uF并联 |
| GND | 地 | GND | 铺地要完整 |
| TXD | 模块串口发送 | MCU的RX | 电平需匹配(3.3V) |
| RXD | 模块串口接收 | MCU的TX | 注意MCU如果5V需分压/电平转换 |
| RST | 复位(低有效) | 普通GPIO | 可悬空,但建议受控 |
| SET | 模式切换/唤醒 | 普通GPIO | 不同固件功能不同,必须看手册 |
另外,部分E104-BT02变体还单独引出LED、ADC或PWM引脚,这类引脚一般用于扩展透传之外的定制功能。如果没有需求,保持悬空即可,千万不要强行接地或接3.3V,否则可能进入测试模式或异常功耗状态。
2.2 电源设计的隐藏要求:小模块也怕饿肚子
BLE模块有一个容易被忽略的特性——瞬态电流尖峰。BLE广播时,每次广播事件会瞬间拉高电流(典型值5~15mA,峰值可达数十mA,取决于发射功率)。如果你的电源是LDO输出,且输出电容只有100nF,纹波会非常明显,严重时会导致模块射频性能劣化,甚至反复复位。
我的做法是模块电源脚附近必须放置两颗电容:一颗100nF高频去耦,一颗10~22uF钽电容或MLCC储能。如果模块和主控共用一个LDO,务必确认LDO的负载瞬态响应能力,实测在LDO输出端加上220uF电解电容能有效吸收广播瞬间的压降。如果你直接使用锂电池供电,模块电源输入端建议加磁珠和TVS管,一方面滤除射频噪声倒灌到电池回路,另一方面防止热插拔瞬间的尖峰电压打坏芯片。
2.3 开源电路怎么看:官方参考设计不等于可以直接抄
官方提供的开源电路建议按照三部分来看:射频天线区、电源去耦区、逻辑接口区。其中射频天线区,模块内部已经完成,外部不需要天线匹配网络。如果你用的是IPEX版本模块,需要额外买一根2.4G天线,走线要用50Ω微带线。PCB上模块位置附近尽量不要铺铜,尤其是不要在天线投影区正下方铺完整地平面,否则天线谐振频偏,距离缩水最明显。
我画第一版E104-BT02底板时犯过一个错:天线区下方正好走了一根I2C数据线,系统跑起来后发现广播距离只有十几米。后来把这根线绕开并挖掉了天线下方所有铜皮,距离立刻恢复到50米级别。这个经验非常值得分享:天线是“零容忍”器件,所有规则都要给它让路。
3. 软件驱动与代码资源落实践:5分钟跑通透传的完整路径
3.1 官方资源包结构及驱动文件说明
E104-BT02的官方驱动代码一般通过官网下载中心获取,或者找代理商要技术文档包。解压后通常包含如下目录:硬件设计参考(原理图、PCB封装库)、软件驱动(STM32/STC/Arduino等平台)、调试工具(PC串口助手、手机APP)、使用手册和AT指令集说明。打开驱动源码包后,核心文件一般是:ble_uart.c/h、stm32_hal_uart_driver、platform.h。不同平台封装的API大同小异,一般都会提供这几个关键函数:
- ble_uart_init:初始化串口和GPIO,配置模块工作模式;
- ble_uart_send_data:将数据通过串口发送给模块,模块再封装为BLE数据发出;
- ble_uart_set_callback:注册接收回调函数,收到BLE数据时触发解析;
- ble_uart_enter_sleep / ble_uart_wakeup:低功耗控制接口。
3.2 STM32 HAL库示例代码精简与上电时序
如果你用的是STM32,我建议直接从官方HAL工程拷贝UART初始化和GPIO初始化代码,但要注意几个关键点。一是串口波特率,模块默认一般115200或9600,看固件版本,建议使用115200以提高大包数据吞吐性能。二是模块上电后需要等待一小段时间(通常50~200ms),因为模块内部要加载固件和初始化协议栈,你如果立刻发AT指令,大概率会丢。下面是精简版的初始化代码框架:
void ble_module_init(void) { // 1. 引脚复位:拉低RST至少10ms,确保模块彻底复位 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET); HAL_Delay(200); // 等待模块固件启动 // 2. 配置串口,波特率115200,8N1 // 注意核对原理图,TXD接MCU的RX引脚,RXD接MCU的TX引脚 usart_handle.Instance = USART1; usart_handle.Init.BaudRate = 115200; usart_handle.Init.WordLength = UART_WORDLENGTH_8B; usart_handle.Init.StopBits = UART_STOPBITS_1; usart_handle.Init.Parity = UART_PARITY_NONE; usart_handle.Init.Mode = UART_MODE_TX_RX; HAL_UART_Init(&usart_handle); // 3. 使能串口接收中断 __HAL_UART_ENABLE_IT(&usart_handle, UART_IT_RXNE); HAL_UART_Receive_IT(&usart_handle, &rx_buf[0], 1); }初始化之后,主循环里只需要处理一组状态机:空闲、等待连接、已连接、休眠。最简单粗暴的方式是直接透传——不解析模块发来的任何状态字符,只做双向数据搬运。但实际产品里,你要解析模块上报的状态信息,比如连接状态变化、绑定状态、信号强度等,才能实现可靠的应用逻辑。
3.3 驱动代码的核心技巧:数据缓冲与分包粘连问题
串口透传最常见的坑是“数据粘连”。模块在接收对端BLE数据时,如果一个长数据包(比如超过20字节)被协议栈分片发送,模块串口侧会连续输出多段数据。如果你的接收逻辑是“接收固定长度”或“等串口空闲后再处理”,非常容易丢包或错包。
我的处理方式是:注册一个环形接收缓冲,串口每收到一个字节就写入环形队列,由应用层定时轮询处理。BLE每次写入最多20字节(默认MTU 23字节),这个规律可以充分利用:应用层每次按“一条完整BLE写请求”为边界,判断数据是否结束,再拼接成应用层协议帧。如果你需要收发更大数据包,就必须先发起MTU协商,具体内容我放在下一节讲。没有缓冲区的裸串口中断架构,在数据量大时几乎必崩。
4. 连接与调试全链路实录:从广播到绑定(Bond)的完整过程
4.1 广播类型与连接参数:决定对端能不能发现你
很多初学者第一次用手机APP扫描E104-BT02,怎么也搜不到模块。问题很可能出在广播配置上。E104-BT02默认的广播类型是可连接非扫描应答(Connectable Undirected)广播,周期在100ms到1s不等,通过AT指令可调。你要确认模块固件里广播使能是否打开,另外广播名称可能被设置成了一串默认字符,比如“BT02_XXXX”,这在APP扫描列表里很容易跟其他蓝牙设备混淆。
关于广播类型,我简单整理一个实用对照:
| 广播类型 | 是否可被扫描 | 是否可连接 | 典型用途 |
|---|---|---|---|
| Connectable Undirected | 是 | 是 | 默认透传、数据采集 |
| Connectable Directed | 是 | 是(仅指定设备) | 快速重连场景 |
| Non-connectable Undirected | 是 | 否 | 纯Beacon广播 |
| Scannable Undirected | 是 | 否 | 可携带额外数据但不连接 |
如果你只是把模块当透传用,保持默认类型即可。但如果做Beacon应用,要注意把广播类型改为不可连接,否则手机会一直试图连接它,白白增加功耗。
4.2 连接参数与MTU:为什么一次只能发20字节
BLE 4.2及之前版本,默认ATT MTU为23字节,减去协议头,实际有效载荷只有20字节。这就是很多工程师第一次用手机APP往模块发一个30字节字符串,发现只能收到前20字节的原因。
E104-BT02支持的MTU值取决于SoC固件配置,一般可通过AT指令或私有命令协商到247字节。我在项目里实测,将MTU从23提升到247后,单次写入的有效负载从20字节变成了244字节,传输速率提升非常明显。协商MTU有两种方式:一种是由手机端APP主动发起(比如用nRF Connect的MTU设置功能),另一种是由模块端在连接建立后主动发起协商。后者需要模块固件支持,你可以通过串口下发AT指令触发。如果协商成功,后续通信的单包长度上限会提高。但注意:即使MTU协商到了247,对端如果仍然按20字节发送,模块串口收到的依然是分包数据,这时必须靠应用层组包逻辑处理。所谓“MTU提升之后串口一次收到完整数据”的说法是片面的,跟对端发送策略关系更大。
4.3 绑定(Bond)的完整链路与调试助手的使用方法
在BLE协议栈中,“配对(Pairing)”和“绑定(Bonding)”是两个阶段。配对是临时产生加密密钥的过程,绑定则是把该密钥永久存储下来,以便下次快速重连。E104-BT02默认可能是“Just Works”配对方式——不需要输入PIN码,但绑定过程需要双方都支持。
调试时我一直推荐先用nRF Connect手机APP,因为它能看到非常完整的GATT服务表、MTU协商状态、连接参数、信号强度等底层信息。操作步骤大致如下:手机APP扫描到模块后,点击Connect,等连接建立;然后找到默认的UART服务,UUID通常是FFF0那一组(不同固件可能不同,以前导0xFFF0常见);点击服务列表里的Write特征值,尝试发送0x01等测试字节;再打开Notify开关(CCCD)配置,这样模块发给手机的数据才能实时被看到。完成以上步骤后,如果应用需要免密重连,就继续点击APP里的“Pair/Bond”,确认绑定完成。
| 调试阶段 | 手机端操作 | 串口端观察 | 结论 |
|---|---|---|---|
| 扫描 | 打开BLE扫描列表 | 模块串口无变化 | 能看到广播名即可 |
| 连接 | 点击设备并Connect | 串口收到“CONNECTED”状态码 | 连接链路成功 |
| 查服务 | 浏览GATT服务列表 | 无 | 默认UART服务可见 |
| 双向收发 | 写入0x01~0xFF | 串口收到对应字节 | 下行链路OK |
| 开启Notify | 打开CCCD通知 | 串口发送数据后手机能收到 | 上行链路OK |
| 绑定 | 点击Pair/Bond | 模块串口收到BONDED状态码 | 安全配对完成 |
我在一次实际调试中遇到一个诡异问题:手机连接模块后,模块串口已经打印出连接状态,但手机APP却无法发现UART服务。后来发现是手机端缓存了旧版本的GATT服务信息。解决办法很简单:清掉手机蓝牙缓存,或者使用另一个手机重新扫描。这类问题跟模块本身无关,但排查时浪费了我不少时间。
4.4 BLE调试助手与常见AT指令速查
很多文章会写“使用BLE调试助手即可”,但没说具体点哪里。我整理一份我常用到的E104-BT02参数配置指令,适用基于透传固件的模块,具体指令集以你手里的固件版本为准:
| 功能 | 指令(AT格式) | 说明 |
|---|---|---|
| 查询模块地址 | AT+ADDR? | 返回模块MAC地址 |
| 设置广播间隔 | AT+ADVINT=100 | 单位ms,范围20~10000 |
| 设置广播名称 | AT+NAME=MyDevice | 不超过20个字符 |
| 查询连接状态 | AT+CONN? | 返回连接/断开状态 |
| 恢复出厂设置 | AT+RESTORE | 所有参数回到默认值 |
| 进入休眠模式 | AT+SLEEP | 深度睡眠,需唤醒引脚拉低 |
需要特别提醒的是,AT指令后面要根据固件要求决定是否加回车换行。部分固件要求结尾带CRLF,部分只认CR,搞错了指令永远被判定为无效。我先查手册再发指令,从不盲试。另外,模块在透传模式下收到的串口数据会被直接转发到BLE链路,此时不能混入AT指令,否则会当成普通数据发出,导致链路数据混乱。一般需要通过模块的AT模式切换引脚进入命令模式。
5. 常见问题与排查思路实录:别让硬件坑拦住你
5.1 模块连不上手机:先看供电再看天线区
这是出现频率最高的问题。我的排查顺序是:先量模块VCC电压,纹波是否大;再确认RST引脚有没有被外部电路意外拉低;然后检查TXD/RXD是否接反;最后用示波器看串口是否有数据输出。如果这些都没问题,考虑天线区域是否有强干扰源或金属遮挡。
| 现象 | 可能原因 | 排查手段 |
|---|---|---|
| 扫描不到广播 | 广播未开启/天线区铜皮问题/电源异常 | 按AT指令查广播状态,检查供电纹波 |
| 能扫描但不能连接 | 连接参数错误/广播类型为不可连接 | 用nRF Connect查看广播类型标签 |
| 连接后频繁断开 | 电源跌落/距离过远/干扰 | 查看串口断连状态码,缩小距离交叉验证 |
| 手机收不到数据 | Notify未开启/MTU协商失败 | 检查CCCD并重新开启通知 |
| 串口有数据但APP无 | 通道UUID不匹配 | 用APP查看实际UUID并核对配置 |
5.2 绑定(Bond)失败的常见原因与绕过方案
绑定失败很大一部分跟加密密钥存储有关。模块断电重连时,若密钥存储在模块内部Flash,能够实现秒级重连且无需重新配对。但如果你用不同手机交叉连接模块,就可能出现密钥不匹配导致绑定失败。处理方式有两种:一是如果不需要强安全加密,干脆关闭绑定功能,直接使用“Just Works”配对,不存密钥;二是如果必须绑定,每次更换调试手机后都要先把模块恢复出厂设置,清掉旧密钥再重新绑定。
我遇到过一次边界情况:手机提示配对成功,但断电后重新上电,模块无法被手机自动重连。排查后发现原因是模块侧没有完成绑定密钥存储操作——绑定过程发起方必须在连接建立后主动发送Security Request,否则模块端不会保存密钥。之后我在自己的驱动代码里,在连接建立后自动调用一次安全请求接口,问题就解决了。
5.3 睡眠功耗降不下来:先做减法再做低功耗
低功耗是BLE项目绕不开的话题。E104-BT02广播时的平均电流大约在几十微安到几百微安(取决于广播间隔),连接建立后的通信电流会更高,深度休眠电流可以做到几微安级别。但很多人在实际产品中测到模块睡眠电流居高不下,原因往往是模块外设还在工作,比如串口没有关闭外部上拉电阻、SET引脚被拉高、模块并没有真正进入睡眠模式。
我的经验是:在进入睡眠前,先设置一个GPIO去控制模块的电源开关,把模块整板断电;或者在需要工作时才让主控通过AT指令唤醒模块。无谓地去抠模块自身那零点几微安的电流,不如在系统层级把电源域切干净。尤其是主控MCU的外设引脚如果有上拉电阻漏电到模块VCC,睡眠电流能直接飙到毫安级,这比模块本身功耗严重得多。
5.4 BLE与经典蓝牙(BR/EDR)的差异误区
最后再说一个概念性的问题。很多新手把BLE和经典蓝牙混为一谈,以为手机蓝牙关了BLE也就断了。实际上,BR/EDR和BLE共享2.4GHz频段,但在协议栈、功耗、连接方式上完全不同。E104-BT02只支持BLE,所以它无法跟旧版的蓝牙音箱、蓝牙耳机进行协议匹配。如果你在产品里需要同时兼容两者,就不能选这种纯BLE模块,而应该考虑双模蓝牙模块或支持双模的SoC。
在Linux环境下,蓝牙调试有个常见操作是打开bluetoothctl,然后默认使用BR/EDR的adapter去扫描BLE外设,有时候会发现扫描列表为空。正确的做法是确认adapter是否支持LE,并通过设置过滤或直接启用LE模式来扫描。类似的“扫描不到设备”的坑,追根溯源都是BLE和BR/EDR的适配器模式选择问题。
6. 从官方示例到量产固件:我的几点实操心得
我见过不少项目,Demo跑通了,却在量产阶段反复出问题。究其原因,往往是直接把官方示例代码当成产品代码。官方代码的目的是演示功能,它不处理异常恢复、不处理多帧缓冲、也不做看门狗喂狗策略,这在量产品里是不够的。我的建议是,你需要额外做三件事:一是给透传数据加上应用层帧协议,包括帧头、长度、CRC校验,因为BLE空中链路虽然可靠,但串口到模块之间的路径仍然可能受干扰;二是设计好断线重连机制,不能每次掉线都建议用户重新上电;三是写一个简单的日志系统,记录模块状态码和应用层收发统计,方便现场定位问题。
在线升级也是一个隐藏需求。很多E104-BT02模块固件支持通过串口OTA,但具体操作方法完全依赖原厂固件。如果你的产品需要支持批量更新模块固件,最好在设计初期就预留一键进入Bootloader的GPIO触发逻辑,并且在量产治具上放好固件升级工具。我吃过一次亏:产品已经定型,发现模块存在一个广播丢包的固件Bug,结果因为硬件上没有预留升级引脚,只能拆机返厂处理,代价非常大。
另外,关于开源电路,我的体会是:官方开源电路主要解决“能不能用”的问题,而“好不好用”要自己优化。比如官方参考设计里模块VCC旁路电容通常只有100nF,但我实测增加10uF电容后,BLE通信稳定性确实提升了一个档次。你不一定完全照搬参考设计,但所有的改动都必须经过传导杂散和辐射杂散验证,否则过了研发关也会在认证关翻车。
最后再分享一个小技巧:在你把E104-BT02贴上PCB之前,先拿一个现成的开发板或者转接板把官方示例代码烧进去,用串口抓一遍AT指令返回和透传数据格式。这样一来,你后面写MCU驱动时心里就有底,不会出现“板子贴完发现根本不知道模块是否工作正常”的情况。项目管理里这叫“前置验证”,在无线模块上尤其管用。先用最低成本验证,再投入做硬件,这个习惯能帮你省掉大量无谓的反复改板时间。