菜鸟哥玩蓝牙Ble4.0系列 ESP32初玩篇① Scan —— 扫描周边蓝牙,了解BLE扫描的方方面面

授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

1、前言

为了不想一下子就讲解Ble协议,我们还是先找点成就感吧,先玩玩ESP32 提供的demo。然后在demo中慢慢去学习ble的技术点,等到后面原理篇讲解完再次回来看初玩篇,应该会有一种豁然开朗的感觉。

再次来回忆一下Arduino ESP32 Ble核心内容:
在这里插入图片描述

  • 从设备角度来看,统一叫做 BLEDevice
  • 从CS角度来看,可能又会分为 客户端(BLEClient)和 服务端(BLEServer
  • 从状态角度来看,又会涉及广播状态(BLEAdvertisedDeviceBLEAdvertising)和 扫描状态(BLEScan

记住一点,各个角度之间不是独立的。

其中涉及到我们本篇需要讲解的BLEScan

接下来,这个蓝牙Ble协议图需要时刻记住(我们写的任何代码都离不开协议):

在这里插入图片描述

在蓝牙中,底层物理层(PHY)信道分为:

  • 数据信道(Data Channel)
  • 广播信道(Advertising Channel)

在物理层之上也就是链路层又定义了蓝牙的五种状态:
在这里插入图片描述

  • 处于就绪态(Standby)时,链路层不收发报文,默认是此状态。

  • 处于广播态(Advertising)的链路层可以发送报文广播报文,也可以监听以及相应这些广播报文触发的响应报文。可被发现(也就是可以被扫描到)或者可被连接(后面进行数据通信)的设备必须处于广播态。需要向一定范围内的其他蓝牙设备广播数据的设备也必须处于广播态。

  • 处于扫描态(Scanning)的设备能够接收广播报文——也就是本篇的Scan。扫描又分为两种,主动扫描和被动扫描。主动扫描可以发送扫描请求给广播态设备,并获取额外的扫描响应数据,而被动扫描仅仅是接收广播报文。

  • 处于初始态(Initiating)的设备可以发起连接请求(有连接才会有数据通信链路),然后进入到连接态(Connection)

  • 处于连接态(Connection)下的设备才是我们经常看到的数据传输状态。并且又会区分为主设备(Master)从设备(Slaver)。谁主动发起连接,谁就是主设备。

2、ESP32 Scan案例

2.1 直接打开官方示例

  • 选择目标板子(ESP32 Dev Module
    在这里插入图片描述

  • 选择目标示例(ESP32 Ble Arduino -》BLE_Scan
    在这里插入图片描述
    源码注释:

/*
   Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
   Ported to Arduino ESP32 by Evandro Copercini
*/

#include <BLEDevice.h> // 蓝牙Ble设备库
#include <BLEUtils.h> 
#include <BLEScan.h> // 蓝牙ble设备的扫描功能库 本篇重点
#include <BLEAdvertisedDevice.h> // 扫描到的蓝牙设备(广播状态)

int scanTime = 5; //In seconds
BLEScan* pBLEScan; // 扫描对象

// 扫描广播设备结果回调
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    
    
    void onResult(BLEAdvertisedDevice advertisedDevice) {
    
    
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
    }
};

void setup() {
    
    
  Serial.begin(115200);
  Serial.println("Scanning...");

  BLEDevice::init(""); // 设备初始化
  pBLEScan = BLEDevice::getScan(); // 创建一个新扫描入口
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());// 注册扫描结果回调
  pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster // 配置主动扫描
  pBLEScan->setInterval(100); // 配置扫描PDU间隔
  pBLEScan->setWindow(99);  // less or equal setInterval value // 设置扫描窗口大小,需要小于扫描间隔
}

void loop() {
    
    
  // put your main code here, to run repeatedly:
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);// 开始扫描 等待扫描结果
  Serial.print("Devices found: ");
  Serial.println(foundDevices.getCount());
  Serial.println("Scan done!");
  pBLEScan->clearResults();   // delete results fromBLEScan buffer to release memory,释放扫描缓存消耗
  delay(2000);
}
  • 编译成功,直接烧录到ESP32,串口观察结果
    在这里插入图片描述
    打印出了空的设备名字,蓝牙设备地址。

3、Scan核心库 —— BLEScan

查看源码最好的入口就是看头文件(BLEScan)

/*
 * BLEScan.h
 *
 *  Created on: Jul 1, 2017
 *      Author: kolban
 */

#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_
#define COMPONENTS_CPP_UTILS_BLESCAN_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gap_ble_api.h> // gap管理广播连接等等

// #include <vector>
#include <string>
#include "BLEAdvertisedDevice.h" // 定义为一个广播设备
#include "BLEClient.h"  // 客户端
#include "FreeRTOS.h"

class BLEAdvertisedDevice;
class BLEAdvertisedDeviceCallbacks;
class BLEClient;
class BLEScan;


/**
 * @brief The result of having performed a scan.
 * When a scan completes, we have a set of found devices.  Each device is described
 * by a BLEAdvertisedDevice object.  The number of items in the set is given by
 * getCount().  We can retrieve a device by calling getDevice() passing in the
 * index (starting at 0) of the desired device.
 * (大概意思就是这里保存扫描结果。当扫描完成,我们会得到一系列发现的设备信息。每一个设备会被定义位一个 BLEAdvertisedDevice 对象。)
 */
class BLEScanResults {
    
    
public:
	void                dump();
	int                 getCount();
	BLEAdvertisedDevice getDevice(uint32_t i);

private:
	friend BLEScan;
	std::map<std::string, BLEAdvertisedDevice*> m_vectorAdvertisedDevices;  // 设备映射  设备地址-》设备
};

/**
 * @brief Perform and manage %BLE scans.
 *
 * Scanning is associated with a %BLE client that is attempting to locate BLE servers.
 */
class BLEScan {
    
    
public:
    // 扫描方式
	void           setActiveScan(bool active);
	// 扫描回调
	void           setAdvertisedDeviceCallbacks(
			              BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
										bool wantDuplicates = false,
										bool shouldParse = true);
	// 扫描间隔									  
	void           setInterval(uint16_t intervalMSecs);
	// 扫描窗口		
	void           setWindow(uint16_t windowMSecs);
	// 启动扫描	异步
	bool           start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false);
	// 启动扫描	同步
	BLEScanResults start(uint32_t duration, bool is_continue = false);
	// 停止扫描
	void           stop();
	// 清除具体蓝牙地址扫描结果
	void 		   erase(BLEAddress address);
	// 获取全部扫描结果
	BLEScanResults getResults();
	// 清除全部扫描结果
	void			clearResults();

private:
	BLEScan();   // One doesn't create a new instance instead one asks the BLEDevice for the singleton.
	friend class BLEDevice; // 这个东西非常重要,每个设备都是一个抽象的BLEDevice,里面会处理很多通用事件
	// 处理GAP事件
	void         handleGAPEvent(
		esp_gap_ble_cb_event_t  event,
		esp_ble_gap_cb_param_t* param);
	// 扫描内容解析	
	void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload);

    // 扫描参数
	esp_ble_scan_params_t         m_scan_params;
	// 扫描回调
	BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
	// 标识扫描状态
	bool                          m_stopped = true;
	// 标识扫描内容解析
	bool                          m_shouldParse = true;
	FreeRTOS::Semaphore           m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd");
	// 扫描结果
	BLEScanResults                m_scanResults;
	// 扫描去重
	bool                          m_wantDuplicates;
	void                        (*m_scanCompleteCB)(BLEScanResults scanResults);
}; // BLEScan

#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */

博哥整理了百度脑图:
在这里插入图片描述
这里主要分为两类:

  • 管理扫描(主要是管理扫描整个扫描过程,包括启动、获取结果、停止等)
  • 配置扫描(主要是配置如何进行扫描)

3.1 管理扫描

3.1.1 创建扫描对象 —— BLEScan

函数说明

/**
 * 默认构造函数
 */
BLEScan::BLEScan() {
    
    
	memset(&m_scan_params, 0, sizeof(m_scan_params)); // 初始化所有的扫描参数
	m_scan_params.scan_type          = BLE_SCAN_TYPE_PASSIVE; // 主动扫描
	m_scan_params.own_addr_type      = BLE_ADDR_TYPE_PUBLIC; // 公共蓝牙Ble地址
	m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;// 扫描过滤策略
	m_scan_params.scan_duplicate     = BLE_SCAN_DUPLICATE_DISABLE;
	m_pAdvertisedDeviceCallbacks     = nullptr;// 扫描结果回调
	m_stopped                        = true;
	m_wantDuplicates                 = false;
	m_shouldParse                    = true;
	setInterval(100); // 设置扫描间隔
	setWindow(100); // 设置扫描窗口大小
} // BLEScan
  • m_scan_params(esp_ble_scan_params_t)扫描参数
  • m_pAdvertisedDeviceCallbacks 扫描结果回调,通过它我们才知道到底扫描到什么设备
  • m_wantDuplicates 是否去重设备
  • m_shouldParse 是否解析广播数据
  • m_stopped 表示扫描状态(启动或者停止)
3.1.1.1 详解扫描参数 esp_ble_scan_params_t

结构体定义

/// Ble scan parameters
typedef struct {
    
    
    esp_ble_scan_type_t     scan_type;              /*!< Scan type 扫描类型 */
    esp_ble_addr_type_t     own_addr_type;          /*!< Owner address type 地址类型*/
    esp_ble_scan_filter_t   scan_filter_policy;     /*!< Scan filter policy 扫描过滤策略*/
    uint16_t                scan_interval;          /*!< Scan interval. This is defined as the time interval from
                                                      when the Controller started its last LE scan until it begins the subsequent LE scan.
                                                      Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms)
                                                      Time = N * 0.625 msec
                                                      Time Range: 2.5 msec to 10.24 seconds*/
    uint16_t                scan_window;            /*!< Scan window. The duration of the LE scan. LE_Scan_Window
                                                      shall be less than or equal to LE_Scan_Interval
                                                      Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms)
                                                      Time = N * 0.625 msec
                                                      Time Range: 2.5 msec to 10240 msec 扫描窗口大小*/
    esp_ble_scan_duplicate_t  scan_duplicate;       /*!< The Scan_Duplicates parameter controls whether the Link Layer should filter out
                                                        duplicate advertising reports (BLE_SCAN_DUPLICATE_ENABLE) to the Host, or if the Link Layer should generate
                                                        advertising reports for each packet received 设置链接层是否过滤重复的扫描报告*/
} esp_ble_scan_params_t;
  • scan_type扫描类型,主要是通过是否要求广播方发送更多数据
/// Ble scan type
typedef enum {
    
    
    // 主动扫描,主动扫描的设备会发送ScanReq给到广播设备要求更多数据
    BLE_SCAN_TYPE_PASSIVE   =   0x0,            /*!< Passive scan */
    // 被动扫描,仅仅接收广播,不会去主动要求更多数据
    BLE_SCAN_TYPE_ACTIVE    =   0x1,            /*!< Active scan */
} esp_ble_scan_type_t;
  • own_addr_type 扫描地址类型,蓝牙地址分为随机地址和公有地址
/// BLE device address type
typedef enum {
    
    
    // 公有地址,一般是芯片硬件MAC地址
    BLE_ADDR_TYPE_PUBLIC        = 0x00,
    // 随机地址,通过软件随机生成的一个地址,安全性更高
    BLE_ADDR_TYPE_RANDOM        = 0x01,
    BLE_ADDR_TYPE_RPA_PUBLIC    = 0x02,
    BLE_ADDR_TYPE_RPA_RANDOM    = 0x03,
} esp_ble_addr_type_t;
  • scan_filter_policy 扫描过滤策略,主要是用来过滤扫描结果(说白就是哪些地址需要被忽略,如何处理(去或者留)广播数据包)
/// Ble scan filter type
typedef enum {
    
    
    // 1.除了不包含扫描者地址的可连接定向广播数据包之外,其他广播数据包都会处理(默认情况)
    BLE_SCAN_FILTER_ALLOW_ALL           = 0x0,  /*!< Accept all :
                                                  1. advertisement packets except directed advertising packets not addressed to this device (default). */
                                                  
    // 1.只处理在扫描地址白名单里面存在对应地址的广播数据包
    // 2.或者发给自己的可连接定向广播数据包
    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. */
    
    // 1、处理所有非定向广播数据包
    // 2、处理指向当前扫描者地址的可连接定向广播数据包
    // 3、处理初始者地址为可解析私有地址的可连接定向广播数据包
    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. */
                                                  
    // 1、只处理设备地址在扫描白名单里面的广播数据包
    // 2、处理初始者地址为可解析私有地址的可连接定向广播数据包
    // 3、处理指向当前扫描者地址的可连接定向广播数据包                                               
    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;

扫描过滤策略定义了扫描者如何处理广播数据包。
这里需要关注 WhiteList(设备地址白名单),这个非常有用
从上到下,扫描过滤策略越来越严格,所以过滤更加精准化

  • scan_window 扫描窗口大小
    扫描事件在一个周期内的时间大小,表示的是在三个广播信道(37/38/39)上一个周期运行的时间。
    在这里插入图片描述

  • scan_interval 扫描间隔大小
    表示两个扫描窗口(扫描事件)开始时间点的时间间隔。所以如果 scan_window等于scan_interval,表示连续扫描。

  • scan_duplicate 设置链接层是否过滤重复地址的扫描报告

/// 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;

为什么会有这个设置?

正常情况下,对于能通过扫描过滤策略的广播数据包,链接层都会生成扫描报告给到主机层。那么有可能多个扫描周期内扫描到同一个设备地址,这是就需要考虑要不要在底层直接过滤重复。建议打开。

3.1.1.2 详解扫描结果回调 m_pAdvertisedDeviceCallbacks

源码说明

/**
 * @brief A callback handler for callbacks associated device scanning.
 *
 * When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising
 * has been found.  This class can be sub-classed and registered such that when a scan is performed and
 * a new advertised device has been found, we will be called back to be notified.
 */
class BLEAdvertisedDeviceCallbacks {
    
    
public:
	virtual ~BLEAdvertisedDeviceCallbacks() {
    
    }
	/**
	 * @brief Called when a new scan result is detected.
	 *
	 * As we are scanning, we will find new devices.  When found, this call back is invoked with a reference to the
	 * device that was found.  During any individual scan, a device will only be detected one time.
	 */
	virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0;
};

这是一个抽象的扫描结果处理类。一般情况下我们会继承这个类去实现它定义的方法 OnResult(这里会返回扫描到蓝牙广播设备 BLEAdvertisedDevice)。

具体案例

/**
 * Scan for BLE servers and find the first one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    
    
 /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    
    
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());

    // We have found a device, let us now see if it contains the service we are looking for.(判断一下有没有服务UUID以及是否是我们要找的服务UUID)
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
    
    

      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = true;

    } // Found our server
  } // onResult
}; // MyAdvertisedDeviceCallbacks

那么我们就需要关注一下 BLEAdvertisedDevice(蓝牙广播设备) 到底能给我们带来什么信息?
(非本章重点内容,先留个悬念,后续篇章再详细讲解)

3.1.2 启动扫描 —— start

函数说明

/**
 * @brief 启动扫描(异步扫描)
 * @param duration 扫描时间,s为单位
 * @param scanCompleteCB 扫描完成结束回调
 * @param is_continue 是否清除扫描设备映射,false表示清除
 * @return bool 启动状态,成功或者失败
 */
bool  start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false);

/**
 * @brief 启动扫描(同步扫描)
 * @param duration 扫描时间,s为单位
 * @param scanCompleteCB 扫描完成结束回调
 * @param is_continue 是否清除扫描设备映射,false表示清除
 * @return BLEScanResults 扫描结果
 */
BLEScanResults start(uint32_t duration, bool is_continue = false);
  • void (*scanCompleteCB)(BLEScanResults) 外部定义的一个扫描完成回调方法,会有一个 BLEScanResults 结果
/**
 * @brief The result of having performed a scan.
 * When a scan completes, we have a set of found devices.  Each device is described
 * by a BLEAdvertisedDevice object.  The number of items in the set is given by
 * getCount().  We can retrieve a device by calling getDevice() passing in the
 * index (starting at 0) of the desired device.
 */
class BLEScanResults {
    
    
public:
	void                dump();
	// 扫描到的设备数量
	int                 getCount();
	// 通过索引i去取出数组中具体一个Ble广播设备信息,与getCount一起用。
	BLEAdvertisedDevice getDevice(uint32_t i);

private:
	friend BLEScan;
	// 设备地址与Ble广播设备的映射关系
	std::map<std::string, BLEAdvertisedDevice*> m_vectorAdvertisedDevices;
};

/**
 * @brief Dump the scan results to the log.
 * 打印日志结果,方便调试
 */
void BLEScanResults::dump() {
    
    
	log_v(">> Dump scan results:");
	for (int i=0; i<getCount(); i++) {
    
    
		log_d("- %s", getDevice(i).toString().c_str());
	}
} // dump


/**
 * @brief Return the count of devices found in the last scan.
 *        获取最近一次扫描的数量
 * @return The number of devices found in the last scan.
 */
int BLEScanResults::getCount() {
    
    
	return m_vectorAdvertisedDevices.size();
} // getCount


/**
 * @brief Return the specified device at the given index.
 * The index should be between 0 and getCount()-1.
 * @param [in] i The index of the device.
 * @return The device at the specified index.
 */
BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t i) {
    
    
	uint32_t x = 0;
	BLEAdvertisedDevice dev = *m_vectorAdvertisedDevices.begin()->second;
	for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) {
    
    
		dev = *it->second;
		if (x==i)	break;
		x++;
	}
	return dev;
}

主要关注 m_vectorAdvertisedDevices 这个map结构(关于C++ map操作 可以参考 C++ Map常见用法说明 )。

源码说明

/**
 * @brief Start scanning.
 * @param [in] duration The duration in seconds for which to scan.
 * @param [in] scanCompleteCB A function to be called when scanning has completed.
 * @param [in] are we continue scan (true) or we want to clear stored devices (false)
 * @return True if scan started or false if there was an error.
 */
bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) {
    
    
	log_v(">> start(duration=%d)", duration);

	m_semaphoreScanEnd.take(std::string("start"));
	// 扫描回调
	m_scanCompleteCB = scanCompleteCB;                  // Save the callback to be invoked when the scan completes.

	//  if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
	//  then we should not clear map or we will connect the same device few times
	if(!is_continue) {
    
      
	    // 清除缓存结果
		for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){
    
    
			delete _dev.second;
		}
		m_scanResults.m_vectorAdvertisedDevices.clear();
	}
    // esp_ble_gap_set_scan_params(sdk方法) 设置扫描参数
	esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params);

	if (errRc != ESP_OK) {
    
    
		log_e("esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
		m_semaphoreScanEnd.give();
		return false;
	}

    // 开始扫描 esp_ble_gap_start_scanning(sdk方法)
	errRc = ::esp_ble_gap_start_scanning(duration);

	if (errRc != ESP_OK) {
    
    
		log_e("esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
		m_semaphoreScanEnd.give();
		return false;
	}

	m_stopped = false;

	log_v("<< start()");
	return true;
} // start


/**
 * @brief Start scanning and block until scanning has been completed.
 * @param [in] duration The duration in seconds for which to scan.
 * @return The BLEScanResults.
 */
BLEScanResults BLEScan::start(uint32_t duration, bool is_continue) {
    
    
    // 调用了上面的方法,如果是true,就继续等待
	if(start(duration, nullptr, is_continue)) {
    
    
		m_semaphoreScanEnd.wait("start");   // Wait for the semaphore to release.
	}
	return m_scanResults;
} 
  • 是不是很懵逼?设置完之后好像也没做什么,那么哪里做了回调呢?(提示一下,需要时刻记住我们的设备无时无刻都是一个BLEDevice,Scan只是BLEDevice里面的一个子功能(这个概念非常重要),BLEDevice init方法,我们会在里面注入GAP回调)
/**
 * @brief Handle GAP events.
 */
/* STATIC */ void BLEDevice::gapEventHandler(
	esp_gap_ble_cb_event_t event,
	esp_ble_gap_cb_param_t *param) {
    
    

	BLEUtils::dumpGapEvent(event, param);

	switch(event) {
    
    

		 case ESP_GAP_BLE_OOB_REQ_EVT:                                /* OOB request event */
			 log_i("ESP_GAP_BLE_OOB_REQ_EVT");
			 break;
		 case ESP_GAP_BLE_LOCAL_IR_EVT:                               /* BLE local IR event */
			 log_i("ESP_GAP_BLE_LOCAL_IR_EVT");
			 break;
		 case ESP_GAP_BLE_LOCAL_ER_EVT:                               /* BLE local ER event */
			 log_i("ESP_GAP_BLE_LOCAL_ER_EVT");
			 break;
		 case ESP_GAP_BLE_NC_REQ_EVT:								/*  NUMERIC CONFIRMATION  */
			 log_i("ESP_GAP_BLE_NC_REQ_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE   // Check that BLE SMP (security) is configured in make menuconfig
			if(BLEDevice::m_securityCallbacks != nullptr){
    
    
			 	 esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey));
			}
#endif	// CONFIG_BLE_SMP_ENABLE
			 break;
		 case ESP_GAP_BLE_PASSKEY_REQ_EVT:                           /* passkey request event */
			log_i("ESP_GAP_BLE_PASSKEY_REQ_EVT: ");
			// esp_log_buffer_hex(m_remote_bda, sizeof(m_remote_bda));
#ifdef CONFIG_BLE_SMP_ENABLE   // Check that BLE SMP (security) is configured in make menuconfig
			if(BLEDevice::m_securityCallbacks != nullptr){
    
    
				esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest());
			}
#endif	// CONFIG_BLE_SMP_ENABLE
			break;
			/*
			 * TODO should we add white/black list comparison?
			 */
		 case ESP_GAP_BLE_SEC_REQ_EVT:
			 /* send the positive(true) security response to the peer device to accept the security request.
			 If not accept the security request, should sent the security response with negative(false) accept value*/
			 log_i("ESP_GAP_BLE_SEC_REQ_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE   // Check that BLE SMP (security) is configured in make menuconfig
			if(BLEDevice::m_securityCallbacks!=nullptr){
    
    
				esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest());
			}
			else{
    
    
				esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
			}
#endif	// CONFIG_BLE_SMP_ENABLE
			break;
			 /*
			  *
			  */
		 case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:  //the app will receive this evt when the IO  has Output capability and the peer device IO has Input capability.
			 //display the passkey number to the user to input it in the peer deivce within 30 seconds
			 log_i("ESP_GAP_BLE_PASSKEY_NOTIF_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE   // Check that BLE SMP (security) is configured in make menuconfig
			log_i("passKey = %d", param->ble_security.key_notif.passkey);
			if(BLEDevice::m_securityCallbacks!=nullptr){
    
    
				BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey);
			}
#endif	// CONFIG_BLE_SMP_ENABLE
			 break;
		 case ESP_GAP_BLE_KEY_EVT:
			 //shows the ble key type info share with peer device to the user.
			 log_d("ESP_GAP_BLE_KEY_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE   // Check that BLE SMP (security) is configured in make menuconfig
			 log_i("key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type));
#endif	// CONFIG_BLE_SMP_ENABLE
			 break;
		 case ESP_GAP_BLE_AUTH_CMPL_EVT:
			 log_i("ESP_GAP_BLE_AUTH_CMPL_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE   // Check that BLE SMP (security) is configured in make menuconfig
			 if(BLEDevice::m_securityCallbacks != nullptr){
    
    
				 BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl);
			 }
#endif	// CONFIG_BLE_SMP_ENABLE
			 break;
		default: {
    
    
			break;
		}
	} // switch

	if (BLEDevice::m_pClient != nullptr) {
    
    
		BLEDevice::m_pClient->handleGAPEvent(event, param);
	}
    // 关注这一行,是不是感觉有点意思了??????
	if (BLEDevice::m_pScan != nullptr) {
    
    
		BLEDevice::getScan()->handleGAPEvent(event, param);
	}

	if(m_bleAdvertising != nullptr) {
    
    
		BLEDevice::getAdvertising()->handleGAPEvent(event, param);
	}

	if(m_customGapHandler != nullptr) {
    
    
		BLEDevice::m_customGapHandler(event, param);
	}

}

看到这里,请重新回去看看 2.1 是不是有点突然豁然开朗的感觉。
接下来我们只需要关注 Scan里面的handleGAPEvent 方法即可。

/**
 * @brief Handle GAP events related to scans.
 * @param [in] event The event type for this event.
 * @param [in] param Parameter data for this event.
 */
void BLEScan::handleGAPEvent(
	esp_gap_ble_cb_event_t  event,
	esp_ble_gap_cb_param_t* param) {
    
    

	switch(event) {
    
    

		// ---------------------------
		// 返回的数据格式
		// scan_rst:
		// esp_gap_search_evt_t search_evt
		// esp_bd_addr_t bda
		// esp_bt_dev_type_t dev_type
		// esp_ble_addr_type_t ble_addr_type
		// esp_ble_evt_type_t ble_evt_type
		// int rssi
		// uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX]
		// int flag
		// int num_resps
		// uint8_t adv_data_len
		// uint8_t scan_rsp_len
		case ESP_GAP_BLE_SCAN_RESULT_EVT: {
    
    

			switch(param->scan_rst.search_evt) {
    
    
				//
				// ESP_GAP_SEARCH_INQ_CMPL_EVT
				//
				// Event that indicates that the duration allowed for the search has completed or that we have been
				// asked to stop.
				case ESP_GAP_SEARCH_INQ_CMPL_EVT: {
    
    
					log_w("ESP_GAP_SEARCH_INQ_CMPL_EVT");
					m_stopped = true;
					m_semaphoreScanEnd.give();
					// 扫描结束回调 m_scanCompleteCB
					if (m_scanCompleteCB != nullptr) {
    
    
						m_scanCompleteCB(m_scanResults);
					}
					break;
				} // ESP_GAP_SEARCH_INQ_CMPL_EVT

				//
				// ESP_GAP_SEARCH_INQ_RES_EVT
				//
				// Result that has arrived back from a Scan inquiry.
				case ESP_GAP_SEARCH_INQ_RES_EVT: {
    
    
					if (m_stopped) {
    
     // If we are not scanning, nothing to do with the extra results.
						break;
					}

// Examine our list of previously scanned addresses and, if we found this one already,
// ignore it.
                    // 生成一个蓝牙地址对象
					BLEAddress advertisedAddress(param->scan_rst.bda);
					bool found = false;
					bool shouldDelete = true;

                    // 是否去重
					if (!m_wantDuplicates) {
    
    
						if (m_scanResults.m_vectorAdvertisedDevices.count(advertisedAddress.toString()) != 0) {
    
    
							found = true;
						}

						if (found) {
    
      // If we found a previous entry AND we don't want duplicates, then we are done.
							log_d("Ignoring %s, already seen it.", advertisedAddress.toString().c_str());
							vTaskDelay(1);  // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we are blocked here
							break;
						}
					}

					// We now construct a model of the advertised device that we have just found for the first
					// time.
					// ESP_LOG_BUFFER_HEXDUMP((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG);
					// log_w("bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type);
					// 核心代码,生成新的蓝牙广播设备
					BLEAdvertisedDevice *advertisedDevice = new BLEAdvertisedDevice();
					// 填上一些具体信息 地址 信号强度  广播数据标签
					advertisedDevice->setAddress(advertisedAddress);
					advertisedDevice->setRSSI(param->scan_rst.rssi);
					advertisedDevice->setAdFlag(param->scan_rst.flag);
					// 是否解析广播数据
					if (m_shouldParse) {
    
    
						advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len);
					} else {
    
    
						advertisedDevice->setPayload((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len);
					}
					advertisedDevice->setScan(this);
					// 地址类型
					advertisedDevice->setAddressType(param->scan_rst.ble_addr_type);

                    // 触发回调,有回调就触发回调
					if (m_pAdvertisedDeviceCallbacks) {
    
     // if has callback, no need to record to vector
						m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice);
					} else if (!m_wantDuplicates && !found) {
    
       // if no callback and not want duplicate, and not already in vector, record it
					// 没回调就写入内存map中 m_vectorAdvertisedDevices	 
					m_scanResults.m_vectorAdvertisedDevices.insert(std::pair<std::string, BLEAdvertisedDevice*>(advertisedAddress.toString(), advertisedDevice));
						shouldDelete = false;
					}
					if (shouldDelete) {
    
    
						delete advertisedDevice;
					}

					break;
				} // ESP_GAP_SEARCH_INQ_RES_EVT

				default: {
    
    
					break;
				}
			} // switch - search_evt


			break;
		} // ESP_GAP_BLE_SCAN_RESULT_EVT

		default: {
    
    
			break;
		} // default
	} // End switch
} // gapEventHandler

至此,着重看注释的地方,启动扫描讲解完毕,这是本篇核心内容。说完这个大头,接下来的方法就简单很多了。

留个疑问:

  • esp_gap_ble_cb_event_t 这是什么?(就是GAP里面定义的回调事件类型)
/// GAP BLE callback event type
typedef enum {
    
    
    ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT        = 0,       /*!< When advertising data set complete, the event comes */
    ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT,             /*!< When scan response data set complete, the event comes */
    ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT,                /*!< When scan parameters set complete, the event comes */
    ESP_GAP_BLE_SCAN_RESULT_EVT,                            /*!< When one scan result ready, the event comes each time */
    ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT,              /*!< When raw advertising data set complete, the event comes */
    ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT,         /*!< When raw advertising data set complete, the event comes */
    ESP_GAP_BLE_ADV_START_COMPLETE_EVT,                     /*!< When start advertising complete, the event comes */
    ESP_GAP_BLE_SCAN_START_COMPLETE_EVT,                    /*!< When start scan complete, the event comes */
    ESP_GAP_BLE_AUTH_CMPL_EVT,                              /* Authentication complete indication. */
    ESP_GAP_BLE_KEY_EVT,                                    /* BLE  key event for peer device keys */
    ESP_GAP_BLE_SEC_REQ_EVT,                                /* BLE  security request */
    ESP_GAP_BLE_PASSKEY_NOTIF_EVT,                          /* passkey notification event */
    ESP_GAP_BLE_PASSKEY_REQ_EVT,                            /* passkey request event */
    ESP_GAP_BLE_OOB_REQ_EVT,                                /* OOB request event */
    ESP_GAP_BLE_LOCAL_IR_EVT,                               /* BLE local IR event */
    ESP_GAP_BLE_LOCAL_ER_EVT,                               /* BLE local ER event */
    ESP_GAP_BLE_NC_REQ_EVT,                                 /* Numeric Comparison request event */
    ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT,                      /*!< When stop adv complete, the event comes */
    ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT,                     /*!< When stop scan complete, the event comes */
    ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT,                   /*!< When set the static rand address complete, the event comes */
    ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT,                     /*!< When update connection parameters complete, the event comes */
    ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT,                /*!< When set pkt length complete, the event comes */
    ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT,             /*!< When  Enable/disable privacy on the local device complete, the event comes */
    ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT,               /*!< When remove the bond device complete, the event comes */
    ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT,                /*!< When clear the bond device clear complete, the event comes */
    ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT,                  /*!< When get the bond device list complete, the event comes */
    ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT,                     /*!< When read the rssi complete, the event comes */
    ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT,              /*!< When add or remove whitelist complete, the event comes */
    ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT,  /*!< When update duplicate exceptional list complete, the event comes */
    ESP_GAP_BLE_EVT_MAX,
} esp_gap_ble_cb_event_t;

其中就有我们Scan需要的 ESP_GAP_BLE_SCAN_RESULT_EVT

  • esp_ble_gap_cb_param_t 这是什么?(说白了就是事件回调的时候,需要给我们带回来的数据)
/**
 * @brief Gap callback parameters union
 */
typedef union {
    
    
    /**
     * @brief ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT
     */
    struct ble_adv_data_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the set advertising data operation success status */
    } adv_data_cmpl;                                /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT
     */
    struct ble_scan_rsp_data_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the set scan response data operation success status */
    } scan_rsp_data_cmpl;                           /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT
     */
    struct ble_scan_param_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the set scan param operation success status */
    } scan_param_cmpl;                              /*!< Event parameter of ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */
    /**
     * @brief 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 */
    /**
     * @brief ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT
     */
    struct ble_adv_data_raw_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the set raw advertising data operation success status */
    } adv_data_raw_cmpl;                            /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT
     */
    struct ble_scan_rsp_data_raw_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the set raw advertising data operation success status */
    } scan_rsp_data_raw_cmpl;                       /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_ADV_START_COMPLETE_EVT
     */
    struct ble_adv_start_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate advertising start operation success status */
    } adv_start_cmpl;                               /*!< Event parameter of ESP_GAP_BLE_ADV_START_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_SCAN_START_COMPLETE_EVT
     */
    struct ble_scan_start_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate scan start operation success status */
    } scan_start_cmpl;                              /*!< Event parameter of ESP_GAP_BLE_SCAN_START_COMPLETE_EVT */

    esp_ble_sec_t ble_security;                     /*!< ble gap security union type */
    /**
     * @brief ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT
     */
    struct ble_scan_stop_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate scan stop operation success status */
    } scan_stop_cmpl;                               /*!< Event parameter of ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT
     */
    struct ble_adv_stop_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate adv stop operation success status */
    } adv_stop_cmpl;                                /*!< Event parameter of ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT
     */
    struct ble_set_rand_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate set static rand address operation success status */
    } set_rand_addr_cmpl;                           /*!< Event parameter of ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT */
    /**
     * @brief ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT
     */
    struct ble_update_conn_params_evt_param {
    
    
        esp_bt_status_t status;                    /*!< Indicate update connection parameters success status */
        esp_bd_addr_t bda;                         /*!< Bluetooth device address */
        uint16_t min_int;                          /*!< Min connection interval */
        uint16_t max_int;                          /*!< Max connection interval */
        uint16_t latency;                          /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */
        uint16_t conn_int;                         /*!< Current connection interval */
        uint16_t timeout;                          /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80.
                                                     Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec */
    }update_conn_params;                           /*!< Event parameter of ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT */
    /**
     * @brief ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT
     */
    struct ble_pkt_data_length_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the set pkt data length operation success status */
        esp_ble_pkt_data_length_params_t params;    /*!<  pkt data length value */
    } pkt_data_lenth_cmpl;                          /*!< Event parameter of ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT
     */
    struct ble_local_privacy_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the set local privacy operation success status */
    } local_privacy_cmpl;                           /*!< Event parameter of ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT
     */
    struct ble_remove_bond_dev_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the remove bond device operation success status */
        esp_bd_addr_t bd_addr;                      /*!< The device address which has been remove from the bond list */
    }remove_bond_dev_cmpl;                          /*!< Event parameter of ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT
     */
    struct ble_clear_bond_dev_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the clear bond device operation success status */
    }clear_bond_dev_cmpl;                           /*!< Event parameter of ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT
     */
    struct ble_get_bond_dev_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the get bond device operation success status */
        uint8_t dev_num;                            /*!< Indicate the get number device in the bond list */
        esp_ble_bond_dev_t *bond_dev;               /*!< the pointer to the bond device Structure */
    }get_bond_dev_cmpl;                             /*!< Event parameter of ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
     */
    struct ble_read_rssi_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the read adv tx power operation success status */
        int8_t rssi;                                /*!< The ble remote device rssi value, the range is from -127 to 20, the unit is dbm,
                                                         if the RSSI cannot be read, the RSSI metric shall be set to 127. */
        esp_bd_addr_t remote_addr;                  /*!< The remote device address */
    } read_rssi_cmpl;                               /*!< Event parameter of ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT
     */
    struct ble_update_whitelist_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate the add or remove whitelist operation success status */
        esp_ble_wl_opration_t wl_opration;          /*!< The value is ESP_BLE_WHITELIST_ADD if add address to whitelist operation success, ESP_BLE_WHITELIST_REMOVE if remove address from the whitelist operation success */
    } update_whitelist_cmpl;                        /*!< Event parameter of ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT */
    /**
     * @brief ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT
     */
    struct ble_update_duplicate_exceptional_list_cmpl_evt_param {
    
    
        esp_bt_status_t status;                     /*!< Indicate update duplicate scan exceptional list operation success status */
        uint8_t         subcode;                    /*!< Define in esp_bt_duplicate_exceptional_subcode_type_t */
        uint16_t         length;                     /*!< The length of device_info */
        esp_duplicate_info_t device_info;           /*!< device information, when subcode is ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_CLEAN, the value is invalid */
    } update_duplicate_exceptional_list_cmpl;       /*!< Event parameter of ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT */
} esp_ble_gap_cb_param_t;

这是一个 union,其中包含了GAP中目前用到的所有结构体,Scan需要关注 ble_scan_result_evt_param

/**
     * @brief 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 */
        // 蓝牙设备类型 经典、BLE、dumo,我们这里只关注ble
        esp_bt_dev_type_t dev_type;                 /*!< Device type */
        // 设备地址类型 公有还是随机
        esp_ble_addr_type_t ble_addr_type;          /*!< Ble device address type */
        // 广播事件类型,后续会有篇章专门讲解,只需要有如下几种类型
        // Connectable undirected advertising (ADV_IND)
        // Connectable directed advertising (ADV_DIRECT_IND)
        // Scannable undirected advertising (ADV_SCAN_IND)
        // Non connectable undirected advertising (ADV_NONCONN_IND)
        // Scan Response (SCAN_RSP)
        // 这些事件都是通过Scan功能扫描得到
        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 */
        // 广播数据标签位,对应 广播数据结构体 0x01 也就是标签位,后续进阶篇章详细讲解
        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 */

重点只说一遍。这个点非常容易忽略。

// 广播所有数据 = 广播数据 + 扫描响应数据(可选)
uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */
扫描返回的数据包括了 扫描响应数据,所以你会发现payload大于31个字节。
ESP_BLE_ADV_DATA_LEN_MAX = 31
ESP_BLE_SCAN_RSP_DATA_LEN_MAX = 31
两个广播包的大小

3.1.3 停止扫描 —— stop

函数说明

/**
 * @brief 停止扫描
 */
void stop();

源码说明

/**
 * @brief Stop an in progress scan.
 * @return N/A.
 */
void BLEScan::stop() {
    
    
	log_v(">> stop()");
    // 停止扫描
	esp_err_t errRc = ::esp_ble_gap_stop_scanning();

	m_stopped = true;
	m_semaphoreScanEnd.give();

	if (errRc != ESP_OK) {
    
    
		log_e("esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
		return;
	}

	log_v("<< stop()");
}

3.1.4 获取扫描结果 —— gerResults

函数说明

/**
 * @brief 获取扫描结果
 * @return BLEScanResults 扫描结果(同上讲解,不重复)
 */
BLEScanResults getResults();

源码说明

BLEScanResults BLEScan::getResults() {
    
    
	return m_scanResults;
}

3.1.5 清除扫描结果 —— clearResults

函数说明

/**
 * @brief 清除扫描结果
 */
void  clearResults();

源码说明

void BLEScan::clearResults() {
    
    
    // 这下子是不是感觉清晰很多
	for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){
    
    
		delete _dev.second;
	}
	m_scanResults.m_vectorAdvertisedDevices.clear();
}

3.1.6 从扫描结果缓存中清除特定的蓝牙地址 —— erase

函数说明

/**
 * @brief 从扫描结果缓存中清除特定的蓝牙地址
 * @param address 具体某一个蓝牙地址
 */
void  erase(BLEAddress address);

源码说明

// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address
void BLEScan::erase(BLEAddress address) {
    
    
	log_i("erase device: %s", address.toString().c_str());
	BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second;
	m_scanResults.m_vectorAdvertisedDevices.erase(address.toString());
	delete advertisedDevice;
}

3.2 配置扫描设置

3.2.1 配置扫描方式 —— setActiveScan

函数说明

/**
 * @brief 配置扫描方式
 * @param active true表示主动扫描,false表示被动扫描
 */
void  setActiveScan(bool active);

源码说明

/**
 * @brief Should we perform an active or passive scan?
 * The default is a passive scan.  An active scan means that we will wish a scan response.
 * @param [in] active If true, we perform an active scan otherwise a passive scan.
 * @return N/A.
 */
void BLEScan::setActiveScan(bool active) {
    
    
	if (active) {
    
    
	    // 主动扫描
		m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE;
	} else {
    
    
	    // 被动扫描
		m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE;
	}
} 
  • 同上详解扫描参数

3.2.2 配置扫描结果回调 —— setAdvertisedDeviceCallbacks

函数说明

/**
 * @brief 配置扫描结果回调
 * @param pAdvertisedDeviceCallbacks 广播设备信息回调,此处一般需要我们继承 BLEAdvertisedDeviceCallbacks 去实现具体处理,同上 详解扫描参数
 * @param wantDuplicates 是否去重,默认不去重,全部生成扫描报告给到host层
 * @param shouldParse 是否解析广播数据包,默认是true,false表示保留原始数据
 */
void  setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
								   bool wantDuplicates = false,
								   bool shouldParse = true);

源码说明

/**
 * @brief Set the call backs to be invoked.
 * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
 * @param [in] wantDuplicates  True if we wish to be called back with duplicates.  Default is false.
 * @param [in] shouldParse  True if we wish to parse advertised package or raw payload.  Default is true.
 */
void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates, bool shouldParse) {
    
    
	m_wantDuplicates = wantDuplicates;
	m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
	m_shouldParse = shouldParse;
} // setAdvertisedDeviceCallbacks

3.2.3 配置扫描间隔 —— setInterval

函数说明

/**
 * @brief  配置扫描间隔
 * @param intervalMSecs 间隔时间,ms为单位
 */
void  setInterval(uint16_t intervalMSecs);

源码说明

/**
 * @brief Set the interval to scan.
 * @param [in] The interval in msecs.
 */
void BLEScan::setInterval(uint16_t intervalMSecs) {
    
    
	m_scan_params.scan_interval = intervalMSecs / 0.625;
} // setInterval
  • 同上详解扫描参数

注意点

  • 扫描间隔会转成 0.625ms的倍数(思考一下为什么是0.625ms,提示一下广播包间隔)

3.2.4 配置扫描窗口大小 —— setWindow

函数说明

/**
 * @brief   配置扫描窗口大小
 * @param windowMSecs 窗口大小时间,ms为单位
 */
void  setWindow(uint16_t windowMSecs);

源码说明

/**
 * @brief Set the window to actively scan.
 * @param [in] windowMSecs How long to actively scan.
 */
void BLEScan::setWindow(uint16_t windowMSecs) {
    
    
	m_scan_params.scan_window = windowMSecs / 0.625;
} // setWindow
  • 同上详解扫描参数

注意点

  • 窗口(window)时间大小不能小于间隔(interval)时间大小

3.3 BLEDevice扫描相关功能

正常来说,所有的蓝牙BLE设备都抽象为BLEDevice,然后赋予它Scan、Advertising等功能,

一般代码会这样写:

// 初始化蓝牙BLE环境,设置设备名字
BLEDevice::init("");
// 获取扫描对象
pBLEScan = BLEDevice::getScan(); //create new scan
// 配置一系列扫描配置
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99);  // less or equal setInterval value
// 启动扫描
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);

以下这句话必须记住:

在代码角度来说就是 BLEDevice会拥有成员变量BLEScan、BLEAdvertising等对象

那我们就去BLEDevice里面找找关于BLEScan的代码引入。

BLEDevice 头文件:

class BLEDevice {
    
    
public:
    // 省略部分代码 只留下Scan相关
	static BLEScan*    getScan();         // Get the scan object
	static void        init(std::string deviceName);   // Initialize the local BLE environment.
	
private:
	static BLEScan*		m_pScan;

	static void gapEventHandler(
		esp_gap_ble_cb_event_t  event,
		esp_ble_gap_cb_param_t* param);

public:
/* custom gap and gatt handlers for flexibility */
	static gap_event_handler m_customGapHandler;

}; // class BLE

可以看到 BLEDevice使用了类(static)方法来获取各种功能。

  • init 初始化整个蓝牙BLE设备环境
  • getScan 获取扫描入口

3.3.1 init —— 初始化BLE设备环境

这是一个非常非常重要的方法,BLE设备都要第一个调用它。这里只说明跟Scan相关,后面有独立一个章节讲解BLEDevice。

函数说明

/**
 * @brief  初始化BLE设备环境
 * @param  deviceName 设备名字
 */
static void init(std::string deviceName);   // Initialize the local BLE environment.

源码说明

/**
 * @brief Initialize the %BLE environment.
 * @param deviceName The device name of the device.
 */
/* STATIC */ void BLEDevice::init(std::string deviceName) {
    
    
	if(!initialized){
    
    
		initialized = true; // Set the initialization flag to ensure we are only initialized once.

		esp_err_t errRc = ESP_OK;
#ifdef ARDUINO_ARCH_ESP32
		if (!btStart()) {
    
    
			errRc = ESP_FAIL;
			return;
		}
#else
		errRc = ::nvs_flash_init();
		if (errRc != ESP_OK) {
    
    
			log_e("nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		}

#ifndef CLASSIC_BT_ENABLED
		esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);  
#endif
		esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
		errRc = esp_bt_controller_init(&bt_cfg);
		if (errRc != ESP_OK) {
    
    
			log_e("esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		}

#ifndef CLASSIC_BT_ENABLED
		errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE);
		if (errRc != ESP_OK) {
    
    
			log_e("esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		}
#else
		errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
		if (errRc != ESP_OK) {
    
    
			log_e("esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		}
#endif
#endif

		esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
		if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
    
    
			errRc = esp_bluedroid_init();
			if (errRc != ESP_OK) {
    
    
				log_e("esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
				return;
			}
		}

		if (bt_state != ESP_BLUEDROID_STATUS_ENABLED) {
    
    
			errRc = esp_bluedroid_enable();
			if (errRc != ESP_OK) {
    
    
				log_e("esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
				return;
			}
		}
        // 这一行需要关注 所有的GAP事件都在这里处理  
		errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler);
		if (errRc != ESP_OK) {
    
    
			log_e("esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		}

#ifdef CONFIG_GATTC_ENABLE   // Check that BLE client is configured in make menuconfig
		errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler);
		if (errRc != ESP_OK) {
    
    
			log_e("esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		}
#endif   // CONFIG_GATTC_ENABLE

#ifdef CONFIG_GATTS_ENABLE  // Check that BLE server is configured in make menuconfig
		errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler);
		if (errRc != ESP_OK) {
    
    
			log_e("esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		}
#endif   // CONFIG_GATTS_ENABLE
        // 这里配置设备名字
		errRc = ::esp_ble_gap_set_device_name(deviceName.c_str());
		if (errRc != ESP_OK) {
    
    
			log_e("esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		};

#ifdef CONFIG_BLE_SMP_ENABLE   // Check that BLE SMP (security) is configured in make menuconfig
		esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
		errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
		if (errRc != ESP_OK) {
    
    
			log_e("esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
			return;
		};
#endif // CONFIG_BLE_SMP_ENABLE
	}
	vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue.
} // init

重点关注中文注释的地方。。。

3.3.2 getScan—— 获取扫描对象

函数说明

/**
 * @brief  获取扫描对象
 */
 static BLEScan*    getScan();         // Get the scan object

源码说明

/**
 * @brief Retrieve the Scan object that we use for scanning.
 * @return The scanning object reference.  This is a singleton object.  The caller should not
 * try and release/delete it.
 */
/* STATIC */ BLEScan* BLEDevice::getScan() {
    
    
	//log_v(">> getScan");
	if (m_pScan == nullptr) {
    
    
		m_pScan = new BLEScan();
		//log_d(" - creating a new scan object");
	}
	//log_v("<< getScan: Returning object at 0x%x", (uint32_t)m_pScan);
	return m_pScan;
}

4、案例分析

4.1 重新分析官方案例

/*
   Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
   Ported to Arduino ESP32 by Evandro Copercini
*/

#include <BLEDevice.h> // 蓝牙Ble设备库
#include <BLEUtils.h> 
#include <BLEScan.h> // 蓝牙ble设备的扫描功能库 本篇重点
#include <BLEAdvertisedDevice.h> // 扫描到的蓝牙设备(广播状态)

int scanTime = 5; //扫描时间
BLEScan* pBLEScan; // 扫描对象

// 扫描广播设备结果回调
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    
    
    void onResult(BLEAdvertisedDevice advertisedDevice) {
    
    
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
    }
};

void setup() {
    
    
  Serial.begin(115200);
  Serial.println("Scanning...");

  // ESP32首先得是一个BLEDevice
  BLEDevice::init(""); 
  //  获取 BLEDevice下的Scan子功能
  pBLEScan = BLEDevice::getScan(); 
  // 配置一系列扫描设置
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());// 注册扫描结果回调
  pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster // 配置主动扫描
  pBLEScan->setInterval(100); // 配置扫描PDU间隔
  pBLEScan->setWindow(99);  // less or equal setInterval value // 设置扫描窗口大小,需要小于扫描间隔
}

void loop() {
    
    
  // 同步扫描,等待结果
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);// 开始扫描 等待扫描结果
  Serial.print("Devices found: ");
  Serial.println(foundDevices.getCount());
  Serial.println("Scan done!");
  //  清除缓存
  pBLEScan->clearResults();   // delete results fromBLEScan buffer to release memory,释放扫描缓存消耗
  delay(2000);
}

4.2 扫描特定设备

5、总结

本章作为Scan扫描功能的讲解,会涉及到BLEScan核心库的讲解,包含管理扫描和配置扫描两个方向,希望读者能跟着代码思路来理解。

猜你喜欢

转载自blog.csdn.net/dpjcn1990/article/details/113097811