周六晚上十一点,一个做智能家居的朋友给我发消息:E104-BT02到货了,照着数据手册接线,上电后模块灯也亮了,可手机上的扫描软件死活找不到设备。我问他串口电平是多少伏,他说“测过了,5V,灯亮得很”。问题恰好就出在这。
这颗模块在BLE透传圈里算是不多见的好脾气选手:主控只要用串口把数据丢给它,它就把数据塞进BLE广播包、连接帧里发出去,反过来收到手机端数据也会从串口吐出来。你不需要去配置GATT服务、不需要处理蓝牙状态机、更不需要操心广播包要怎么组,甚至不用懂BLE协议,就能在几分钟内把两个设备用无线打通。“5分钟上手”这个说法听起来像宣传话术,但只要管线接对、驱动代码路径正确,四步确实能跑通:上电、调AT指令、手机连上、双向透传。
我之所以专门写这篇,是因为社区里围绕这颗模块的提问几乎都集中在同几个地方:开不了广播、连上就断、发长数据丢失、配对弹窗反复出现。这些问题九成不是模块坏了,而是供电、电平、AT模式门禁和MTU理解这几关上没跨过去。下面按我从硬件到代码、从广播到排错的实际经验,把这套东西完整过一遍。
1. 先搞清楚E104-BT02是什么角色:一颗为串口而生的BLE从机模块
1.1 它在整个链路里的位置
E104-BT02本质上是一颗BLE透传模块,内部封装了完整的BLE协议栈,对外只暴露UART(串口)作为数据和配置通道。它承担的角色是“蓝牙从机/外设”,也就是说默认情况下它不会主动去连别人,而是打开广播,等待手机或者PC端的Central设备来连接。
这就决定了典型应用场景往往长这样:
- MCU通过串口发送传感器数据,模块将数据经BLE Notify发给手机App
- 手机App通过BLE Write特征下发指令,模块从串口吐给MCU
- 设备只有一个串口资源,通过模块快速获得无线通信能力,不需要重做硬件平台
很多朋友会问,它和传统的蓝牙BT2.1/BR、蓝牙3.0/EDR到底有什么区别。这里简单说透:BR/EDR(传统蓝牙)设计目标是大数据流,比如蓝牙音箱的音频传输、蓝牙耳机的通话;BLE设计目标则是极短小数据包、超低功耗、秒级连接,比如传感器、遥控器、门锁、电子标签。E104-BT02是纯BLE设备,你不能把它当普通蓝牙模块拿去传音频或连蓝牙音箱,二者协议栈完全不同,工具链也不通用。市面上的“双模蓝牙”芯片是同时具备BR/EDR和BLE两套协议栈,E104-BT02这类BLE透传模块只保留后者的功能。
1.2 省掉了什么,又付出了什么代价
自己直接用nRF52832这类SoC做产品时,你需要自己处理的事情包括:GATT服务表的定义、特征值的读写权限设计、广播包的组包、连接参数的协商、配对绑定流程、低功耗状态切换、甚至PCB天线匹配。这些环节每一处都有大量细节,稍不留神就会踩坑。
透传模块把这些全部封装好了。厂商固件里已经替你实现了一套完整的从机服务,通常就是大家熟知的Nordic UART Service(NUS),提供两个特征值:一个用于主控接收手机数据,一个用于发送数据给手机。你作为用户只需要关心UART侧收发即可。
代价就是灵活性受限。你想改服务UUID?你想自定义特征值权限?你想在广播包里塞厂商自定义数据?模块级的AT指令多半支持有限,绕不过去就只能走SDK自研路线。所以我的判断是:E104-BT02这类模块最适合样板验证、快速原型、小批量产品;如果要做大规模量产且对协议有强定制需求,还是得切到芯片级开发。
2. 硬件最小系统的搭建:接线之前先看这三个细节
2.1 引脚分配与最小电路参考
E104-BT02的引脚不多,典型的最小系统只需要四根线:VCC、GND、TXD、RXD。在动手接之前,先把关键引脚的功能摸清楚:
| 引脚/接口 | 方向 | 作用 |
|---|---|---|
| VCC | 电源输入 | 通常支持2.0V~3.6V,典型值3.3V |
| GND | 电源地 | 必须与主控共地 |
| TXD | 模块输出 | 模块串口发送脚,接主控的RX |
| RXD | 模块输入 | 模块串口接收脚,接主控的TX |
| SET/EN | 输入 | 配置模式使能/复位控制,不同固件定义不同 |
| AUX/状态脚 | 输出 | 可用来判断模块连接状态、数据流状态 |
这里单独强调一个新手特别容易犯的错:TXD和RXD的交叉连接。模块TXD接主控RXD,模块RXD接主控TXD。很多人看着丝印直接同名相连,结果就是模块发数据主控收不到,主控发AT指令模块也没反应。
标题里说的“开源电路”,实际指的是厂商资料包中提供的最小系统参考原理图和社区仓库中整理出的底板开源工程。我在几个社区仓库里见过比较干净的做法:主控用STM32F103C8T6最小系统板,模块插在转接底板上,底板包含3.3V LDO、滤波电容、串口电平转换和状态LED,整体原理图开源。照抄这份电路来做验证板是完全可以的,但有两个地方我建议自己再确认一遍:RXD引脚的输入电压范围、以及模块复位引脚是否需要外部上拉。
2.2 供电和电平:九成“模块坏了”的真实原因
大多数BLE模块标称工作电压是2.0V至3.6V,典型值3.3V,E104-BT02也不例外。拿5V直接怼VCC,运气好一点模块内部有LDO还能撑住,运气差一点就直接冒烟或者芯片永久损坏。我那位朋友说“5V灯也能亮”就是这个道理——LED的工作电压宽,模块内部逻辑器件却不一定扛得住。
即使你从USB转串口工具取电,也建议先做两步确认:
- 用万用表实测模块VCC引脚上的电压,确保在规格书范围内
- 确认主控串口的TX电平不能超过模块RXD引脚的耐压范围
主控是5V TTL输出时,不要直接连模块RXD,至少加一个电阻分压或者用逻辑电平转换芯片。反过来,模块TXD输出一般是3.3V电平,接到5V主控RX上通常没问题,因为大多数MCU的RX在3.3V时已经能识别为高电平,但为了稳妥,最好确认主控的输入阈值低于3.3V。
电源滤波也别省。模块发射瞬间电流波动明显,在VCC和GND之间放一个10uF钽电容加一个0.1uF陶瓷电容,能让供电更稳定。我实测过,省掉这两个电容后,使用质量较差的USB供电时,模块偶尔会出现连接成功但数据传输中断的诡异现象,加电容后再没复现过。
2.3 天线的净空与摆放
E104-BT02通常是板载PCB天线或邮票孔天线,这类天线对周边环境非常敏感。最直接的经验是:模块边缘至少保持5mm以上的净空,下方不要大面积铺铜,上方不要覆盖金属外壳或LCD排线。
举一个我实际调过的案例:把模块固定在金属支架上时,手机在3米外就收不到广播;换到塑料外壳位置,同样功率,10米外信号依然稳定。信号强度差距在RSSI上可以体现10甚至15dBm。所以硬件设计时,天线区域单独留空,是投入产出比最高的一项工作。
3. 驱动代码怎么写:串口打通之后,透传才算真通
3.1 工程组织和驱动骨架
开源驱动代码里,最典型的是STM32标准库或HAL库工程,文件结构大致如下:
- ble_uart.c / ble_uart.h:模块初始化、数据发送
- at_cmd.c / at_cmd.h:AT指令封装与解析
- lowlevel_uart.c:底层串口中断与DMA配置
- app_main.c:业务逻辑
一个可用的驱动骨架核心逻辑并不复杂,见下面的简化代码:
// ble_uart.h void BLE_UART_Init(void); uint32_t BLE_UART_Send(uint8_t *buf, uint16_t len); void BLE_UART_SetRxCallback(void (*cb)(uint8_t *data, uint16_t len)); bool BLE_UART_IsConnected(void);// ble_uart.c 核心逻辑 void BLE_UART_Init(void) { UART_InitTypeDef uart; uart.baudrate = 115200; // 取决于模块固件默认波特率 uart.dataBits = UART_DATA_8BITS; uart.stopBits = UART_STOP_1BIT; uart.parity = UART_PARITY_NONE; UART_Init(&uart); GPIO_SetDir(MODULE_EN_PORT, MODULE_EN_PIN, GPIO_OUTPUT); GPIO_SetDir(MODULE_SET_PORT, MODULE_SET_PIN, GPIO_OUTPUT); GPIO_WriteBit(MODULE_SET_PORT, MODULE_SET_PIN, RESET); // 进入透传模式 GPIO_WriteBit(MODULE_EN_PORT, MODULE_EN_PIN, RESET); // 使能模块 BLE_UART_EnterPassthrough(); }有细心的朋友一定会发现,这套驱动实际上没做任何蓝牙协议相关的事情,它只是让MCU能和模块通过UART正常聊天。这恰恰是透传模块的价值所在:协议栈已经在模块内部跑起来了,MCU侧只是接线员。
3.2 AT指令集速查与门禁条件
很多人在AT指令这里卡住,不是指令拼错,而是根本没进入AT模式。E104-BT02这类模块通常有两种工作状态:
- AT配置模式:模块不广播/不连接,专门用来收发AT指令
- 透传模式:模块正常工作,UART收到什么就通过BLE发什么
怎么切换?不同固件定义不同,常见做法包括拉低SET/EN引脚后复位模块、上电时保持某引脚电平进入AT模式、以及发送特定退出指令。这些信息必须查你手里固件版本对应的数据手册,不同批次固件可能不兼容。
这里给一份常见的AT指令速查表,具体命令名以你模块的官方文档为准:
| 指令/查询 | 含义 |
|---|---|
| AT | 测试通信是否正常,正常返回OK |
| AT+RESET | 模块软复位 |
| AT+MAC? | 查询模块MAC地址 |
| AT+NAME=xxx | 修改广播名称 |
| AT+ADV=间隔,电平 | 配置广播参数 |
| AT+POWER=等级 | 设置发射功率 |
| AT+MTU=大小 | 查询/设置MTU |
| AT+NOTIFY=... | 控制通知模式 |
实际操作中要注意的是,AT指令通常以回车换行结尾。我曾见过一位读者的代码里发指令没带\r\n,模块一直没反应,他一度怀疑收到的是坏模块。加上换行后缀后立刻恢复正常。
3.3 一个标准的初始化流程
我建议的固件初始化顺序是:
- GPIO初始化,把SET/EN引脚设置为正确电平
- UART初始化,建议先用115200,连不上再试9600/57600
- 发送“AT”等待“OK”,确认通信通路
- 用AT指令配置名称、广播间隔、发射功率
- 发送保存指令并让模块重启,进入透传模式
- 主控UART收到的数据直接转发给业务逻辑
这里有个容易踩的坑:初始化顺序反了。如果先发AT再拉高SET/EN,模块可能已经处于透传状态,收到的AT指令会被当成普通数据直接通过BLE广播出去,表现为“模块毫无反应”。所以配置指令一定要在模块处于AT模式时发,不要跳步。
另外,主控程序里尽量别用阻塞式延时等待模块的AT响应。BLE模块上电后内部启动时间不定,从几十毫秒到几百毫秒都有可能。更稳的做法是发完AT后等待串口接收中断,用超时机制判断是否收到OK。
3.4 和OLED显示模块共存的驱动组织思路
社区里常有人搜“hal库驱动oled代码”最终找到的是别人整套工程里顺带包含BLE和OLED的整合源码。这说明在一个项目里同时驱动BLE模块和显示模块非常常见。STM32 HAL库工程里跑FreeRTOS时,我习惯的做法是:
- OLED走I2C,独立中断优先级,不占用UART资源
- BLE模块走USART1+IDLE中断/空闲DMA
- 状态显示任务每秒刷新一次OLED缓存,显示连接状态、RSSI、发送计数
OLED最合适的介入点是查状态量而不是查数据流。BLE透传数据是高频的,OLED显示是低频的,两者如果互相阻塞,系统会显得非常卡顿。用DMA+空闲中断接收BLE数据,将数据放入环形缓冲,业务任务再从缓冲中取数。这样OLED只管读环形缓冲里的计数器和状态变量,完全不影响透传实时性。
4. 广播、连接、GATT与MTU:四个你迟早会撞上的底层概念
虽然是透传模块,但以下几个概念直接决定你能不能把数据传稳、传快、传明白。
4.1 广播类型与广播参数
BLE设备要被人发现,必须向外发广播包。广播包的类型直接决定别人能不能连接你。常见的广播类型包括:
- 可连接且可扫描广播:最常见,手机能扫描到也能发起连接
- 不可连接广播:纯单向广播,别人收到包但无法连接
- 可扫描广播:允许别人扫描附加数据,但不一定允许连接
- 定向广播:针对特定主机,只有指定设备能连接
E104-BT02这类模块默认都会打开可连接广播。如果你用AT指令把广播类型改成只发数据不可连接,手机自然搜不到可连接设备。遇到“扫不到模块”的问题时,先确认广播类型而不是马上怀疑天线。
广播间隔同样要关注。间隔越短,发现越快,功耗越大;间隔越长,省电,但手机可能等一两秒才看到设备。工业应用常设在100ms到500ms之间,既能保证发现速度,也不会过于耗电。
4.2 连接过程到底怎么发生的
BLE连接不是“先配对再连”的流程。实际顺序是:
- 从机(E104-BT02)持续广播
- 主机(手机App)扫描到广播后,发起连接请求
- 连接建立成功,双方交换连接参数
- 如果应用层要求配对/加密,此时才启动配对流程
- 配对成功后,可选择是否绑定(bond)保存密钥
这个流程可以用“两个人在展会搭话”来类比:广播就像举着名片到处逛,扫描就是看谁的名片有趣,连接请求相当于走过去握手,配对才是真正交换联系方式,绑定则是把联系方式存进通讯录,下次见面直接叫出名字。手机调试App里绑定(bond)后,下次重连时不需要重新配对,这在很多物联网场景里能大幅改善用户体验。
4.3 GATT不是协议,是一张数据表
GATT(Generic Attribute Profile)本质上是一个属性表规范,用来规定数据如何组织和访问。BLE通信中,数据以“服务(Service)”和“特征值(Characteristic)”的形式存在。
一个典型的GATT服务里,每个特征值有:
- UUID:唯一标识符
- 属性:是只读、可写、支持通知(Notify)还是支持指示(Indicate)
- Value:实际数据缓冲区
E104-BT02透传模块默认跑的NUS服务,就是Nordic定义的一套经典GATT服务。主机端向Write特征值写入数据,对应模块UART输出;模块UART收到数据后,通过Notify特征值推给主机。你可以用手机上的BLE调试助手主动探测模块的服务和特征值,自己看看UUID和权限配置,这样对GATT的理解会更直观。
4.4 MTU:20字节限制与吞吐量的关键
MTU(Maximum Transmission Unit)在BLE里是个高频话题。默认情况下,BLE的ATT层MTU是23字节,扣除3字节的ATT头,单次有效负载最多20字节。也就是说,你不做MTU协商时,一次最多发20字节,超过就会自动分包或失败。
Android和iOS的BLE协议栈默认MTU不尽相同,很多调试App支持手动协商MTU。E104-BT02这类模块通常支持更大的MTU,常见可以协商到247字节,单次有效负载最多244字节。但要注意:MTU协商是双向的,模块支持多大、手机支持多大、系统是否自动协商,三者必须都成立才能生效。
实际操作中我的建议是:
- 单次不超过20字节的数据,不用管MTU,直接发即可
- 稍大数据建议在应用层分包成20字节一组,模块和手机都兼容
- 需要追求高吞吐时,先确认手机调试App能协商MTU,再在模块侧开启大包支持,做一次完整验证
很多人遇到“发小数据正常,发长数据就丢包”的问题,本质就是MTU没协调好,或模块在20字节边界分包时出错。
4.5 配对与绑定:为什么调试助手总是弹窗
配对(Pairing)用于加密链路,绑定(Bonding)则在配对基础上把密钥持久化保存。手机和E104-BT02连接后,如果模块固件配置了必须配对/加密,App就会弹窗要求配对。
如果每次连接都弹窗,大概率是:
- 模块侧没保存绑定信息
- 手机侧之前删除了绑定记录
- 模块的任务是在每次连接后清除了LTK等长期密钥
调试时如果不想每次都折腾配对,可以先把模块的配对策略设为“不要求配对”或“Just Works”模式。等方案定型后,再按安全需求决定是否开启强制配对。很多BLE调试助手绑定(bond)失败,不是密码不对,而是两端的配对策略不匹配。
5. 实测排错:从扫不到设备到中途掉线,完整排查链路
这部分我挑几个高频问题,给出完整的排查思路,而不是直接说答案。建议按链路一步一步走,避免跳步造成二次故障。下面用表格做一个汇总,再逐个展开说明。
| 现象 | 主要可能原因 | 优先级排查点 |
|---|---|---|
| 手机扫不到模块 | 供电不足、广播类型不可连接、天线净空不够 | 电压 → AT查询广播 → 天线位置 |
| 能连上但收不到数据 | 串口TX/RX接反、模块处于AT模式、波特率不匹配 | 串口收发测试 → 查看状态引脚 → 波特率检查 |
| 大数据传输中掉线 | MTU协商问题、供电波动、连接间隔过大 | MTU → 电源 → 连接参数 |
| 每次连接都弹配对框 | 未绑定、配对策略不匹配 | 模块配对策略 → 手机端删除重新扫描 |
| Linux下ble蓝牙命令异常 | 双模控制器BR/EDR干扰 | 关掉BR/EDR保留BLE |
5.1 现象一:手机完全扫不到模块
第一步,测硬件。模块供电是否稳定,电压是否在规格书允许范围内。模块的指示灯(如果有)是否正常点亮或闪烁,但不亮不代表没电,很多模块把指示灯复用为状态指示,有些固件默认关闭LED。
第二步,进AT模式查配置。发送AT指令查询广播状态、广播类型、广播名称。最常见的情况是广播类型被改成了不可连接或关闭广播。如果AT能通而手机扫不到,问题十有八九在广播参数上。
第三步,检查天线环境。把模块拿在手里,天线远离金属,再做手机扫描测试。如果手持就能扫到,固定位置扫不到,那就是摆放位置问题。
5.2 现象二:连上了但收发不到数据
这是最常见的“假连接成功”。手机显示已连接,但发指令模块没反应,模块发数据手机也收不到。按照这个顺序排查:
- 用串口工具直接连接模块TXD/RXD,发AT测试串口是否正常。如果UART侧都收不到模块数据,先把串口接线、电平、波特率查清楚
- 确认模块工作状态。如果模块停在AT模式,透传通道是没有数据流动的
- 确认主控串口与模块的TX/RX交叉正确,不要拿着逻辑分析仪只看一路信号
- 最后再排查手机App端特征值是否找对,Notify是否已经订阅
关于订阅这点多说一句:BLE的Notify不是自动推送的,主机必须先向CCCD(Client Characteristic Configuration Descriptor)写入0x0001,模块才允许发送Notify。很多调试App连接后不会自动订阅,你得在界面里手动点击打开通知。这个不是模块问题,但特别容易被忽略。
5.3 现象三:传输大文件或长数据时中途掉线
长数据掉线不是一个原因,而是几个因素叠加:
- MTU协商不成功,模块侧按20字节分包发送,手机端乱序或丢失,数据校验失败后App主动断开
- 连接间隔太长,主机未及时拉取从机数据,缓冲溢出
- 发射瞬间电流较大,供电电压跌落,模块逻辑复位
如果要传大文件,正确路径是:先确认MTU协商到最大,再适当调小连接间隔,最后用供电能力充足的电源给模块单独供电。多数情况下MTU协商问题可以列为第一排查项,用调试App手动协商MTU到247字节后,很多卡顿消失。
5.4 现象四:Linux环境下蓝牙调试时BR/EDR干扰
在PC上做嵌入式蓝牙调试很常见,Ubuntu等系统用bluetoothctl操作BLE设备时,如果电脑自带双模蓝牙控制器,BR/EDR和BLE共用同一个物理射频前端,有时候BR/EDR的扫描查询过程会影响BLE的连接稳定性。社区里经常有人提到“bluetoothctl关闭br保留ble”,这个经验很实用。
实际操作中,可以先用hciconfig查看当前控制器,再通过控制器的配置项只启用LE功能,避免BR/EDR自动扫描。这样能减少莫名其妙的连接失败:
hciconfig hci0 down hciconfig hci0 up # 仅保留LE的方式因BlueZ版本不同而异,常见做法是修改main.conf的对应配置项注意一点:不建议在系统级别直接完全禁用BR/EDR,因为鼠标、键盘等传统蓝牙外设可能还在使用它。只在你需要稳定调试BLE模块时临时切换即可。
6. 从“会用模块”到“自己搭方案”:三个立刻能上手的进阶方向
6.1 用E104-BT02给ESP32-S3设备做BLE配网
很多物联网设备没有屏幕也没有键盘,第一次联网时怎么配WiFi?BLE配网是一条成熟路径。设备上电后先跑一个临时BLE服务,手机App把WiFi SSID和密码通过BLE写特征发给设备,设备拿到后尝试连接路由器,成功后关闭或休眠BLE,转入常规运行。
ESP32-S3本身自带BLE控制器,直接用原生BLE配网当然可行,但要写整套GATT服务和配网逻辑。如果项目里已经有主控MCU且并不想依赖ESP32的原生协议栈,用E104-BT02这类透传模块做配网入口,主控侧只处理串口数据,开发速度会快得多。这种情况下,模块的运行状态通常由主控通过串口或GPIO控制,BLE配网功能独立于主业务运行。
6.2 用低成本国产MCU当Central主机连接BT02
E104-BT02是从机,但很多场景需要一个东西主动去连它。手机当然能当主机,但有些项目要求低成本、固定式网关,或者用8位/32位国产MCU实现采集,这时候就需要一颗能跑BLE Controller的芯片来当中央设备。
现在国内不少MCU厂商已经推出集成BLE Controller的系列型号,WCH、泰凌微等的方案在成本和功耗上都很有竞争力,社区里甚至有人把ESP32的BLE Controller单独抽取出来配合其他MCU使用。但必须提醒一点:做Central侧的软件工作量通常远大于做从机侧,你需要自己处理扫描、连接、服务发现、MTU协商、Notify订阅、连接参数更新。哪怕是买模块级的Central方案,AT指令的粒度也往往更细、更繁琐。
所以我的建议是:如果只是验证E104-BT02能不能和某颗MCU打通,先别急着拿它来接;先用手机调试App把数据链路调通,再用MCU侧验串口字节,最后才轮到Central芯片上场。分阶段验证能帮你快速定位到底是模块问题、手机问题还是代码问题。BLE数字钥匙、室内定位信标这类项目,基本都能在这个框架下找到验证路径。
6.3 STM32 HAL库工程里增加OLED状态页面的组织套路
接着3.4小节继续展开。实际项目中我用过一套比较清爽的组织方式,分享出来供参考:
- OLED用I2C接口,引脚只占SCL/SDA两个,软件I2C或硬件I2C都行
- 写一个oled_task,每500ms刷新一次,内容来自全局结构体
- BLE模块状态由一个全局状态变量维护,包含未初始化、广播中、已连接、数据传输中四个状态
- 中断回调里更新计数器和状态,OLED任务只读不写
举个例子,OLED上可以显示三行内容:
| 行 | 内容 | 数据来源 |
|---|---|---|
| 第1行 | 模块连接状态 | ble_status全局变量 |
| 第2行 | 当前RSSI | 模块通过AT查询/Notify回调更新 |
| 第3行 | 发送/接收计数 | UART收发累加器 |
这样做的核心价值是把“高频的数据传递”和“低频的人机交互”解耦,BLE串口中断永远不会被OLED刷屏阻塞。有人习惯把OLED驱动和BLE驱动写在同一个文件里,前期图省事,后面一旦调参就非常痛苦,每个功能点都要在一个大文件里翻找。拆分成独立模块,哪怕只是靠函数指针做回调,后续维护体验会好一个量级。
这几年调BLE模块,我最大的感受是:绝大多数问题都不是蓝牙协议多深奥,而是供电、电平、状态模式这些基本功没做好。E104-BT02这样的透传模块把协议栈挡在身后,你能接触到的错误面已经很窄了,剩下的就是耐心把串口链路和状态机理顺。如果你也卡在某个“扫不到”或“连不上”的环节,建议先拿起万用表测一下模块供电电压,再打开串口助手发一条AT,看看它到底回应了什么——很多时候,答案已经在那里了。