--- tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — BLE章节 + 配套代码12" author: "尚硅谷研究院" date: 2026-07-16 created: 2026-07-15 --- # BLE低功耗蓝牙 > **用生活理解**:BLE 就像一台"静音待机"的对讲机——平时完全关闭喇叭省电(休眠),每隔几秒悄悄喊一声"我在这呢"(广播)。当有人听到广播后说"跟我连个线吧"(连接请求),通信就建立起来了。连上之后也不是一直说,而是每隔 30ms 说一句(连接间隔),不说话时极低功耗。 --- ## BLE vs 经典蓝牙 | 特性 | 经典蓝牙 (BR/EDR) | BLE (Bluetooth Low Energy) | | -------- | --------------------- | ------------------------------------------- | | 功耗 | ~1W(持续发送) | **~0.01W**(广播时 5~10mA,休眠时 μA 级) | | 峰值速率 | 1~3 Mbps | 125Kbps~2Mbps | | 连接延迟 | ~100ms | **~3ms** | | 数据包 | 长包(可达 339 字节) | 短包(最大 251 字节 ATT MTU) | | 信道 | 79 个 1MHz 信道 | **40 个 2MHz 信道**(3 个广播 + 37 个数据) | | 语音 | 支持(SCO 链路) | 不支持(需通过协议栈上层实现) | | 主要应用 | 音频流、文件传输 | **IoT、传感器、可穿戴设备** | > BLE 和经典蓝牙工作在相同 2.4GHz ISM 频段,但调制方式和信道划分不同,互不兼容。 --- ## BLE 协议栈架构 ``` Application(应用层) ↓ GAP (Generic Access Profile) — 广播、扫描、连接、角色 GATT (Generic Attribute Profile) — 数据读写、服务/特征值结构 ↓ ATT (Attribute Protocol) — 属性读/写/通知/指示 L2CAP (Logical Link Control and Adaptation Protocol) — 数据分包/重组 ↓ HCI (Host Controller Interface) — 主机与控制器通信接口 ↓ Link Layer (链路层) — 广播/数据信道管理、加密 Physical Layer (物理层) — 2.4GHz GFSK 调制 ``` ### GAP 角色模型 | GAP 角色 | 行为 | 典型设备 | | --------------- | ------------------ | -------------- | | **Broadcaster** | 只发广播(不能连) | 信标(Beacon) | | **Observer** | 只扫描广播(不连) | 扫描仪 | | **Peripheral** | 广播 + 接受连接 | 传感器节点 | | **Central** | 扫描 + 发起连接 | 手机、网关 | **典型连接流程**: ``` Peripheral 广播 → Central 扫描到 → Central 发起连接请求 → 连接建立 → 双方按连接间隔周期性交互数据 → 一方断开 ``` ### GATT 数据架构 BLE 的数据按以下层级组织: ``` Service (服务) 如 "心率服务" UUID: 0x180D ├── Characteristic 如 "心率测量" UUID: 0x2A37 │ ├── Properties read / write / notify / indicate │ ├── Value 实际数据(心率值) │ └── Descriptor 配置描述(如 CCCD 使能 notify) └── Characteristic ├── ... ``` **UUID(唯一标识符)**: - 16 位标准 UUID:由 Bluetooth SIG 定义(0x180D = 心率服务) - 128 位自定义 UUID:用户自定义服务/特征 **GATT 角色**: - **GATT Server**:提供数据(如传感器) - **GATT Client**:读写数据(如手机 App) --- ## 实验:STM32 + BLE 模块通信 **项目路径**:`stm32/12_ble_server_hal` ESP32-C3 同时支持 WiFi 和 BLE。BLE 实验使用 ESP32-C3 的 BLE 功能作为 GATT Server(从机),通过串口 AT 指令控制。`ble.c` 中包含了完整的 BLE 初始化流程和 SPP 透传模式切换。 ### 软件设计 **文件:`stm32/12_ble_server_hal/Core/Src/main.c`** ```c #include "main.h" #include "usart.h" #include "gpio.h" #include "ble.h" uint8_t rxBuff[1024]; uint16_t rxLen; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("尚硅谷BLE实验...\n"); BLE_Init(); while (1) { BLE_ReadData(rxBuff, &rxLen); if (rxLen > 0) { printf("收到数据:数据长度 = %d, 内容 = %.*s\n", rxLen, rxLen, rxBuff); BLE_SendData(rxBuff, rxLen); rxLen = 0; } } } ``` **文件:`stm32/12_ble_server_hal/App/BLE/ble.h`** ```c #ifndef __BLE_H #define __BLE_H #include "esp32.h" void BLE_Init(void); void BLE_SendData(uint8_t txBuff[], uint16_t txLen); void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen); #endif ``` **文件:`stm32/12_ble_server_hal/App/BLE/ble.c`**(包含完整的 BLE AT 指令序列和 SPP 透传处理): ```c #include "ble.h" void BLE_Init(void) { ESP32_Init(); // 1. 设置蓝牙角色为 Peripheral(服务器) printf("设置蓝牙角色为服务器...\n"); uint8_t *cmd = "AT+BLEINIT=2\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 2. 创建 GATT 服务 printf("创建GATT服务...\n"); cmd = "AT+BLEGATTSSRVCRE\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 3. 启动 GATT 服务 printf("启动GATT服务...\n"); cmd = "AT+BLEGATTSSRVSTART\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 4. 获取本机 MAC 地址 printf("获取本机MAC地址...\n"); cmd = "AT+BLEADDR?\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 5. 配置广播参数 // 参数含义:最小间隔,最大间隔,广播类型,过滤策略,信道,广播策略 // 最小/最大间隔单位 0.625ms,50 = 31.25ms printf("配置广播参数...\n"); cmd = "AT+BLEADVPARAM=50,50,0,0,7,0\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 6. 设置广播数据 // 参数:设备名,自定义UUID,厂商数据,长度类型 printf("设置广播数据...\n"); cmd = "AT+BLEADVDATAEX=\"atguigu-ble\",\"A123\",\"0102030405\",1\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 7. 开始广播 printf("开始广播...\n"); cmd = "AT+BLEADVSTART\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 8. 配置 BLE SPP 透传参数 // 参数含义:SPP使能,最大连接数,MTU大小,是否使能通知,分段大小 printf("配置 BLE SPP 参数...\n"); cmd = "AT+BLESPPCFG=1,1,7,1,5\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); // 9. 设置系统消息指示 // 4 = 只输出连接/断开事件通知 printf("设置系统提示信息...\n"); cmd = "AT+SYSMSG=4\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); } void BLE_SendData(uint8_t txBuff[], uint16_t txLen) { HAL_UART_Transmit(&huart2, txBuff, txLen, 1000); } static uint8_t BLE_IsConnChanged(uint8_t msg[]); void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen) { HAL_UARTEx_ReceiveToIdle(&huart2, rxBuff, 1024, rxLen, 1000); if (*rxLen == 0) return; if ( BLE_IsConnChanged(rxBuff) ) *rxLen = 0; } static uint8_t BLE_IsConnChanged(uint8_t msg[]) { // 收到 +BLECONN: 连接建立 → 进入 SPP 透传 if (strstr((char *)msg, "+BLECONN:") != NULL) { printf("有BLE客户端连接,准备进入SPP模式...\n"); uint8_t * cmd = "AT+BLESPP\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); return 1; } // 收到 +BLEDISCONN: 断开连接 → 退出 SPP 透传 else if (strstr((char *)msg, "+BLEDISCONN:") != NULL) { printf("BLE客户端断开连接,准备退出SPP模式...\n"); HAL_UART_Transmit(&huart2, "+++", 3, 1000); HAL_Delay(2000); return 1; } // 过滤 WiFi 网络事件(ESP32-C3 为 WiFi+BLE 双模芯片) else if(strstr((char *)msg, "WIFI CONNECTED") != NULL || strstr((char *)msg, "WIFI GOT IP") != NULL || strstr((char *)msg, "+DIST_STA_IP:") != NULL) { return 1; } return 0; } ``` --- ## 核心速查表 | BLE 操作 | AT 指令 | 说明 | | ------------------------ | ---------------------------------------- | --------------- | | GATT Server 初始化(从机) | `AT+BLEINIT=2` | 2=从机模式 | | GATT Client 初始化(主机) | `AT+BLEINIT=1` | 1=主机模式 | | 创建 GATT 服务 | `AT+BLEGATTSSRVCRE` | 创建标准服务 | | 启动 GATT 服务 | `AT+BLEGATTSSRVSTART` | 服务上线 | | 获取 MAC 地址 | `AT+BLEADDR?` | 查询本机地址 | | 设置名称 | `AT+BLENAME="name"` | 最长 29 字节 | | 设置广播间隔 | `AT+BLEADVPARAM=min,max,...` | 单位 0.625ms | | 设置广播数据 | `AT+BLEADVDATAEX="name","uuid","data",1` | 16 进制字符串 | | 开始广播 | `AT+BLEADVSTART` | Peripheral 模式 | | 停止广播 | `AT+BLEADVSTOP` | — | | 发送数据(非透传) | `AT+BLESEND=conn,len` → 数据 | — | | SPP 透传配置 | `AT+BLESPPCFG=1,1,7,1,5` | 使能透传 | | 进入 SPP 透传 | `AT+BLESPP` | 连接之后调用 | | 退出透传 | `+++`(无换行,20ms 间隔) | — | | 查询连接状态 | `AT+BLECONN?` | 返回当前连接 | | 断开连接 | `AT+BLEDISCONN=n` | n=连接 ID | | 扫描设备 | `AT+BLESCAN=0` | 0=被动扫描 | | 连接事件通知 | `AT+SYSMSG=4` | 仅输出连接/断开 | ## 常见问题与避坑 1. **手机扫描不到设备** → 检查广播是否启动、名称是否设置、广播功率是否太低 2. **连接后数据发不出去** → 确保 GATT Server 已启动、SPP 使能配置正确、MTU 大小 3. **功耗高** → 广播间隔增大(100ms+)、连接间隔增大(30ms+)、不用时停止广播 4. **断开连接后需要显式重连** → BLE 不会自动重连,Peripheral 需重新调用 `AT+BLEADVSTART` 5. **SPP 透传意外退出** → 数据中不能出现连续 `+++`,否则会被误判为退出命令 6. **WiFi+BLE 同芯片干扰** → ESP32-C3 是单射频双模,BLE 通信时 WiFi 性能会下降