一、背景
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.h 和 bt/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 *)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 module
if ((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 日