ESP32 BLE扫描实战:手把手教你用ESP-IDF API解析广播包里的设备名、UUID和自定义数据

张开发
2026/4/19 20:04:15 15 分钟阅读

分享文章

ESP32 BLE扫描实战:手把手教你用ESP-IDF API解析广播包里的设备名、UUID和自定义数据
ESP32 BLE广播数据解析实战从设备名到自定义数据的完整指南在智能家居和物联网应用中BLE低功耗蓝牙设备间的通信已成为标配。作为开发者我们经常需要从BLE设备的广播包中提取关键信息比如设备名称、服务UUID以及厂商自定义数据。本文将深入探讨如何利用ESP32的ESP-IDF框架高效解析这些隐藏在广播数据中的宝贵信息。1. BLE广播数据基础解析BLE设备通过广播包向外传递信息这些数据包遵循特定的结构。每个广播数据单元由长度、类型和值三部分组成[长度][类型][值][长度][类型][值]...ESP-IDF提供了esp_ble_resolve_adv_data()函数来简化解析过程。这个函数接收原始广播数据、目标数据类型和长度指针返回对应数据的指针。常见广播数据类型AD Type包括类型常量值描述ESP_BLE_AD_TYPE_NAME_CMPL0x09完整设备名ESP_BLE_AD_TYPE_16SRV_PART0x0216位UUID部分列表ESP_BLE_AD_TYPE_16SRV_CMPL0x0316位UUID完整列表ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE0xFF厂商自定义数据在智能家居场景中温湿度传感器可能通过厂商自定义数据字段直接发送当前读数而门磁传感器则可能用特定UUID标识设备类型。2. 实战解析设备名称设备名称是最基础也是最重要的信息之一它帮助用户识别设备。以下是提取设备名的完整代码示例uint8_t *adv_name NULL; uint8_t adv_name_len 0; void handle_scan_result(esp_ble_gap_cb_param_t *scan_result) { adv_name esp_ble_resolve_adv_data( scan_result-scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, adv_name_len ); if(adv_name_len 0) { ESP_LOGI(BLE_SCAN, 发现设备: %.*s, adv_name_len, (char*)adv_name); } }关键点说明当设备名超过31字节时部分设备可能只发送缩短的名称AD Type 0x08设备名不一定以null结尾必须依赖返回的长度参数某些设备可能将名称放在扫描响应包而非广播包中提示实际项目中建议同时检查0x08和0x09两种类型并优先使用完整名称。3. 深入UUID提取技巧服务UUID是识别设备功能的关键。BLE设备可能公布部分或全部服务UUIDuint8_t *uuid_list; uint8_t uuid_len; void extract_uuids(esp_ble_gap_cb_param_t *scan_result) { // 尝试获取完整UUID列表 uuid_list esp_ble_resolve_adv_data( scan_result-scan_rst.ble_adv, ESP_BLE_AD_TYPE_16SRV_CMPL, uuid_len ); if(uuid_len 0) { // 回退到部分UUID列表 uuid_list esp_ble_resolve_adv_data( scan_result-scan_rst.ble_adv, ESP_BLE_AD_TYPE_16SRV_PART, uuid_len ); } if(uuid_len 0) { ESP_LOGI(BLE_SCAN, 发现UUID列表:); for(int i 0; i uuid_len; i 2) { uint16_t uuid (uuid_list[i1] 8) | uuid_list[i]; ESP_LOGI(BLE_SCAN, - 0x%04X, uuid); } } }UUID处理注意事项16位UUID是蓝牙SIG定义的标准化服务标识每个UUID占2字节列表长度应为偶数对于32位和128位UUID需要使用对应的AD Type4. 厂商自定义数据解析实战厂商自定义数据Manufacturer Specific Data是设备厂商的自由发挥空间通常包含设备特有的信息。以下是解析示例typedef struct { uint16_t company_id; // 蓝牙公司标识符 uint8_t data[]; // 厂商自定义数据 } manufacturer_data_t; void handle_manufacturer_data(esp_ble_gap_cb_param_t *scan_result) { uint8_t *mfg_data; uint8_t mfg_data_len; mfg_data esp_ble_resolve_adv_data( scan_result-scan_rst.ble_adv, ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE, mfg_data_len ); if(mfg_data_len 2) { // 至少包含2字节的公司ID manufacturer_data_t *data (manufacturer_data_t*)mfg_data; ESP_LOGI(BLE_SCAN, 厂商ID: 0x%04X,>void handle_scan_result_complete(esp_ble_gap_cb_param_t *scan_result) { // 合并广播数据和扫描响应数据 uint8_t total_len scan_result-scan_rst.adv_data_len scan_result-scan_rst.scan_rsp_len; uint8_t *combined_data malloc(total_len); memcpy(combined_data, scan_result-scan_rst.ble_adv, scan_result-scan_rst.adv_data_len); memcpy(combined_data scan_result-scan_rst.adv_data_len, scan_result-scan_rst.ble_adv scan_result-scan_rst.adv_data_len, scan_result-scan_rst.scan_rsp_len); // 从合并数据中解析 uint8_t *name; uint8_t name_len; name esp_ble_resolve_adv_data(combined_data, ESP_BLE_AD_TYPE_NAME_CMPL, name_len); free(combined_data); }5.2 信号强度与距离估算RSSI接收信号强度指示可用于粗略估计设备距离void log_device_distance(esp_ble_gap_cb_param_t *scan_result) { int8_t rssi scan_result-scan_rst.rssi; uint8_t *tx_power esp_ble_resolve_adv_data( scan_result-scan_rst.ble_adv, ESP_BLE_AD_TYPE_TX_PWR, NULL ); if(tx_power) { int8_t tx_pwr *tx_power; // 简单距离估算米 double distance pow(10, (tx_pwr - rssi) / 20.0); ESP_LOGI(BLE_SCAN, 预估距离: %.2f米, distance); } }5.3 过滤重复设备连续扫描时可以使用设备地址和部分特征过滤重复报告#define MAX_DEVICES 20 typedef struct { esp_bd_addr_t addr; uint32_t last_seen; } seen_device_t; seen_device_t seen_devices[MAX_DEVICES]; bool is_new_device(esp_bd_addr_t addr) { for(int i 0; i MAX_DEVICES; i) { if(memcmp(seen_devices[i].addr, addr, ESP_BD_ADDR_LEN) 0) { seen_devices[i].last_seen xTaskGetTickCount(); return false; } } // 找到空位或最旧的记录 int oldest 0; for(int i 1; i MAX_DEVICES; i) { if(seen_devices[i].last_seen seen_devices[oldest].last_seen) { oldest i; } } memcpy(seen_devices[oldest].addr, addr, ESP_BD_ADDR_LEN); seen_devices[oldest].last_seen xTaskGetTickCount(); return true; }6. 实战案例智能家居网关实现结合以上技术我们可以实现一个完整的智能家居网关示例typedef enum { DEVICE_UNKNOWN, DEVICE_THERMOMETER, DEVICE_DOOR_SENSOR, DEVICE_SMART_PLUG } device_type_t; typedef struct { esp_bd_addr_t addr; device_type_t type; char name[32]; time_t last_update; union { struct { float temperature; float humidity; } thermo; struct { bool is_open; } door; } data; } smart_device_t; void handle_smart_home_device(esp_ble_gap_cb_param_t *scan_result) { // 1. 获取设备基本信息 smart_device_t device; memcpy(device.addr, scan_result-scan_rst.bda, ESP_BD_ADDR_LEN); // 2. 解析设备名 uint8_t *name esp_ble_resolve_adv_data( scan_result-scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, name_len ); if(name_len 0) { strncpy(device.name, (char*)name, MIN(name_len, sizeof(device.name)-1)); } // 3. 解析厂商数据确定设备类型 uint8_t *mfg_data; uint8_t mfg_data_len; mfg_data esp_ble_resolve_adv_data( scan_result-scan_rst.ble_adv, ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE, mfg_data_len ); if(mfg_data_len 3 ((manufacturer_data_t*)mfg_data)-company_id 0x02A5) { uint8_t *data mfg_data 2; // 跳过公司ID switch(data[0]) { case 0x01: device.type DEVICE_THERMOMETER; device.data.thermo.temperature ((data[1] 8) | data[2]) / 10.0; device.data.thermo.humidity ((data[3] 8) | data[4]) / 10.0; break; case 0x02: device.type DEVICE_DOOR_SENSOR; device.data.door.is_open data[1]; break; } } device.last_update time(NULL); update_device_in_database(device); }这个示例展示了如何将各种BLE数据解析技术整合到一个实际的智能家居网关应用中实现了设备发现、识别和状态更新的完整流程。

更多文章