tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — BLE章节 + 配套代码12" author: "尚硅谷研究院" date: 2026-07-16
用生活理解:BLE 就像一台"静音待机"的对讲机——平时完全关闭喇叭省电(休眠),每隔几秒悄悄喊一声"我在这呢"(广播)。当有人听到广播后说"跟我连个线吧"(连接请求),通信就建立起来了。连上之后也不是一直说,而是每隔 30ms 说一句(连接间隔),不说话时极低功耗。
| 特性 | 经典蓝牙 (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 频段,但调制方式和信道划分不同,互不兼容。
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 角色 | 行为 | 典型设备 |
|---|---|---|
| Broadcaster | 只发广播(不能连) | 信标(Beacon) |
| Observer | 只扫描广播(不连) | 扫描仪 |
| Peripheral | 广播 + 接受连接 | 传感器节点 |
| Central | 扫描 + 发起连接 | 手机、网关 |
典型连接流程:
Peripheral 广播 → Central 扫描到 → Central 发起连接请求
→ 连接建立 → 双方按连接间隔周期性交互数据 → 一方断开
BLE 的数据按以下层级组织:
Service (服务) 如 "心率服务" UUID: 0x180D
├── Characteristic 如 "心率测量" UUID: 0x2A37
│ ├── Properties read / write / notify / indicate
│ ├── Value 实际数据(心率值)
│ └── Descriptor 配置描述(如 CCCD 使能 notify)
└── Characteristic
├── ...
UUID(唯一标识符):
GATT 角色:
项目路径: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
#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
#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 透传处理):
#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 |
仅输出连接/断开 |
AT+BLEADVSTART+++,否则会被误判为退出命令