ESP32学习笔记(27)——BLE GAP主机端扫描

Posted Leung_ManWah

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32学习笔记(27)——BLE GAP主机端扫描相关的知识,希望对你有一定的参考价值。

一、背景

1.1 低功耗蓝牙(BLE)协议栈


链路层(LL) 控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。

广播 为广播数据包,而 扫描 则是监听广播。

GAP通信中角色,中心设备(Central - 主机) 用来扫描和连接 外围设备(Peripheral - 从机)

大部分情况下外围设备通过广播自己来让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。

也有些情况是不需要连接的,只要外设广播自己的数据即可,用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。

1.2 扫描概念

扫描是一个在一定范围内用来寻址其他低功耗蓝牙设备广播的过程。扫描者设备在扫描过程中会使用广播信道。与广播过程不同的是,扫描过程没有严格的时间定义和信道规则。扫描过程应该按照由 Host 层所设定扫描定时参数还运行。

1.2.1 被动扫描

被动扫描:在被动扫描中,扫描设备应该仅仅去监听广播包,而不向广播设备发送任何数据。

一旦扫描参数设置完成,主机就可以在协议栈中发送命令启动扫描。扫描过程中,如果控制器接收到符合过滤策略或其他规则的广播数据包,则发送一个广播报告事件给主机。报告事件除了有广播者的设备地址外,还包括广播数据包中的数据,以及接收广播数据包时的信号接收强度。我们可以利用该信号强度以及位于广播包中的发射功率,共同确定信号的路径损失,从而给出大致的范围,这个应用就是防丢器和蓝牙定位。

1.2.2 主动扫描

主动扫描:不仅可以捕获到从端设备的广播数据包,还可以捕获扫描响应包,并区分它们。

控制器收到扫描数据后将向主机发送一个广播报告事件(adv_report),该事件包括了链路层数据包的广播类型。因此,主机能够判断从端设备是否可以连接或扫描,并且区分出广播数据包和扫描响应包。

1.3 ESP32蓝牙应用结构

蓝牙是⼀种短距通信系统,其关键特性包括鲁棒性、低功耗、低成本等。蓝牙系统分为两种不同的技术:经典蓝牙 (Classic Bluetooth) 和蓝牙低功耗 (Bluetooth Low Energy)。
ESP32 支持双模蓝牙,即同时支持经典蓝牙和蓝牙低功耗。

从整体结构上,蓝牙可分为控制器 (Controller) 和主机 (Host) 两⼤部分:控制器包括了 PHY、Baseband、Link Controller、Link Manager、Device Manager、HCI 等模块,用于硬件接⼝管理、链路管理等等;主机则包括了 L2CAP、SMP、SDP、ATT、GATT、GAP 以及各种规范,构建了向应用层提供接口的基础,方便应用层对蓝牙系统的访问。主机可以与控制器运行在同⼀个宿主上,也可以分布在不同的宿主上。ESP32 可以支持上述两种方式。

1.4 Bluedroid主机架构

在 ESP-IDF 中,使用经过大量修改后的 BLUEDROID 作为蓝牙主机 (Classic BT + BLE)。BLUEDROID 拥有较为完善的功能,⽀持常用的规范和架构设计,同时也较为复杂。经过大量修改后,BLUEDROID 保留了大多数 BTA 层以下的代码,几乎完全删去了 BTIF 层的代码,使用了较为精简的 BTC 层作为内置规范及 Misc 控制层。修改后的 BLUEDROID 及其与控制器之间的关系如下图:

二、API说明

以下控制器和虚拟 HCI 接口位于 bt/include/esp32/include/esp_bt.h

2.1 esp_bt_controller_mem_release

2.2 esp_bt_controller_init

2.3 esp_bt_controller_enable


以下 GAP 接口位于 bt/host/bluedroid/api/include/api/esp_bt_main.hbt/host/bluedroid/api/include/api/esp_gap_ble_api.h

2.4 esp_bluedroid_init

2.5 esp_bluedroid_enable

2.6 esp_ble_gap_register_callback

2.7 esp_ble_gap_set_scan_params

2.8 esp_ble_gap_start_scanning

2.9 esp_ble_gap_stop_scanning

2.10 esp_ble_resolve_adv_data

三、使用GAP接口BLE扫描

3.1 配置扫描参数

esp_ble_gap_set_scan_params() 使用 esp_ble_scan_params_t 结构体进行设置

static esp_ble_scan_params_t ble_scan_params = {
    .scan_type              = BLE_SCAN_TYPE_ACTIVE,  //扫描类型
    .own_addr_type          = BLE_ADDR_TYPE_PUBLIC,  //拥有者的蓝牙设备地址类型
    .scan_filter_policy     = BLE_SCAN_FILTER_ALLOW_ALL,//扫描过滤器设置
    .scan_interval          = 0x50,  //扫描间隔
    .scan_window            = 0x30,  //扫描窗口
    .scan_duplicate         = BLE_SCAN_DUPLICATE_DISABLE  //扫描重复类型
};

3.1.1 扫描类型

是否主动扫描,配置为1则是主动扫描,0则是被动扫描

/// Ble scan type
typedef enum {
    BLE_SCAN_TYPE_PASSIVE   =   0x0,            /*!< Passive scan */
    BLE_SCAN_TYPE_ACTIVE    =   0x1,            /*!< Active scan */
} esp_ble_scan_type_t;

3.1.2 扫描过滤策略

在 ESP32 的 BLE 中,通过设置 scan_filter_policy 枚举类型来实现扫描过滤策略,此枚举类型中有以下 4 个值:

/// Ble scan filter type
typedef enum {
    BLE_SCAN_FILTER_ALLOW_ALL           = 0x0,  /*!< Accept all :
                                                  1. advertisement packets except directed advertising packets not addressed to this device (default). */
    BLE_SCAN_FILTER_ALLOW_ONLY_WLST     = 0x1,  /*!< Accept only :
                                                  1. advertisement packets from devices where the advertiser’s address is in the White list.
                                                  2. Directed advertising packets which are not addressed for this device shall be ignored. */
    BLE_SCAN_FILTER_ALLOW_UND_RPA_DIR   = 0x2,  /*!< Accept all :
                                                  1. undirected advertisement packets, and
                                                  2. directed advertising packets where the initiator address is a resolvable private address, and
                                                  3. directed advertising packets addressed to this device. */
    BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR = 0x3,  /*!< Accept all :
                                                  1. advertisement packets from devices where the advertiser’s address is in the White list, and
                                                  2. directed advertising packets where the initiator address is a resolvable private address, and
                                                  3. directed advertising packets addressed to this device.*/
} esp_ble_scan_filter_t;

BLE_SCAN_FILTER_ALLOW_ALL
接受所有的 :
除了不寻址到此设备的定向广告数据包之外的广告数据包(默认)。

BLE_SCAN_FILTER_ALLOW_ONLY_WLST
只接受:
1.来自广告商地址在白名单中的设备的广告数据包。
2.未针对该设备寻址的定向广告数据包应被忽略。

BLE_SCAN_FILTER_ALLOW_UND_RPA_DIR
接受所有的 :
1.无定向广告数据包
2.发起者地址是可解析的私有地址的定向广告数据包
3.定向到此设备的广告数据包。

BLE_SCAN_FILTER_ALLOW_WLIST_RPA_DIR
接受所有的 :
1.来自广告商地址在白名单中的设备的广告数据包
2.发起者地址是可解析的私有地址的定向广告数据包
3.定向到此设备的广告数据包。

3.1.3 扫描间隔

扫描间隔,控制器间隔多长时间扫描一次,也就是两个连续的扫描窗口开始时间的时间间隔。在 ESP32 上设置为 0x0004 and 0x4000 in 0.625ms units(2.5ms 到 10.24s)

3.1.4 扫描窗口

扫描窗口,每次扫描所持续的时间,在持续时间内,扫描设备一直在广播信道上运行。在 ESP32 上设置为 0x0004 and 0x4000 in 0.625ms units(2.5ms 到 10.24s)

注意:扫描窗口和扫描间隔两个参数非常重要。扫描窗口的设置要小于或等于扫描间隔,并且都要是 0.625ms 的整倍数。这两个参数决定了主机控制器的扫描占空比。 比如,如果设置扫描间隔为 100 ms,扫描窗口为 10ms ,那么主机控制器的扫描占空比就死 10%。特别注意可以捕获的定向数据包的最低占空比为 0.4%,即每一秒中扫描时间为 3.75ms,这些时间设置在任何蓝牙 4.x 处理器中都是一致的,不仅仅限于 NRF 处理器。

如果把时间间隔设置为相同的大小,那么控制器会进行连续扫描,每个间隔会改变扫描频率,也就死切换扫描信道。

3.1.5 扫描重复类型

/// Ble scan duplicate type
typedef enum {
    BLE_SCAN_DUPLICATE_DISABLE           = 0x0,  /*!< the Link Layer should generate advertising reports to the host for each packet received */
    BLE_SCAN_DUPLICATE_ENABLE            = 0x1,  /*!< the Link Layer should filter out duplicate advertising reports to the Host */
    BLE_SCAN_DUPLICATE_MAX               = 0x2,  /*!< 0x02 – 0xFF, Reserved for future use */
} esp_ble_scan_duplicate_t;
  • BLE_SCAN_DUPLICATE_DISABLE 链路层应为接收到的每个数据包生成向主机的广告报告
  • BLE_SCAN_DUPLICATE_ENABLE 链路层应过滤掉向主机发送的重复广告报告

3.2 开启扫描

一旦设置了扫描参数,就会触发 ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT 事件,该事件由GAP事件处理程序 esp_gap_cb() 处理。这个事件是用来开始扫描附近的GATT服务器

使用 esp_ble_gap_start_scanning 函数启动扫描,该函数接受一个表示连续扫描持续时间(以秒为单位)的参数。扫描期结束后,ESP_GAP_SEARCH_INQ_RES_EVT 事件将被触发。最后当 duration 超时时,会通过 ESP_GAP_SEARCH_INQ_CMPL_EVT 事件返回。

注意: 当 duration 值为 0 时,将会永久扫描而不产⽣超时。

3.3 获取扫描结果

扫描结果在到达 ESP_GAP_BLE_SCAN_RESULT_EVT 事件时立即显示,该事件包括以下参数:

struct ble_scan_result_evt_param {
        esp_gap_search_evt_t search_evt;            /*!< Search event type */
        esp_bd_addr_t bda;                          /*!< Bluetooth device address which has been searched */
        esp_bt_dev_type_t dev_type;                 /*!< Device type */
        esp_ble_addr_type_t ble_addr_type;          /*!< Ble device address type */
        esp_ble_evt_type_t ble_evt_type;            /*!< Ble scan result event type */
        int rssi;                                   /*!< Searched device's RSSI */
        uint8_t  ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX];     /*!< Received EIR */
        int flag;                                   /*!< Advertising data flag bit */
        int num_resps;                              /*!< Scan result number */
        uint8_t adv_data_len;                       /*!< Adv data length */
        uint8_t scan_rsp_len;                       /*!< Scan response length */
        uint32_t num_dis;                          /*!< The number of discard packets */
    } scan_rst;                                     /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */

该事件还包括如下所示的子事件列表:

/// Sub Event of ESP_GAP_BLE_SCAN_RESULT_EVT
typedef enum {
    ESP_GAP_SEARCH_INQ_RES_EVT             = 0,      /*!< Inquiry result for a peer device. */
    ESP_GAP_SEARCH_INQ_CMPL_EVT            = 1,      /*!< Inquiry complete. */
    ESP_GAP_SEARCH_DISC_RES_EVT            = 2,      /*!< Discovery result for a peer device. */
    ESP_GAP_SEARCH_DISC_BLE_RES_EVT        = 3,      /*!< Discovery result for BLE GATT based service on a peer device. */
    ESP_GAP_SEARCH_DISC_CMPL_EVT           = 4,      /*!< Discovery complete. */
    ESP_GAP_SEARCH_DI_DISC_CMPL_EVT        = 5,      /*!< Discovery complete. */
    ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT  = 6,      /*!< Search cancelled */
    ESP_GAP_SEARCH_INQ_DISCARD_NUM_EVT     = 7,      /*!< The number of pkt discarded by flow control */
} esp_gap_search_evt_t;

我们感兴趣的是 ESP_GAP_SEARCH_INQ_RES_EVT 事件,它在每次找到新设备时都会被调用。我们还对 ESP_GAP_SEARCH_INQ_CMPL_EVT 感兴趣,它在扫描完成时触发,可以用来重新启动扫描过程:

case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
        case ESP_GAP_SEARCH_INQ_RES_EVT:
            /* Search for BLE iBeacon Packet */
            if (esp_ble_is_ibeacon_packet(scan_result->scan_rst.ble_adv, scan_result->scan_rst.adv_data_len)){
                esp_ble_ibeacon_t *ibeacon_data = (esp_ble_ibeacon_t*)(scan_result->scan_rst.ble_adv);
                ESP_LOGI(DEMO_TAG, "----------iBeacon Found----------");
                esp_log_buffer_hex("IBEACON_DEMO: Device address:", scan_result->scan_rst.bda, ESP_BD_ADDR_LEN );
                esp_log_buffer_hex("IBEACON_DEMO: Proximity UUID:", ibeacon_data->ibeacon_vendor.proximity_uuid, ESP_UUID_LEN_128);

                uint16_t major = ENDIAN_CHANGE_U16(ibeacon_data->ibeacon_vendor.major);
                uint16_t minor = ENDIAN_CHANGE_U16(ibeacon_data->ibeacon_vendor.minor);
                ESP_LOGI(DEMO_TAG, "Major: 0x%04x (%d)", major, major);
                ESP_LOGI(DEMO_TAG, "Minor: 0x%04x (%d)", minor, minor);
                ESP_LOGI(DEMO_TAG, "Measured power (RSSI at a 1m distance):%d dbm", ibeacon_data->ibeacon_vendor.measured_power);
                ESP_LOGI(DEMO_TAG, "RSSI of packet:%d dbm", scan_result->scan_rst.rssi);
            }
            break;

3.4 提取广播内容

AD_Type:

/* relate to BTM_BLE_AD_TYPE_xxx in stack/btm_ble_api.h */
/// The type of advertising data(not adv_type)
typedef enum {
    ESP_BLE_AD_TYPE_FLAG                     = 0x01,    /* relate to BTM_BLE_AD_TYPE_FLAG in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_16SRV_PART               = 0x02,    /* relate to BTM_BLE_AD_TYPE_16SRV_PART in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_16SRV_CMPL               = 0x03,    /* relate to BTM_BLE_AD_TYPE_16SRV_CMPL in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_32SRV_PART               = 0x04,    /* relate to BTM_BLE_AD_TYPE_32SRV_PART in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_32SRV_CMPL               = 0x05,    /* relate to BTM_BLE_AD_TYPE_32SRV_CMPL in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_128SRV_PART              = 0x06,    /* relate to BTM_BLE_AD_TYPE_128SRV_PART in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_128SRV_CMPL              = 0x07,    /* relate to BTM_BLE_AD_TYPE_128SRV_CMPL in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_NAME_SHORT               = 0x08,    /* relate to BTM_BLE_AD_TYPE_NAME_SHORT in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_NAME_CMPL                = 0x09,    /* relate to BTM_BLE_AD_TYPE_NAME_CMPL in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_TX_PWR                   = 0x0A,    /* relate to BTM_BLE_AD_TYPE_TX_PWR in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_DEV_CLASS                = 0x0D,    /* relate to BTM_BLE_AD_TYPE_DEV_CLASS in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_SM_TK                    = 0x10,    /* relate to BTM_BLE_AD_TYPE_SM_TK in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_SM_OOB_FLAG              = 0x11,    /* relate to BTM_BLE_AD_TYPE_SM_OOB_FLAG in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_INT_RANGE                = 0x12,    /* relate to BTM_BLE_AD_TYPE_INT_RANGE in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_SOL_SRV_UUID             = 0x14,    /* relate to BTM_BLE_AD_TYPE_SOL_SRV_UUID in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_128SOL_SRV_UUID          = 0x15,    /* relate to BTM_BLE_AD_TYPE_128SOL_SRV_UUID in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_SERVICE_DATA             = 0x16,    /* relate to BTM_BLE_AD_TYPE_SERVICE_DATA in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_PUBLIC_TARGET            = 0x17,    /* relate to BTM_BLE_AD_TYPE_PUBLIC_TARGET in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_RANDOM_TARGET            = 0x18,    /* relate to BTM_BLE_AD_TYPE_RANDOM_TARGET in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_APPEARANCE               = 0x19,    /* relate to BTM_BLE_AD_TYPE_APPEARANCE in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_ADV_INT                  = 0x1A,    /* relate to BTM_BLE_AD_TYPE_ADV_INT in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_LE_DEV_ADDR              = 0x1b,    /* relate to BTM_BLE_AD_TYPE_LE_DEV_ADDR in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_LE_ROLE                  = 0x1c,    /* relate to BTM_BLE_AD_TYPE_LE_ROLE in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_SPAIR_C256               = 0x1d,    /* relate to BTM_BLE_AD_TYPE_SPAIR_C256 in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_SPAIR_R256               = 0x1e,    /* relate to BTM_BLE_AD_TYPE_SPAIR_R256 in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_32SOL_SRV_UUID           = 0x1f,    /* relate to BTM_BLE_AD_TYPE_32SOL_SRV_UUID in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_32SERVICE_DATA           = 0x20,    /* relate to BTM_BLE_AD_TYPE_32SERVICE_DATA in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_128SERVICE_DATA          = 0x21,    /* relate to BTM_BLE_AD_TYPE_128SERVICE_DATA in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_LE_SECURE_CONFIRM        = 0x22,    /* relate to BTM_BLE_AD_TYPE_LE_SECURE_CONFIRM in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_LE_SECURE_RANDOM         = 0x23,    /* relate to BTM_BLE_AD_TYPE_LE_SECURE_RANDOM in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_URI                      = 0x24,    /* relate to BTM_BLE_AD_TYPE_URI in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_INDOOR_POSITION          = 0x25,    /* relate to BTM_BLE_AD_TYPE_INDOOR_POSITION in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_TRANS_DISC_DATA          = 0x26,    /* relate to BTM_BLE_AD_TYPE_TRANS_DISC_DATA in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_LE_SUPPORT_FEATURE       = 0x27,    /* relate to BTM_BLE_AD_TYPE_LE_SUPPORT_FEATURE in stack/btm_ble_api.h */
    ESP_BLE_AD_TYPE_CHAN_MAP_UPDATE          = 0x28,    /* relate to BTM_BLE_AD_TYPE_CHAN_MAP_UPDATE in stack/btm_ble_api.h */
    ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE    = 0xFF,    /* relate to BTM_BLE_AD_MANUFACTURER_SPECIFIC_TYPE in stack/btm_ble_api.h */
} esp_ble_adv_data_type;

3.4.1 提取名字

按照 AD_Type: ESP_BLE_AD_TYPE_NAME_CMPL 提取名字,
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);

uint8_t *adv_name;
uint8_t adv_name_len;

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
  esp_err_t err;
  switch (event) {
  case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
        case ESP_GAP_SEARCH_INQ_RES_EVT:
            adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
            ESP_LOGI(DEMO_TAG, "searched Device Name Len %d", adv_name_len);
            esp_log_buffer_char(DEMO_TAG, (char *)adv_name, adv_name_len);
            break;
        default:
            break;
        }
        break;
  }
}

3.4.2 提取UUID

按照 AD_Type: ESP_BLE_AD_TYPE_16SRV_PART 提取部分UUID,
part_uuid = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_16SRV_PART, &part_uuid_len);

按照 AD_Type: ESP_BLE_AD_TYPE_16SRV_CMPL 提取全部UUID,
cmpl_uuid = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_16SRV_CMPL, &cmpl_uuid_len);

uint8_t *part_uuid;
uint8_t part_uuid_len;
uint8_t *cmpl_uuid;
uint8_t cmpl_uuid_len;

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
  esp_err_t err;
  switch (event) {
  case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
        case ESP_GAP_SEARCH_INQ_RES_EVT:
            part_uuid = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_16SRV_PART, &part_uuid_len);
            if(part_uuid_len != 0)
            {
                esp_log_buffer_hex("searched part uuid", part_uuid, part_uuid_len);
            } 
            cmpl_uuid = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_16SRV_CMPL, &cmpl_uuid_len);
            if(cmpl_uuid_len != 0)
            {
                esp_log_buffer_hex("searched cmpl uuid", cmpl_uuid, cmpl_uuid_len);
            }
            break;
        default:
            break;
        }
        break;
  }
}

3.4.3 提取自定义数据

按照 AD_Type: ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE 提取部分自定义数据,
user_data = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE, &user_data_len);

uint8_t *user_data;
uint8_t user_data_len;

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
  esp_err_t err;
  switch (event) {
  case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
        case ESP_GAP_SEARCH_INQ_RES_EVT:
            user_data = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE, &user_data_len);
            if(user_data_len != 0)
            {
                esp_log_buffer_hex("searched user data:", user_data, user_data_len);
            }
            break;
        default:
            break;
        }
        break;
  }
}

3.5 实例

根据 esp-idf\\examples\\bluetooth\\bluedroid\\ble\\ble_ibeacon 中的例程修改

#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "nvs_flash.h"

#include "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_gattc_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_defs.h"
#include "esp_ibeacon_api.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"

static const char* DEMO_TAG = "IBEACON_DEMO";

///Declare static functions
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);

static esp_ble_scan_params_t ble_scan_params = {
    .scan_type              = BLE_SCAN_TYPE_ACTIVE,
    .own_addr_type          = BLE_ADDR_TYPE_PUBLIC,
    .scan_filter_policy     = BLE_SCAN_FILTER_ALLOW_ALL,
    .scan_interval          = 0x50,
    .scan_window            = 0x30,
    .scan_duplicate         = BLE_SCAN_DUPLICATE_DISABLE
};

uint8_t *adv_name;
uint8_t adv_name_len;
uint8_t *part_uuid;
uint8_t part_uuid_len;
uint8_t *cmpl_uuid;
uint8_t cmpl_uuid_len;
uint8_t *user_data;
uint8_t user_data_len;

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    esp_err_t err;

    switch (event) {
    case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
        //the unit of the duration is second, 0 means scan permanently
        uint32_t duration = 0;
        esp_ble_gap_start_scanning(duration);
        break;
    }
    case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
        //scan start complete event to indicate scan start successfully or failed
        if ((err = param->scan_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) {
            ESP_LOGE(DEMO_TAG, "Scan start failed: %s", esp_err_to_name(err));
        }
        break;
    case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
        case ESP_GAP_SEARCH_INQ_RES_EVT:
            ESP_LOGI(DEMO_TAG, "----------Device Found----------");
            esp_log_buffer_hex("Device address:", scan_result->scan_rst.bda, ESP_BD_ADDR_LEN );
            ESP_LOGI(DEMO_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
            esp_log_buffer_hex("searched Adv Data:", scan_result->scan_rst.ble_adv, scan_result->scan_rst.adv_data_len + scan_result->scan_rst.scan_rsp_len);
            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(DEMO_TAG, "searched Device Name Len %d", adv_name_len);
                esp_log_buffer_char(DEMO_TAG, (char *以上是关于ESP32学习笔记(27)——BLE GAP主机端扫描的主要内容,如果未能解决你的问题,请参考以下文章

ESP32学习笔记(26)——BLE GAP从机端广播

ESP32学习笔记(28)——BLE GAP从机端广播自定义数据

ESP32学习笔记(30)——BLE GATT服务端自定义服务和特征

ESP32学习笔记(33)——BLE GATT客户端发现服务和读写特征值

ESP32学习笔记(34)——BLE一主多从连接

nrf52832 学习笔记蓝牙主从机连接和连接参数更新