ESP32 BLE广播包中自定义厂商数据的动态更新与抓包验证实战
👁 1 阅读 · 2026-08-27 · 嵌入式
在物联网开发中,ESP32的BLE广播是设备发现的核心机制。然而,默认广播数据无法满足复杂应用需求,尤其是需要动态更新设备状态时。本文将深入讲解如何在ESP32上通过esp_ble_gap_config_adv_data()动态更新广播包中的厂商自定义数据,并利用nRF Connect或Wireshark进行抓包验证。你将掌握从原理到代码的完整流程,并避开常见坑点,提升开发效率。
# ESP32 BLE广播包中自定义厂商数据的动态更新与抓包验证实战
## 一、原理:广播数据与厂商字段
BLE广播包(Advertising Packet)由多个AD Structure组成,每个结构包含长度、类型和数据。其中,类型`0xFF`表示厂商自定义数据(Manufacturer Specific Data),格式为:
- 2字节公司ID(Company ID,如0x004C为Apple,0x0059为Nordic)
- 自定义数据(长度可变)
ESP32的BLE协议栈通过`esp_ble_adv_data_t`结构体配置广播数据,其中`manufacturer_len`和`manufacturer_data`字段用于设置厂商数据。动态更新即指在广播过程中,通过`esp_ble_gap_config_adv_data()`函数重新配置广播数据,使设备状态变化实时反映在广播包中。
## 二、硬件与软件准备
- 硬件:ESP32开发板(如ESP32-DevKitC)
- 软件:ESP-IDF v4.4+(或Arduino-ESP32,但本文以ESP-IDF为例)
- 抓包工具:手机nRF Connect App,或PC端Wireshark + BLE dongle(如nRF52840)
## 三、配置步骤
### 1. 初始化BLE并设置广播参数
```c
#include
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
// 厂商数据缓冲区(假设公司ID为0xFFFF,自定义数据为4字节状态)
static uint8_t manufacturer_data[6] = {0xFF, 0xFF, 0x01, 0x02, 0x03, 0x04};
// 广播数据配置结构体
static esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
.min_interval = 0x20,
.max_interval = 0x40,
.appearance = 0x00,
.manufacturer_len = sizeof(manufacturer_data),
.manufacturer_data = manufacturer_data,
};
void ble_init() {
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();
esp_bt_controller_init(&bt_cfg);
esp_bt_controller_enable(ESP_BT_MODE_BTDM);
esp_bluedroid_init();
esp_bluedroid_enable();
// 注册GAP回调
esp_ble_gap_register_callback(gap_event_handler);
// 设置广播参数
esp_ble_adv_params_t adv_params = {
.adv_int_min = 0x20,
.adv_int_max = 0x40,
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
esp_ble_gap_config_adv_data(&adv_data); // 初始配置
esp_ble_gap_start_advertising(&adv_params);
}
```
### 2. 动态更新厂商数据
在需要更新状态时(如传感器读数变化),修改`manufacturer_data`数组,并重新调用`esp_ble_gap_config_adv_data()`。注意:必须先停止广播或直接重新配置,ESP-IDF允许在广播中更新,但需确保数据长度一致。
```c
void update_manufacturer_data(uint8_t *new_data, uint8_t len) {
if (len > 4) return; // 假设最大4字节自定义数据
memcpy(&manufacturer_data[2], new_data, len); // 跳过公司ID
adv_data.manufacturer_len = len + 2;
adv_data.manufacturer_data = manufacturer_data;
esp_ble_gap_config_adv_data(&adv_data);
}
```
### 3. GAP事件处理
在`gap_event_handler`中处理`ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT`,确认配置完成,然后重新启动广播(如果之前停止了)。
```c
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
// 配置完成,确保广播在运行
esp_ble_gap_start_advertising(&adv_params);
break;
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
printf("广告启动失败\n");
}
break;
default:
break;
}
}
```
## 四、完整代码示例
以下是一个可运行的示例,每5秒更新一次状态值(递增):
```c
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
static uint8_t manufacturer_data[6] = {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};
static esp_ble_adv_data_t adv_data;
static esp_ble_adv_params_t adv_params;
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
if (event == ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT) {
esp_ble_gap_start_advertising(&adv_params);
}
}
void update_task(void *arg) {
uint8_t counter = 0;
while (1) {
vTaskDelay(pdMS_TO_TICKS(5000));
counter++;
manufacturer_data[2] = counter; // 更新第3字节
adv_data.manufacturer_len = sizeof(manufacturer_data);
adv_data.manufacturer_data = manufacturer_data;
esp_ble_gap_config_adv_data(&adv_data);
printf("更新广播数据: counter=%d\n", counter);
}
}
void app_main() {
// 初始化BLE...(同前)
// 配置adv_data和adv_params...
// 启动初始广播...
xTaskCreate(update_task, "update", 2048, NULL, 5, NULL);
}
```
## 五、抓包验证技巧
### 1. 使用nRF Connect(手机)
- 打开App,扫描设备,找到你的ESP32设备名。
- 点击设备,查看“ADVERTISING DATA”中的“Manufacturer Specific Data”。
- 观察数据变化:每5秒应看到第3字节递增。
### 2. 使用Wireshark + BLE dongle
- 将nRF52840 dongle插入PC,使用Wireshark捕获BLE流量。
- 过滤`btle.advertising_data.manufacturer_specific_data.company_id == 0xffff`。
- 展开数据包,查看“Manufacturer Specific Data”字段,确认动态更新。
### 3. 常见问题排查
- **数据不更新**:检查是否在`ADV_DATA_SET_COMPLETE_EVT`后重新启动广播?如果广播未停止,某些版本可能不生效。建议先`esp_ble_gap_stop_advertising()`,再配置,再启动。
- **数据长度变化**:如果长度变化,需确保`adv_data.manufacturer_len`正确,且广播数据总长度不超过31字节(含其他AD结构)。
- **抓包看不到**:确认广播类型为`ADV_TYPE_IND`(可扫描),且手机或dongle支持厂商数据解析。
## 六、注意事项
- **广播间隔**:动态更新频率不宜过高,否则会增加功耗和无线拥塞。建议至少100ms以上。
- **数据一致性**:更新过程中,广播包可能被截获为旧数据,这是正常的,因为广播是异步的。
- **内存管理**:`manufacturer_data`必须保持有效,直到配置完成事件触发。
- **兼容性**:不同ESP-IDF版本API略有差异,请参考对应文档。
## 七、总结
通过本文,你学会了在ESP32上动态更新BLE广播包中的厂商自定义数据,并掌握了抓包验证的方法。这一技巧在智能家居、信标、传感器网络等场景中非常实用。记住,动态更新广播数据是BLE开发中的高级技巧,合理运用能大幅提升设备交互体验。