一、背景

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, and2. directed advertising packets where the initiator address is a resolvable private address, and3. 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, and2. directed advertising packets where the initiator address is a resolvable private address, and3. 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 permanentlyuint32_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 failedif ((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 *)adv_name, adv_name_len);}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);}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);}ESP_LOGI(DEMO_TAG, "RSSI of packet:%d dbm", scan_result->scan_rst.rssi);break;default:break;}break;}case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:if ((err = param->scan_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS){ESP_LOGE(DEMO_TAG, "Scan stop failed: %s", esp_err_to_name(err));}else {ESP_LOGI(DEMO_TAG, "Stop scan successfully");}break;default:break;}
}void ble_ibeacon_appRegister(void)
{esp_err_t status;ESP_LOGI(DEMO_TAG, "register callback");//register the scan callback function to the gap moduleif ((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK) {ESP_LOGE(DEMO_TAG, "gap register error: %s", esp_err_to_name(status));return;}}void ble_ibeacon_init(void)
{esp_bluedroid_init();esp_bluedroid_enable();ble_ibeacon_appRegister();
}void app_main(void)
{ESP_ERROR_CHECK(nvs_flash_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_BLE);ble_ibeacon_init();/* set scan parameters */esp_ble_gap_set_scan_params(&ble_scan_params);
}

查看打印:


• 由 Leung 写于 2021 年 6 月 22 日

• 参考:ESPIDF开发ESP32学习笔记【经典蓝牙与BLE】
    ESP32蓝牙的Gatt Client的例子演练

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

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

    一.简介 1.1 低功耗蓝牙(BLE)协议栈 链路层(LL) 控制设备的射频状态,有五个设备状态:待机.广播.扫描.初始化和连接. 广播 为广播数据包,而 扫描 则是监听广播. GAP通信中角色,中心 ...

  2. 关于esp32蓝牙模块的使用——esp32学习笔记

    关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 零.前言 一.经典蓝牙BT 二.低功耗蓝牙B ...

  3. ESP32学习笔记(20)——SPI(从机)接口使用

    一.SPI简介 SPI(Serial Peripheral Interface) 协议是由摩托罗拉公司提出的通讯协议,即串行外围设备接口,是一种高速全双工的通信总线.它被广泛地使用在 ADC.LCD ...

  4. ESP32学习笔记( VSCode + ESP-IDF环境) 3 ——GPIO相关的简单外设驱动

    1.如何在VSCode和ESP-IDF的环境下创建工程 说实话,这是我用ESP-IDF在VSCode环境下最不喜欢的事情,在一顿CSDN和百度之后,很多大佬博主都推荐使用VSCode,通过官方示例来进 ...

  5. ESP32学习笔记(一) 芯片型号介绍

    ESP32学习笔记(一) 芯片型号介绍 目录: ESP32学习笔记(一) 芯片型号介绍 ESP32学习笔记(二) 开发环境搭建 VSCode+platformio ESP32学习笔记(三) 硬件资源介 ...

  6. ESP32学习笔记(19)——SPI(主机)接口使用

    一.SPI简介 SPI(Serial Peripheral Interface) 协议是由摩托罗拉公司提出的通讯协议,即串行外围设备接口,是一种高速全双工的通信总线.它被广泛地使用在 ADC.LCD ...

  7. ESP32学习笔记(51)——搭建环境、编译烧写(Windows+Espressif-IDE)

    VS Code 环境搭建参看 ESP32学习笔记(1)--搭建环境.编译烧写(Windows+VS Code) 一.搭建环境 1.1 官方资料 ESP-IDF 编程指南 1.2 下载Espressif ...

  8. [ESP32]学习笔记03

    今天我们使用ESP32自带的LEDC外设制作一个LED呼吸灯 目录 前言 一.呼吸灯是什么 二.首先我们在Blink实例的基础上建立工程 1.引入库添加宏定义 2.修改主函数 最后我们看一下呼吸灯的效 ...

  9. ESP32学习笔记(1)——搭建环境、编译烧写(Windows+VS Code)

    Espressif-IDE 环境搭建参看 ESP32学习笔记(50)--搭建环境.编译烧写(Windows+Espressif-IDE) 一.搭建环境 1.1 官方资料 ESP-IDF 编程指南 1. ...

最新文章

  1. finalshell文件列表不显示_Jira面板配置_待办事项不显示问题列表
  2. Android事件机制:事件传递和消费
  3. 大型网站核心架构要素--可用性
  4. SVM-支持向量机原理详解与实践之三
  5. SAP Spartacus部署到SAP Commerce Cloud,不同的系统设置不同的OCC Base url
  6. 89. Gray Code - LeetCode
  7. Qt图形界面编程入门(Qt的历史、Qt安装资源链接、Qt Creator简介)
  8. Day 4-7 -configparser模块
  9. Linux查看和注销用户
  10. 解决了!联邦学习+推荐系统,冷启动和数据隐私问题双双K.O
  11. PhotoShop 2022安装教程(附安装包)
  12. crm系统需要的服务器,灵当CRM管理系统运行环境-CRM服务器配置
  13. 施密特正交化过程编程c语言,利用C程序编写格拉姆-施密特正交化的过程.docx
  14. 零基础小白,如何从零开始搭建网站?(详细步骤)
  15. 计算机系统概述学后感,计算机操作系统学习心得体会总结(2)
  16. 北师大1903计算机在线答案,[南开大学(本部)]20秋学期(1709、1803、1809、1903、1909、2003、2009 )《程序设计基础(下)》在线作业-2...
  17. 如何安装flash_如何安装
  18. word中的表格怎么按照章节自动插入题注(即表头的编号)
  19. 个人网站的设计与实现
  20. 采购里的“一分钱一分货”,确定你没被套路?

热门文章

  1. 一文搞懂移动设备ID的那些事儿
  2. 什么是软件模块化,为什么要模块化?
  3. 你都用 Python 来做什么 学Python能做什么
  4. 调用“elm_kernel_adda>kernel_matrix3“时,未对输出参数“omega“ (可能还包括其他参数)赋值。
  5. 【LSTM时间序列预测】基于matlab鲸鱼算法优化LSTM时间序列预测【含Matlab源码 105期】
  6. linux限制ssh 无公网ip白名单限制 基于网段
  7. 在一个老外微信PM的眼中,中国移动App UI那些事儿
  8. shell之 ps、kill、killall命令详解
  9. 前端开发如何配置一个新电脑 VScode
  10. 弱类型语言与强语言类型