3. 解析广播数据包
任务说明
BLE广播信道上的数据包以AD Structure(Advertising Data Structure)形式承载信息。每个AD Structure由Length、Type、Data三部分组成。本案例要求编写一个解析器,从原始广播数据中提取设备名称(Type=0x09/0x08)和服务UUID(Type=0x02/0x03/0x06/0x07),并输出结构化结果。
AD Structure格式
代码实现
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define AD_TYPE_FLAGS 0x01
#define AD_TYPE_INCOMPLETE_16UUID 0x02
#define AD_TYPE_COMPLETE_16UUID 0x03
#define AD_TYPE_INCOMPLETE_32UUID 0x04
#define AD_TYPE_COMPLETE_32UUID 0x05
#define AD_TYPE_INCOMPLETE_128UUID 0x06
#define AD_TYPE_COMPLETE_128UUID 0x07
#define AD_TYPE_SHORT_NAME 0x08
#define AD_TYPE_COMPLETE_NAME 0x09
typedef struct {
uint8_t length;
uint8_t type;
uint8_t* data;
} ad_structure_t;
typedef struct {
char device_name[64];
uint16_t uuid16[8];
uint8_t uuid16_count;
uint8_t uuid128[8][16];
uint8_t uuid128_count;
} parsed_ad_t;
static void parse_uuid16(uint8_t* data, uint8_t len, parsed_ad_t* out) {
for (int i = 0; i + 1 < len; i += 2) {
if (out->uuid16_count < 8) {
out->uuid16[out->uuid16_count++] = data[i] | (data[i+1] << 8);
}
}
}
static void parse_uuid128(uint8_t* data, uint8_t len, parsed_ad_t* out) {
if (len == 16 && out->uuid128_count < 8) {
memcpy(out->uuid128[out->uuid128_count++], data, 16);
}
}
int parse_ad_structures(uint8_t* adv_data, uint8_t adv_len, parsed_ad_t* out) {
if (!adv_data || !out || adv_len == 0) return -1;
memset(out, 0, sizeof(parsed_ad_t));
uint8_t offset = 0;
while (offset < adv_len) {
uint8_t length = adv_data[offset];
if (length == 0) break; // 结束标志
if (offset + 1 + length > adv_len) return -2; // 数据越界
ad_structure_t ad;
ad.length = length;
ad.type = adv_data[offset + 1];
ad.data = &adv_data[offset + 2];
switch (ad.type) {
case AD_TYPE_COMPLETE_NAME:
case AD_TYPE_SHORT_NAME: {
uint8_t name_len = ad.length - 1;
if (name_len < sizeof(out->device_name)) {
memcpy(out->device_name, ad.data, name_len);
out->device_name[name_len] = '\0';
}
break;
}
case AD_TYPE_INCOMPLETE_16UUID:
case AD_TYPE_COMPLETE_16UUID:
parse_uuid16(ad.data, ad.length - 1, out);
break;
case AD_TYPE_INCOMPLETE_128UUID:
case AD_TYPE_COMPLETE_128UUID:
parse_uuid128(ad.data, ad.length - 1, out);
break;
default:
break; // 其他类型忽略
}
offset += 1 + length;
}
return 0;
}
void print_result(parsed_ad_t* result) {
printf("设备名称: %s\n", result->device_name[0] ? result->device_name : "(未提供)");
printf("16位UUID (%d个):\n", result->uuid16_count);
for (int i = 0; i < result->uuid16_count; i++) {
printf(" 0x%04X\n", result->uuid16[i]);
}
printf("128位UUID (%d个):\n", result->uuid128_count);
for (int i = 0; i < result->uuid128_count; i++) {
printf(" ");
for (int j = 15; j >= 0; j--) {
printf("%02X", result->uuid128[i][j]);
if (j % 2 == 0 && j != 0) printf("-");
}
printf("\n");
}
}
int main() {
// 示例广播数据(十六进制)
// 02 01 06 - Flags
// 05 09 48 65 6C 6C - 设备名称 "Hell"
// 03 03 0F 18 - 16位UUID 0x180F (Battery Service)
// 11 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 128位UUID全零
uint8_t adv_data[] = {
0x02, 0x01, 0x06,
0x05, 0x09, 0x48, 0x65, 0x6C, 0x6C,
0x03, 0x03, 0x0F, 0x18,
0x11, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
uint8_t adv_len = sizeof(adv_data);
parsed_ad_t result;
int ret = parse_ad_structures(adv_data, adv_len, &result);
if (ret != 0) {
printf("解析失败,错误码: %d\n", ret);
return 1;
}
print_result(&result);
return 0;
}
编译与运行
gcc -o ble_ad_parser ble_ad_parser.c
./ble_ad_parser
预期输出
设备名称: Hell
16位UUID (1个):
0x180F
128位UUID (1个):
00000000-0000-0000-0000-000000000000
验证方法
使用上述示例数据运行程序,观察输出是否与预期输出一致。若一致,说明解析逻辑正确。可进一步修改adv_data中的设备名称长度或UUID值,验证解析器能否正确提取变化后的数据。对于真实BLE设备,可通过抓包工具(如nRF Connect、Wireshark配合BLE dongle)获取广播原始字节,替换示例数据后运行程序验证。