ESP32 - 从广告中提取制造商特定数据

ESP32 - Extract Manufacturer Specific Data from advertisement

TL;DR: 一个 ESP32 通过 BLE 广播(已经工作),另一个 ESP32 监听。我无法正确解析收到的广告,即无法提取制造商特定数据!


目标: 一个 ESP32(调用 A)广播包含制造商特定数据 (MSD) 的广告,该广告由另一个 ESP32(调用 B)接收,并将该数据打印到控制台。

我正在使用支持蓝牙 5.0 的新的基于 RISC-V 的 ESP32C3,尽管我所做的一切都是基于蓝牙 4.2。

我所在的位置:

  1. A 可以播放有效的广告(用 Ubertooth/Wireshark 检查)
  2. B 从 A 收到 东西 ,尽管数据包与 Ubertooth 收到的(正确的)数据包非常松散地对应。

代码:

用于设置 A 的结构:

// Struct defining advertising parameters
static esp_ble_adv_params_t ble_adv_params = {
    .adv_int_min        = 0x0800,
    .adv_int_max        = 0x0900,
    .adv_type           = ADV_TYPE_NONCONN_IND,
    .own_addr_type      = BLE_ADDR_TYPE_PUBLIC,
    .channel_map        = ADV_CHNL_ALL,
    .adv_filter_policy  = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, // NO IDEA ABOUT THAT ONE, ESPECIALLY GIVEN THAT WE SEND NONCONNECTABLE AND NONSCANNABLE ADVERTISEMENTS!
};

用于定义广告负载的结构:

    esp_ble_adv_data_t ble_adv_data =
    {
            .set_scan_rsp           = false,
            .include_name           = true,                             // "Name" refers to the name passed as an argument within "esp_ble_gap_set_device_name()"
            .include_txpower        = false,
            .min_interval           = 0xffff,                           // Not sure what those are for, as the chosen advertisement packets are non-connectable...
            .max_interval           = 0xFFFF,
            .appearance             = 64,                               // 64 is appearance ID of phone. Only to maybe be able to find it on my Galaxy phone
            .manufacturer_len       = ble_adv_payload_len,
            .p_manufacturer_data    = (uint8_t *) ble_adv_payload,      // Currently just some human-readable string used for debugging
            .service_data_len       = 0,
            .p_service_data         = NULL,
            .service_uuid_len       = 0,
            .p_service_uuid         = NULL,
            .flag = 0
    };

由于Ubertooth收到了A发送的正确数据包,我认为A设置正确。

用于定义 B 的扫描行为的结构:

// Struct defining scanning parameters
static esp_ble_scan_params_t ble_scan_params = {
    .scan_type              = BLE_SCAN_TYPE_PASSIVE,        // Don't send scan requests upon receiving an advertisement
    .own_addr_type          = BLE_ADDR_TYPE_PUBLIC,         // Use (static) public address, makes debugging easier
    .scan_filter_policy     = BLE_SCAN_FILTER_ALLOW_ONLY_WLST,  // Consider all advertisements
    .scan_interval          = 0x50,                         // Time between each scan window begin
    .scan_window            = 0x30,                         // Length of scan window
    .scan_duplicate         = BLE_SCAN_DUPLICATE_DISABLE        // Filters out duplicate advertisements, e.g. if an advertisement received k times, it is only reported once
};

剩下的大部分代码只是样板文件,唯一真正相关的部分是 B 的回调函数,每当 GAP 事件发生时都会调用它(GAP 事件可以在 esp_gap_ble_api.h 中找到,从第 138 行开始。

B的回调函数:

void esp_ble_callback_fun(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    // Do a case split on the different events. For now, only "ESP_GAP_BLE_SCAN_RESULT_EVT" is of interest
    switch (event) { 
    case ESP_GAP_BLE_SCAN_RESULT_EVT:
        if ((param->scan_rst).search_evt != ESP_GAP_SEARCH_INQ_RES_EVT) {
            printf(
                    "B: Callback function received a non-\"ESP_GAP_SEARCH_INQ_RES_EVT\" event!\n");
            return;
        }

        // Copy the parameter UNION
        esp_ble_gap_cb_param_t scan_result = *param;


        // Create a POINTER to the "bda" entry, which is accessed by interpreting the scan_result UNION as a scan_rst STRUCT
        // Note that "esp_bd_addr_t" is a typedef for a uint8_t array!
        esp_bd_addr_t *ble_adv_addr = &scan_result.scan_rst.bda;

        printf("\n-------------------------\nMessage: \n");
        uint8_t adv_data_len  = scan_result.scan_rst.adv_data_len;
        uint8_t *adv_data     = scan_result.scan_rst.ble_adv;

        printf("Message length: %i\n", adv_data_len);
        printf("Message body:\n");                  // NOT SO SURE ABOUT THIS!

        for(int i = 0; i < adv_data_len; ++i)
        {
            printf("%X", adv_data[i]);
        }
        printf("\n-------------------------\n");

        break; // @suppress("No break at end of case")
    default:
        // NOT SUPPORTED! JUST IGNORE THEM!
        break;
    }
}

示例输出:

B串口输出:

Message: 
Message length: 22
Message body:
31940069414C4943454FF486579512FFFFFFFF

在 Wireshark 中收到的数据包:

Frame 78135: 61 bytes on wire (488 bits), 61 bytes captured (488 bits) on interface /tmp/pipe, id 0
PPI version 0, 24 bytes
    Version: 0
    Flags: 0x00
        .... ...0 = Alignment: Not aligned
        0000 000. = Reserved: 0x00
    Header length: 24
    DLT: 251
    Reserved: 36750c0000620900e05b56b811051000
Bluetooth
Bluetooth Low Energy Link Layer
    Access Address: 0x8e89bed6
    Packet Header: 0x1c22 (PDU Type: ADV_NONCONN_IND, ChSel: #2, TxAdd: Public)
        .... 0010 = PDU Type: ADV_NONCONN_IND (0x2)
        ...0 .... = RFU: 0
        ..1. .... = Channel Selection Algorithm: #2
        .0.. .... = Tx Address: Public
        0... .... = Reserved: False
        Length: 28
    Advertising Address: Espressi_43:3e:d6 (7c:df:a1:43:3e:d6)
    Advertising Data
        Appearance: Generic Phone
        Device Name: ALICE
        Manufacturer Specific
        Slave Connection Interval Range: 81918.8 - 81918.8 msec
        Connection Interval Min: 65535 (81918.8 msec)
        Connection Interval Max: 65535 (81918.8 msec)
    CRC: 0x905934

0000   00 00 18 00 fb 00 00 00 36 75 0c 00 00 62 09 00   ........6u...b..
0010   e0 5b 56 b8 11 05 10 00 d6 be 89 8e 22 1c d6 3e   .[V........."..>
0020   43 a1 df 7c 03 19 40 00 06 09 41 4c 49 43 45 04   C..|..@...ALICE.
0030   ff 48 65 79 05 12 ff ff ff ff 09 9a 2c            .Hey........,

对于没有 MSD 的数据包,我计算了 B 接收到的数据包的二进制表示(即 adv_data 的内容)与 Ubertooth 接收到的数据包之间的最长公共子序列。他们只有 46 位共同点,这肯定是一个奇怪的数字!

我的问题:

  1. 我假设 adv_data 是否正确,即 scan_result.scan_rst.ble_adv 包含原始 BLE 数据包? ble_adv(esp_gap_ble_api.h,第 936 行)的定义是 令人难以置信的 令人困惑的 IMO,因为它被称为“接收到的 EIR”,尽管 EIR 仅在蓝牙 5.0 中引入。 ..
  2. 如何从收到的 BLE 广告中提取 MSD?

EIR 是很久以前引入的,并且出现在蓝牙 4.0 中。

您应该在打印十六进制字符串时使用 %02X,因为这将包括前导零。

ble_adv只包含EIR内容,不包含整个数据包。

EIR 使用长度、类型、值编码。您的制造数据编码如下:

4(长度) 0xff(制造商数据) 嗨(内容)

注意制造商数据内容的两个字节应该是Bluetooth SIG注册的公司id。