BLE蓝牙模块透传实战:从硬件设计到STM32驱动开发

发布时间:2026/9/4 11:26:27
BLE蓝牙模块透传实战:从硬件设计到STM32驱动开发 先把E104-BT02这块模块摆到桌面上说。很多做物联网、智能硬件、上位机通讯的朋友第一次接触BLE蓝牙模块时容易被一堆术语吓住GATT、MTU、广播类型、绑定……其实这类模块的本质就是一个“串口转蓝牙”的桥你用串口发什么对端蓝牙就能收到什么反过来也一样。E104-BT02是亿佰特家基于Nordic nRF52832方案做的一款BLE 5.0透传模块特点就是便宜、稳定、资料全而且官方和社区里能拿到开源电路和驱动代码非常适合快速验证原型或者直接进量产。这篇就围绕“5分钟上手”这个目标把硬件电路、AT指令配置、STM32/HAL库驱动、以及那些容易踩的坑一次讲清楚。我最早用这个模块是在一个环境监测项目里MCU采集温湿度数据要通过手机App实时查看。当时也在ESP32板载蓝牙和独立BLE模块之间犹豫过后来选E104-BT02的原因很简单它把射频天线、晶振、匹配电路、协议栈全都封装好了我只需要关心串口通信不用去折腾BLE协议栈和射频调试硬件设计风险直接降到最低。1. E104-BT02到底是个什么模块要快速上手得先搞清楚它的定位。E104-BT02本质上是一个成品BLE通信模组芯片方案是nRF52832支持BLE 5.0工作频段2.4GHz特色是“透传”。什么叫透传就是模块内部帮你把串口数据和蓝牙无线数据互相转换你不需要懂BLE协议栈把它当成一根“无线串口线”来用就行。模块对外提供两种使用形态一种是AT指令配置模式上电后通过串口发AT指令可以修改广播名称、串口波特率、广播间隔、连接间隔、MAC地址等参数另一种是数据透传模式配置完成后模块直接进入透传状态所有从串口收到的数据会自动打包通过BLE发出去反过来收到蓝牙数据也会从串口吐出来。这个设计非常符合MCU开发者的习惯因为串口是单片机最基础的接口谁都会用。顺便说一句蓝牙技术里常提到的BR/EDR和BLE的区别。BR/EDR是传统蓝牙注重高数据速率和持续连接比如蓝牙耳机、蓝牙音箱BLE是低功耗蓝牙主打低功耗、快连接、小数据量传输适合传感器、遥控器、智能家居设备。E104-BT02只能用来做BLE不能兼容传统蓝牙但这在实际物联网场景里完全够用甚至更合适因为手机、电脑都原生支持BLE不需要额外配对授权。模块电气参数方面供电范围典型值是3.3V但也能容忍2.0V到3.6V这意味着用两节干电池或者锂电池直接供电都没问题。串口电平默认是TTL 3.3V如果MCU是5V系统必须加电平转换不然后患无穷。工作电流在广播状态下大概十几毫安连接状态下几毫安深度睡眠模式下更低具体数值手册里都有表格做电池供电设计时一定要提前查好。2. 开源电路拆解硬件设计的关键细节E104-BT02模块本体已经把射频部分都处理好了模块是板载天线外面只需要接电源、地、串口收发引脚就能跑起来。但“能跑”和“稳定跑”是两码事尤其是量产项目硬件设计的细节直接决定无线性能。2.1 供电电路别小看这3.3VBLE射频发射瞬间电流峰值能到十几毫安甚至更高如果电源纹波大、动态响应慢会导致模块射频指标下降表现就是通信距离变短、连接不稳定、偶尔断开。所以别用那种老旧的LDO或者直接从MCU的GPIO口取电最好用专门的LDO或者DC-DC给模块单独供电。我给一个常用的参考电路输入5V或者锂电池电压经过一颗RT9013或者XC6206这类低 dropout 的LDO输出3.3V输出端放一个10uF钽电容并联一个100nF陶瓷电容分别负责低频储能和高频去耦。模块电源引脚旁边再就近放一个1uF和100nF越小越靠近模块越好这是很多工程师容易忽略的细节。如果项目里有电机、继电器、蜂鸣器这类感性负载一定要保证模块供电不跟这些负载共用同一路电源或者在电源入口串磁珠、加TVS管否则模块极易被干扰复位甚至损坏。2.2 串口连接电平匹配是第一优先级E104-BT02的串口是3.3V TTL电平如果你的MCU是3.3V系统直接交叉连接就行模块的TX接MCU的RX模块的RX接MCU的TXGND必须共地。这里有个血泪教训模块的RX引脚内部没有强上拉如果MCU的TX引脚在复位期间是高阻态模块可能收到乱码所以规范做法是模块的RX引脚外部加一个10K上拉到3.3V。如果是5V的MCU比如老款Arduino、51单片机不能直接连必须做电平转换。最简单的方案是两颗MOS管搭双向电平转换或者直接用一体的电平转换模块。千万别偷懒串电阻分压分压后的信号沿变差高速通信时容易误码。2.3 天线布局净空区和地平面模块是板载PCB天线天线区域周围必须保持净空。意思是PCB上天线投影范围内顶层和底层都不能铺铜、不能走线、不能放置金属器件和塑料外壳遮挡净空建议至少5mm以上。天线区域正下方最好完整铺地给射频信号提供良好回路。如果你把模块贴在金属外壳上或者天线正上方放一颗大电解电容通信距离减半很正常别怪模块怪自己布局。另外模块尽量放在PCB边缘天线朝外悬空这一点和所有2.4G模块的布局要求一致。如果实在空间受限也可以考虑用外置天线版本的模块通过IPEX座子引出天线放远一点。2.4 引脚分配除了串口还有哪些能用E104-BT02除了串口还引出了一些通用GPIO比如状态指示引脚、唤醒引脚、复位引脚。建议把状态指示引脚接一个LED模块连接上蓝牙后LED状态会变化这对调试太重要了。复位引脚通过10K上拉接3.3V再对地接100nF电容需要的时候MCU拉低做硬件复位。唤醒引脚在深度睡眠模式下用平时可以先悬空。整体开源电路的设计逻辑其实就是上一节这些点加上模块最小系统一颗LDO、若干电容、一串引脚排针。亿佰特官方资料包里有参考原理图和PCB封装直接拿来改改就行。我实际打板验证过照抄官方参考设计实测室内穿一堵墙通信稳定距离大概30米没问题。3. 5分钟快速上手从AT指令到双向透传这部分就是标题说的“5分钟上手”我尽量把流程压缩到最短但同时保证你理解每一步在干什么。提前准备的工具一块E104-BT02模块、一个USB转TTL模块、杜邦线若干、手机安装好任意一款BLE调试助手App。3.1 硬件连接和串口参数把模块和USB转TTL接好模块TX接USB转TTL的RX模块RX接USB转TTL的TXGND接GND模块的VCC接3.3V。打开电脑上的串口助手波特率选9600模块出厂默认数据位8停止位1无校验无流控。这个波特率不是固定的后面可以用AT指令改但第一次上手先用默认值。打开串口助手后给模块发送“AT\r\n”或者“AT\n”注意看模块手册说的结束符格式常见的是回车换行。如果模块回复“OKOK”说明通信正常。如果没反应先检查TX/RX是不是接反了再看看供电电压是不是3.3V大概率是这两个问题。3.2 修改广播名称和关键参数默认广播名称一般是“BLE_XXXX”或者模块型号为了好认可以改成自己的设备名。发送ATNAMEMyDevice模块会回复OK并保存。注意BLE广播名称有长度限制一般最长20字节左右超过部分可能显示不全或者广播包超长导致其他设备扫不到别取太长。广播间隔是一个重要的省电参数指令大概是ATADVINT100单位是毫秒这里配置的是100ms广播一次。广播间隔越小手机扫到模块的速度越快但功耗越高越大则反之。如果你的项目是低功耗传感器可以设到500ms以上如果追求快速连接100ms合适。实际项目中不需要经常改这个参数调好一次就行了。3.3 连接测试手机与模块双向通信手机打开BLE调试助手App扫描到改好名字的设备后点击连接。连接成功后App界面一般会显示这个设备的Service和Characteristic模块默认透传服务通常是一个自定义的UUID比如FFE0/FFE1这类。在App里找到可写的Characteristic向它发送任意字符串同时打开串口助手你会在串口里收到同样的内容说明蓝牙到串口的通路已经通了。反向测试在串口助手里发送一个字符串App对应的通知Characteristic界面应该能收到数据。有的App需要先“开启通知”或者“订阅”点一下对应按钮就行。两端都通说明这个模块的核心功能你已经完全掌握了整个过程熟练的话确实不到5分钟。3.4 数据组包长度和透传机制的坑透传模块有没有限制每次发送的最大长度答案是有的。E104-BT02在透传模式下一包数据最长一般是160字节左右不同固件版本有差异以手册为准。如果你从串口一次性发超过这个长度的数据模块会自己拆包分多次发送或者丢弃多余部分特别坑。所以MCU侧代码里要做组帧和拆包把大数据切成150字节左右的小包每包之间留时间间隔或者靠模块的空闲时间判断帧结束。我实际测试过串口波特率115200、BLE连接间隔7.5ms时单向持续传输大概能跑到6~8KB/s作为传感器数据上报完全够用。如果你要传大文件或者音频流BLE透传模块就不是最优方案了得换方案。4. 驱动代码设计与实现不只是“打开串口”很多人拿到模块在STM32上写驱动就是初始化UART然后收发数据。但这个模块真正用起来还得处理几个问题AT指令超时等待、透传数据拼接、状态变化检测。这一节我把驱动代码的设计思路和关键代码贴出来基于HAL库可以移植到F1/F4/G0等多种型号。4.1 串口DMA收发框架如果用串口中断一个字节一个字节收发在115200波特率下容易丢数据而且CPU占用率高。推荐的做法是串口空闲中断加DMA接收DMA把串口收到的数据自动存到缓冲区检测到串口空闲一帧数据结束后触发中断这时候一次性处理缓冲区里的完整数据。初始化部分开启UART的DMA接收并配置空闲中断HAL_UART_Receive_DMA(huart1, uart_rx_buf, UART_RX_BUF_SIZE); __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE);然后在中断回调里判断帧结尾提取有效数据再把DMA缓冲区重新初始化。核心代码类似void UART_IDLE_Callback(UART_HandleTypeDef *huart) { if (huart huart1) { uint16_t len UART_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart1_rx); if (len 0) { ble_uart_rx_handler(uart_rx_buf, len); } HAL_UART_DMAStop(huart1); HAL_UART_Receive_DMA(huart1, uart_rx_buf, UART_RX_BUF_SIZE); } }这个框架是通用模板不管最终对接什么模块都能用区别只在收到的数据怎么处理。4.2 数据透传通道的封装在应用层我习惯把BLE模块的收发封装成两个接口ble_send_data(uint8_t *data, uint16_t len)和ble_data_ind(uint8_t *data, uint16_t len)。前者负责把数据拆包发送后者是接收回调由上层逻辑决定数据往哪走。拆包发送的要点是检查长度超过模块单包上限就循环拆包void ble_send_data(uint8_t *data, uint16_t len) { uint16_t offset 0; while (len - offset BLE_MAX_PACKET_SIZE) { HAL_UART_Transmit(huart1, data offset, BLE_MAX_PACKET_SIZE, 100); offset BLE_MAX_PACKET_SIZE; HAL_Delay(5); } HAL_UART_Transmit(huart1, data offset, len - offset, 100); }每包之间加5ms延时是为了给模块和BLE协议栈留出处理时间实测下来丢了包的情况少很多。接收回调里我一般会把数据放递到一个环形缓冲区然后设置一个事件标志让主循环去处理不占用中断上下文void ble_data_ind(uint8_t *data, uint16_t len) { ring_buf_write(ble_rx_ring, data, len); ble_rx_event 1; }4.3 AT指令交互的状态机写AT指令查询函数的时候如果简单发一条“ATNAMExxx”然后死等串口回复超时机制很难处理。我建议用一个简单的状态机发送指令后记录时间戳定时扫描是否收到“OK”或者“ERROR”。用HAL库的HAL_GetTick()做超时判断超时时间一般500ms到1s足够。参考实现uint8_t ble_at_cmd(char *cmd, char *reply_buf, uint16_t timeout_ms) { uint16_t len strlen(cmd); HAL_UART_Transmit(huart1, (uint8_t*)cmd, len, 100); uint32_t start HAL_GetTick(); while (HAL_GetTick() - start timeout_ms) { if (ble_rx_event) { // 从环形缓冲区取一行判断 return parse_at_reply(reply_buf); } } return 0; // 超时 }注意AT指令结束符一定按手册来有的是\r\n有的是\n格式不对模块不识别这是新手最常犯的错。4.4 扩展HAL库驱动OLED和SPI Flash项目做到后面很多人喜欢加OLED显示屏实时显示蓝牙连接状态、信号强度或者加一颗SPI Flash存历史数据等手机连上后再批量同步。这两个场景分别用到了屏幕上提到的“hal库驱动oled代码”和“sst25vf080b驱动代码”。OLED驱动我用的是常见的0.96寸SSD1306I2C接口HAL库的I2C驱动加上SSD1306的显存映射刷新率控制在10Hz以上没问题代码网上很多核心就是初始化序列加画点函数。SPI Flash我手头是SST25VF080B8Mbit容量SPI接口HAL库驱动SPI后核心操作是读ID、擦除扇区、页编程。页编程一次最多写256字节读写地址要按扇区对齐。加一个简单的FatFS文件系统数据管理就方便很多。5. 深入BLE协议栈GATT、MTU、绑定与连接过程如果你只是把E104-BT02当透传模块用前面几节的内容已经足够了。但项目里一旦出现“连不上”“连上了收不到数据”“传输速率上不去”这类问题你就需要懂一点BLE协议栈的基本概念否则排查起来两眼一抹黑。5.1 GATT结构找对服务和特征BLE的数据交互不是像串口那样发个字节就完事而是基于GATT通用属性协议的层次结构。一个设备有若干个Service每个Service下面有若干个Characteristic每个Characteristic又有读、写、通知等属性。透传模块的Service通常是自定义UUID比如FFE0其中用于数据收发的Characteristic常见是FFE1可写可通知。手机App就是通过“发现服务—发现特征—订阅通知—写数据”这个过程和模块交互的。如果你在App里连上模块却看不到数据十有八九是没找到正确的Service或者没开通知。5.2 连接过程和MTU协商BLE的连接过程大致是外设广播主机扫描到广播后发起连接请求双方进入连接态并协商连接参数连接间隔、从机延迟、超时时间接着做服务发现然后是MTU协商最后才能高效传数据。MTU全称Max Transmission Unit决定了一包数据最多能承载多少字节的LL层载荷。BLE 4.0/4.2默认MTU是23字节减去3字节头部实际应用层数据只有20字节。E104-BT02支持协商更大的MTU比如247字节这样一包能传更多数据吞吐率大幅提升。手机App一般自动协商但有的App没有开启MTU协商设置导致一次只能传20字节传输慢是正常的。要提升传输速率可以从三方面入手调小连接间隔比如7.5ms、开启DLE数据长度扩展、协商更大MTU。E104-BT02的AT指令里一般有设置MTU或者连接间隔的选项具体看手册。5.3 绑定与配对不只是“连上就行”热搜词里有“ble调试助手绑定(bond)”这是很多人的痛点。BLE的绑定Bonding和配对Pairing不同配对是一次性的安全认证过程绑定是在配对基础上把密钥存下来下次连接免密重建安全通道。透传模块默认可能是“Just Works”配对不需要输入PIN码安全性较低但对大多数数据采集场景够用。如果你遇到“手机上配对成功了但模块重启后又得重新配对”说明模块没有保存绑定信息或者被配置成不保存绑定。解决方法一般是发AT指令开启绑定保存功能不同模块指令不一样查手册拿到对应指令比如ATBOND1之类。另外一个相关坑绑定之后手机端把模块删掉重新扫描有时候会扫不到或者连不上。因为模块还在尝试用旧的绑定信息加密连接。解决办法是清空模块侧绑定信息一般是通过AT指令恢复出厂设置或者按住模块上的复位键加某个引脚电平触发清除。6. 常见问题与排查技巧实录我积攒了不少这个模块的排查经验整理成速查表遇到问题对着查能省很多时间。现象可能原因排查及解决方法串口发AT无回复TX/RX接反交换串口两根线串口结束符不对换\r\n或\n试验模块未上电测量VCC-GND电压手机扫描不到模块模块在休眠唤醒模块广播名称超长改短名称广播间隔过长改小广播间隔等待模块设置成不可发现恢复出厂设置连接后串口收不到蓝牙数据未开启通知App里订阅Characteristic没找到正确Service查看模块手册的UUID数据被组包截断拆包发送调整帧间隔传输速率很慢MTU没协商大开大MTU按模块指令配置连接间隔太大调小连接间隔距离短、连不上天线净空不够检查PCB布局附近有2.4G干扰换信道或错开使用时间模块乱码波特率不匹配查看模块配置的波特率电平不匹配加电平转换供电纹波大加强滤波电容6.1 绑定失败的快速处置手机和模块绑定失败经常出现在新手机系统、旧模块固件之间。我建议先升级模块固件到最新版本然后再试。很多所谓“支持有问题”最后都是固件版本太老升级后一切正常。如果固件没法升级试试手机是否装了多个BLE调试工具有些App会占用后台连接导致其他App连不上关掉重试就好。6.2 Linux环境下只保留BLE关闭BR的坑如果是做嵌入式Linux开发经常会用到bluetoothctl命令行工具来调试BLE。装上BlueZ后默认br0接口可能和ble0接口共存导致扫描列表杂、连接目标混乱。建议关闭传统蓝牙BR/EDR只保留BLE。方法通常是修改/lib/systemd/system/bluetooth.service把ExecStart那行加上--nopluginhostname参数或者用命令关闭PSCAN和ISCAN。具体做法因系统版本而不同但方向是一致的让协议栈不要开启BR/EDR的inquiry scan和page scan。6.3 从快速上手到稳定量产模块在原型跑通后要进量产之前有几个点必须复查模块天线区域的最终装配结构是否遮挡、外壳是否是金属、电源在极端电压下是否纹波超标、长时间运行后模块温度是否正常。另外每一批模块的出厂固件版本可能不一样拧螺丝前先在PCBA测试工装里跑一轮自动测试脚本检查广播名称、串口透传、AT指令回复是否都正常。这些检查看起来繁琐但能避免后来在客户端出现大面积售后问题。我真正在这类蓝牙模块上吃过亏的一次是量产时忽略了一个IO电平冲突导致模块在某个批次主板上休眠后唤醒不了。排查了很久才发现是MCU那边一个复用引脚在初始化时为低电平把模块的唤醒引脚一直拉低了。后来在硬件设计规范里加了一条模块所有控制引脚默认高阻或者上拉MCU侧初始化完成后再主动控制状态。7. 进阶玩法一主多从、广播应用和低功耗设计E104-BT02虽然定位是透传模块但在固件能力范围内还能玩出不少花样。如果你不满足于简单透传可以深入研究这几个方向。7.1 一主多从组网模块如果支持多连接可以做一个简单的一主多从采集网络一个主机手机或者MCU模块连接多个从机设备每个从机采集不同的传感器数据。这种场景下要注意每个从机用不同广播名称区分主机端要根据MAC地址或者广播名称识别设备。串口透传模式下要自己加设备地址帧头比如每个设备一个字节地址主机根据地址分发数据。7.2 广播包携带自定义数据BLE的广播包不只能广播设备名称还可以携带厂商自定义数据。如果你希望设备在不连接的情况下就能周期性的广播一些状态信息比如温湿度值可以走这个方案。E104-BT02有些固件版本支持通过AT指令设置广播数据这样手机不用连接就能扫描到设备数据省去了连接流程功耗也更低。缺点是广播数据容量很小一般只能放十几个字节有效数据且单行道只能设备发到手机。7.3 低功耗设计的关键参数做电池供电产品时低功耗设计比功能更重要。核心参数是广播间隔、连接间隔和从机延迟。广播间隔越大越省电但扫描发现变慢连接间隔越大空闲功耗越低但数据延迟变大从机延迟允许模块在多个连接事件内不监听主机数据进一步省电。这些参数要结合业务场景反复测试没有一劳永逸的配置。另外模块不工作时尽量进入休眠用MCU GPIO唤醒需要时再发一条AT指令切回透传模式实测下来休眠功耗能比连续工作降低好几个数量级。7.4 与ESP32等平台的联合调试最后提一个常见的联合调试场景很多做原型验证的工程师会把E104-BT02挂到ESP32开发板上利用ESP32的Wi-Fi/BLE双模能力实现本地串口清障、云端上报两套路径。这时候驱动代码可以沿用前面的HAL库思路只是把UART对接从STM32换成ESP32的UART驱动业务逻辑完全复用。我在实际做这个组合时踩过一个小坑ESP32的GPIO默认上拉状态和模块的某些引脚冲突导致模块启动异常后来在代码里初始化UART引脚前先把引脚模式强制设好才解决。这个细节在文档里没人提但一旦遇到会卡很久。8. 开源资料与扩展建议官方资料包里一般包含的东西有模块硬件手册、AT指令集手册、原理图PDF、封装库、演示用的单片机例程、手机App源码或者调试工具说明。拿到资料后我建议优先看“AT指令集”和“硬件手册”这两份关键文档的优先级最高。原理图参考价值在于电源和天线布局不要直接照抄到自己的高密度板子上。如果你打算把这个模块的方案做成一个通用的无线透传平台后续还可以扩展的方向很多多个模块互传组网、和蓝牙网关对接做资产管理、配合手机App做远程参数配置、加入OTA固件升级等。OTA这个方向尤其值得研究E104-BT02基于nRF52832理论上是支持OTA的量产产品在不拆机的情况下升级固件对售后维护帮助很大。我个人在做过几个BLE项目之后最大的体会是这类透传模块真正花时间的部分往往不是模块本身而是外围业务逻辑包括数据分包、协议设计、低功耗策略、平台对接。模块只是把一个大的复杂系统变成了一个标准串口设备剩下的工程细节照样要靠自己填。所以在最初选型时与其纠结哪块模块的信号更好不如考虑它的资料完整度、社区活跃度、以及供应商的长期供货稳定性。E104-BT02这个模块这三个维度都算合格所以我后来在不少项目里都愿意继续复用这套方案。