ESP蓝牙(二)广播
一、蓝牙广播的介绍
1.广播
广播是两个设备之间建立联系的一个基础,广播设备周期性地发出广播,如果扫描设备接收到了广播且向广播设备发出了扫描请求,广播设备就会发出扫描响应(提供更多的信息),如果扫描者发起连接请求,二者就会建立BLE连接。
就好比你一直给别人发餐厅传单(广播),但是一般没人接你的传单,有人接传单的话(扫描请求),你就会愿意和他介绍饭菜信息(扫描响应),最后他心动了,请你带他去你们餐厅吃饭(链接请求)。呵呵呵我真是举例子的天才!
流程:设备广播时通过GAP层配置广播的参数和格式,L2CAP封装广播数据(转成LL层能识别的数据格式),HCI层主机给控制器下发广播命令,LL层生成一些广播帧,PHY层(即硬件层)将数字信号转为射频信号并发射出去。
了解一下:
蓝牙总共有40个信道,其中有三个广播信道(37,38,39)和37个数据信道,频道范围从2404MHz到2484MHz。

2.广播包
广播设备一直广播的内容其实就是广播包,也就是你发的传单。
广播包的格式如下:
访问地址(Access Address):
固定为 0x8E89BED6(4 字节),用于标识这是一个 BLE 广播包,让扫描设备能识别。
报头(Header 1):控制广播的 “类型与身份”。
注:这报头对应后面代码esp_ble_adv_params_t 的配置
- PDU Type(bit0~bit3):广播类型,例如:
0000=ADV_IND(可连接通用广播,对应代码中ADV_TYPE_IND)0010=ADV_NONCONN_IND(不可连接广播)
- TxAdd(bit6):发送地址类型(0 = 公有地址(厂商规定),1 = 随机地址(每次开机后可能会变))。
- RxAdd(bit7):接收地址类型(广播时为 0,因为是向所有设备发送)。
长度字段(Header 2):控制包的大小
这 1 字节定义了 PDU 的总长度:
length(bit0~bit5):PDU 长度 = 自身地址(6 字节) + 有效载荷(0-31 字节),所以范围是 6-37 字节。(不包含CRC校验)
PDU(协议数据单元):广播的核心内容
PDU 由两部分组成:
- 自身地址(6 字节):设备的 BLE MAC 地址 [ 公有或随机 ( 由TxAdd说明 ) ],扫描方通过这个地址识别设备。
- 有效载荷(Payload,0-31 字节):这就是我们在代码中通过
esp_ble_adv_data_t配置的 广播数据(Adv Data),也是最重要的数据
3. 广播数据
广播数据里面包含若干个广播结构体,蓝牙规范里面称为AD Structure。如下

每个结构体里面有三部分,分别是长度(一字节)、类型(一字节)、内容(n字节),其中,长度部分所标示的数据是类型的长度加上内容的长度,即n+1。
举个例子(如下图),第一个结构体里面的长度是0x04,说明后面的类型加上数据总共有四个字节。

广播数据总共最多能够有31个字节,如果所需的字节数不超过31个,后面会自动补零 。
我们如果想知道广播结构体里面的数据的具体含义,需要知道它的类型是什么含义,下面是我们一些常用到的类型:
这些类型在蓝牙协议规范的文档中都有说明,大家可以自行上网寻找文档查阅。
以下面为例:
这样广播包就一定程度包含了广博设备的一些基础信息,但是有人就会问了:广播数据只有31字节,会不会不够用呢?那那一些放不下的数据咋办?
别担心,只要思想不滑坡,办法总比困难多。前面提到 “ 如果扫描设备接收到了广播且向广播设备发出了扫描请求,广播设备就会发出扫描响应(提供更多的信息)”,这里的扫描响应就是用来提供在广播数据里放不下的数据和信息的,这里是广播的内容,具体事情我们后面在谈。
二、广播的代码实现
在代码实现部分,我们主要是调用主机层的API进行驱动,而在广播流程中,在主机层中参与广播的有GAP层和L2CAP层,但是L2CAP层只负责广播数据的封装,不涉及广播参数的配置,所以我们需要学习的是GAP层的一些函数
而开启蓝牙广播主要有下列步骤:初始化,配置广播参数和广播数据,GAP层操作
1.初始化
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
// 初始化蓝牙控制器
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "初始化蓝牙控制器失败,错误码: 0x%x", ret);
return;
}
// 启用 BLE 模式
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "启用蓝牙控制器失败,错误码: 0x%x", ret);
return;
}
// 初始化 Bluedroid 协议栈
ret = esp_bluedroid_init();
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "初始化 Bluedroid 失败,错误码: 0x%x", ret);
return;
}
// 启用 Bluedroid 协议栈
ret = esp_bluedroid_enable();
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "启用 Bluedroid 失败,错误码: 0x%x", ret);
return;
}
主要流程是:NVS 初始化 → 蓝牙控制器初始化 → BLE 模式启用 → 协议栈初始化。这里学会调用这些函数就可以了。
2.配置广播相应参数和广播数据
// ========================== 基础配置项 ==========================
// 蓝牙设备名称(广播包中会包含该名称,手机扫描时可直接看到)
#define DEVICE_NAME "ESP32_BLE"
// 自定义服务UUID(2字节,BLE广播中用于标识设备提供的服务类型)
// 注意:此处为16位UUID,若需使用128位UUID需改为16字节数组
static uint8_t service_uuid[] = {0x00, 0xFF};
// 厂商自定义数据(广播包中携带的厂商私有数据,格式:厂商ID + 自定义数据)
// 组成说明:
// - 前2字节:厂商ID(0x1234,BLE要求小端存储,所以实际是0x34, 0x12)
// - 后1字节:自定义数据(0x01,示例表示版本1.0)
static uint8_t manufacturer_data[] = {0x34, 0x12, 0x01};
// ========================== 广播参数配置 ==========================
// BLE广播参数结构体(控制广播的行为:间隔、类型、通道等)
esp_ble_adv_params_t adv_params = {
.adv_int_min = 0x20, // 最小广播间隔(单位:0.625ms)→ 0x20*0.625ms = 20ms
.adv_int_max = 0x40, // 最大广播间隔(单位:0.625ms)→ 0x40*0.625ms = 40ms
// 说明:实际广播间隔会在min~max之间随机,降低同频干扰
.adv_type = ADV_TYPE_IND, // 广播类型:ADV_TYPE_IND(可连接的非定向广播)
// 常用类型:
// - ADV_TYPE_IND:可连接、非定向(手机可扫描+连接)
// - ADV_TYPE_NONCONN_IND:不可连接(仅广播,无法连接)
.channel_map = ADV_CHNL_ALL, // 广播通道:ADV_CHNL_ALL(37/38/39全通道广播)
// BLE广播固定使用3个通道,全通道保证兼容性
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY // 广播过滤策略
// 此处表示:允许任意设备扫描、任意设备连接
};
// ========================== 广播数据配置 ==========================
// BLE广播数据包配置(控制广播包中携带的内容:设备名、UUID、厂商数据等)
// 触发:调用esp_ble_gap_config_adv_data()后,会触发ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT事件
esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false, // 是否为扫描响应包:false(主广播包)、true(扫描响应包)
// 说明:主广播包长度有限(31字节),扫描响应包可补充更多数据
.include_name = true, // 是否在广播包中包含设备名称:true(包含)
.include_txpower = NULL,// 是否包含发射功率:NULL(不包含)
.min_interval = 0, // 最小连接间隔(仅连接模式有效,广播模式下无意义)
.max_interval = 0, // 最大连接间隔(仅连接模式有效,广播模式下无意义)
.appearance = 0, // 设备外观(用于标识设备类型,如手环、耳机等)
// 0表示无特殊外观,可选值参考:https://www.bluetooth.com/specifications/assigned-numbers/appearance/
// ---------------- 厂商数据配置(关键错误点) ----------------
.manufacturer_len = 0, // ★ 错误:厂商数据长度应设为sizeof(manufacturer_data)=3
// 此处设为0会导致厂商数据无法被广播
.p_manufacturer_data = manufacturer_data, // 厂商数据指针(指向上面定义的数组)
// ---------------- 服务数据配置(关键错误点) ----------------
.service_data_len = sizeof(manufacturer_data), // ★ 错误:服务数据未使用,应设为0
.p_service_data = NULL, // 服务数据指针(未使用,设为NULL)
// ---------------- 服务UUID配置(关键错误点) ----------------
.service_uuid_len = 16, // ★ 错误:service_uuid是2字节,应设为sizeof(service_uuid)=2
// 设为16会导致UUID解析异常
.p_service_uuid = service_uuid, // 服务UUID指针(指向上面定义的数组)
// ---------------- 广播标志位 ----------------
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
// 广播标志:
// - ESP_BLE_ADV_FLAG_GEN_DISC:通用可发现模式(手机能扫描到)
// - ESP_BLE_ADV_FLAG_BREDR_NOT_SPT:不支持经典蓝牙(仅BLE)
};
| adv_data | 定义广播包里面装什么数据(设备名、UUID等) |
adv_params | 定义广播包怎么发(间隔、类型、通道等) |
3.GAP层
/**
* @brief BLE GAP层事件回调函数
* @param event 触发的GAP事件类型(如广播数据配置完成、广播启动完成等)
* @param param 事件对应的参数结构体,包含事件结果、状态码等信息
* @note ESP32 BLE的GAP操作均为异步触发,所有结果通过此回调函数返回
*/
void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
// 存储函数调用返回值,用于判断操作是否成功
esp_err_t ret;
// 根据不同的GAP事件类型执行对应处理逻辑
switch (event) {
// 事件:广播数据配置完成(调用esp_ble_gap_config_adv_data后触发)
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
// 配置完广播数据后,立即启动BLE广播
ret = esp_ble_gap_start_advertising(&adv_params);
break;
// 事件:广播启动操作完成(调用esp_ble_gap_start_advertising后触发)
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
// 检查广播启动状态:失败/成功分支处理
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
// 广播启动失败,param->adv_start_cmpl.status为错误码
} else {
// 广播启动成功,设备开始对外发送BLE广播包
}
break;
// 事件:广播停止操作完成(调用esp_ble_gap_stop_advertising后触发)
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
// 检查广播停止状态:失败/成功分支处理
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
// 广播停止失败,param->adv_stop_cmpl.status为错误码
} else {
// 广播已成功停止,设备不再发送广播包
}
break;
// 其他未处理的GAP事件(如扫描请求、连接请求等)
default:
break;
}
}
/**
* @brief 初始化BLE GAP(通用访问配置文件)功能
* @return esp_err_t 初始化结果:ESP_OK表示成功,其他值为错误码
* @note 该函数完成两大核心操作:设置设备名称、配置广播数据包
*/
esp_err_t gap_init(void) {
// 存储函数调用返回值,用于判断每一步操作是否成功
esp_err_t ret;
// 第一步:设置BLE设备名称(广播包中会携带此名称,手机扫描时可见)
ret = esp_ble_gap_set_device_name(DEVICE_NAME);
// 设备名称设置失败则直接返回错误码,终止初始化流程
if (ret != ESP_OK) {
return ret;
}
// 第二步:配置BLE广播数据包内容(如设备名、UUID、厂商数据等)
// 调用后会异步触发ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT事件
ret = esp_ble_gap_config_adv_data(&adv_data);
// 广播数据配置失败仅记录状态,不终止流程(可根据需求修改为return ret)
// GAP初始化核心步骤完成,返回成功
return ESP_OK;
}
每调用一个 GAP 层 API(如配置广播、启动广播、设置设备名),系统完成该操作后就会触发对回调函数;
完整代码如下:(这是完整的正确的代码,修改过的,前面的代码有小纰漏)
#include "ble_ad.h"
#include "freertos/FreeRTOS.h"
#include "sdkconfig.h"
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <inttypes.h>
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_bt_main.h"
#include "esp_timer.h"
#include "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// GAP 相关日志标签
#define GAP_TAG "GAP_BLE_demo"
// ========================== 基础配置项 ==========================
// 蓝牙设备名称(广播包中会包含该名称,手机扫描时可直接看到)
#define DEVICE_NAME "ESP32_BLE"
// 自定义服务UUID(2字节,BLE广播中用于标识设备提供的服务类型)
// 注意:此处为16位UUID,若需使用128位UUID需改为16字节数组
static uint8_t service_uuid[] = {0x00, 0xFF};
// 厂商自定义数据(广播包中携带的厂商私有数据,格式:厂商ID + 自定义数据)
// 组成说明:
// - 前2字节:厂商ID(0x1234,BLE要求小端存储,所以实际是0x34, 0x12)
// - 后1字节:自定义数据(0x01,示例表示版本1.0)
static uint8_t manufacturer_data[] = {0x34, 0x12, 0x01};
// ========================== 广播参数配置 ==========================
// BLE广播参数结构体(控制广播的行为:间隔、类型、通道等)
esp_ble_adv_params_t adv_params = {
.adv_int_min = 0x20, // 最小广播间隔(单位:0.625ms)→ 0x20*0.625ms = 20ms
.adv_int_max = 0x40, // 最大广播间隔(单位:0.625ms)→ 0x40*0.625ms = 40ms
// 说明:实际广播间隔会在min~max之间随机,降低同频干扰
.adv_type = ADV_TYPE_IND, // 广播类型:ADV_TYPE_IND(可连接的非定向广播)
// 常用类型:
// - ADV_TYPE_IND:可连接、非定向(手机可扫描+连接)
// - ADV_TYPE_NONCONN_IND:不可连接(仅广播,无法连接)
.channel_map = ADV_CHNL_ALL, // 广播通道:ADV_CHNL_ALL(37/38/39全通道广播)
// BLE广播固定使用3个通道,全通道保证兼容性
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY // 广播过滤策略
// 此处表示:允许任意设备扫描、任意设备连接
};
// ========================== 广播数据配置 ==========================
// BLE广播数据包配置(控制广播包中携带的内容:设备名、UUID、厂商数据等)
// 触发:调用esp_ble_gap_config_adv_data()后,会触发ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT事件
esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false, // 是否为扫描响应包:false(主广播包)、true(扫描响应包)
// 说明:主广播包长度有限(31字节),扫描响应包可补充更多数据
.include_name = true, // 是否在广播包中包含设备名称:true(包含)
.include_txpower = NULL,// 是否包含发射功率:NULL(不包含)
.min_interval = 0, // 最小连接间隔(仅连接模式有效,广播模式下无意义)
.max_interval = 0, // 最大连接间隔(仅连接模式有效,广播模式下无意义)
.appearance = 0, // 设备外观(用于标识设备类型,如手环、耳机等)
// 0表示无特殊外观,可选值参考:https://www.bluetooth.com/specifications/assigned-numbers/appearance/
// ---------------- 厂商数据配置(关键错误点) ----------------
.manufacturer_len = 0, // ★ 错误:厂商数据长度应设为sizeof(manufacturer_data)=3
// 此处设为0会导致厂商数据无法被广播
.p_manufacturer_data = manufacturer_data, // 厂商数据指针(指向上面定义的数组)
// ---------------- 服务数据配置(关键错误点) ----------------
.service_data_len = sizeof(manufacturer_data), // ★ 错误:服务数据未使用,应设为0
.p_service_data = NULL, // 服务数据指针(未使用,设为NULL)
// ---------------- 服务UUID配置(关键错误点) ----------------
.service_uuid_len = 16, // ★ 错误:service_uuid是2字节,应设为sizeof(service_uuid)=2
// 设为16会导致UUID解析异常
.p_service_uuid = service_uuid, // 服务UUID指针(指向上面定义的数组)
// ---------------- 广播标志位 ----------------
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
// 广播标志:
// - ESP_BLE_ADV_FLAG_GEN_DISC:通用可发现模式(手机能扫描到)
// - ESP_BLE_ADV_FLAG_BREDR_NOT_SPT:不支持经典蓝牙(仅BLE)
};
/**
* @brief BLE GAP层事件回调函数
* @param event 触发的GAP事件类型(如广播数据配置完成、广播启动完成等)
* @param param 事件对应的参数结构体,包含事件结果、状态码等信息
* @note ESP32 BLE的GAP操作均为异步触发,所有结果通过此回调函数返回
*/
void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
// 存储函数调用返回值,用于判断操作是否成功
esp_err_t ret;
// 根据不同的GAP事件类型执行对应处理逻辑
switch (event) {
// 事件:广播数据配置完成(调用esp_ble_gap_config_adv_data后触发)
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
// 配置完广播数据后,立即启动BLE广播
ret = esp_ble_gap_start_advertising(&adv_params);
break;
// 事件:广播启动操作完成(调用esp_ble_gap_start_advertising后触发)
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
// 检查广播启动状态:失败/成功分支处理
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
// 广播启动失败,param->adv_start_cmpl.status为错误码
} else {
// 广播启动成功,设备开始对外发送BLE广播包
}
break;
// 事件:广播停止操作完成(调用esp_ble_gap_stop_advertising后触发)
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
// 检查广播停止状态:失败/成功分支处理
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
// 广播停止失败,param->adv_stop_cmpl.status为错误码
} else {
// 广播已成功停止,设备不再发送广播包
}
break;
// 其他未处理的GAP事件(如扫描请求、连接请求等)
default:
break;
}
}
/**
* @brief 初始化BLE GAP(通用访问配置文件)功能
* @return esp_err_t 初始化结果:ESP_OK表示成功,其他值为错误码
* @note 该函数完成两大核心操作:设置设备名称、配置广播数据包
*/
esp_err_t gap_init(void) {
// 存储函数调用返回值,用于判断每一步操作是否成功
esp_err_t ret;
// 第一步:设置BLE设备名称(广播包中会携带此名称,手机扫描时可见)
ret = esp_ble_gap_set_device_name(DEVICE_NAME);
// 设备名称设置失败则直接返回错误码,终止初始化流程
if (ret != ESP_OK) {
return ret;
}
// 第二步:配置BLE广播数据包内容(如设备名、UUID、厂商数据等)
// 调用后会异步触发ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT事件
ret = esp_ble_gap_config_adv_data(&adv_data);
// 广播数据配置失败仅记录状态,不终止流程(可根据需求修改为return ret)
// GAP初始化核心步骤完成,返回成功
return ESP_OK;
}
void system_run() {
// 初始化 NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
// 初始化蓝牙控制器
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "初始化蓝牙控制器失败,错误码: 0x%x", ret);
return;
}
// 启用 BLE 模式
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "启用蓝牙控制器失败,错误码: 0x%x", ret);
return;
}
// 初始化 Bluedroid 协议栈
ret = esp_bluedroid_init();
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "初始化 Bluedroid 失败,错误码: 0x%x", ret);
return;
}
// 启用 Bluedroid 协议栈
ret = esp_bluedroid_enable();
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "启用 Bluedroid 失败,错误码: 0x%x", ret);
return;
}
// 注册 GAP 回调(放在广播配置前)
ret = esp_ble_gap_register_callback(esp_gap_cb);
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "注册 GAP 回调失败,错误码: 0x%x", ret);
return;
}
// 初始化 GAP 广播
ret = gap_init();
if (ret != ESP_OK) {
// ESP_LOGE(GAP_TAG, "GAP 初始化失败");
return;
}
// 主循环
while (1) {
vTaskDelay(10);
}
}
更多推荐



所有评论(0)