Last active
May 4, 2019 11:59
-
-
Save chegewara/6fc38e4127a7e18bb5b0bb4367aa33d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* BLEScan.cpp | |
* | |
* Created on: Jul 1, 2017 | |
* Author: kolban | |
*/ | |
#include "sdkconfig.h" | |
#if defined(CONFIG_BT_ENABLED) | |
#include <esp_log.h> | |
#include <esp_err.h> | |
#include <map> | |
#include "BLEAdvertisedDevice.h" | |
#include "BLEScan.h" | |
#include "BLEUtils.h" | |
#include "GeneralUtils.h" | |
#ifdef ARDUINO_ARCH_ESP32 | |
#include "esp32-hal-log.h" | |
#endif | |
static const char* LOG_TAG = "BLEScan"; | |
/** | |
* Constructor | |
*/ | |
BLEScan::BLEScan() { | |
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. | |
m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; | |
m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; | |
m_pAdvertisedDeviceCallbacks = nullptr; | |
m_stopped = true; | |
m_wantDuplicates = false; | |
setInterval(100); | |
setWindow(100); | |
} // BLEScan | |
/** | |
* @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: { | |
ESP_LOGW(LOG_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT"); | |
m_stopped = true; | |
m_semaphoreScanEnd.give(); | |
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; | |
if (m_scanResults.m_vectorAdvertisedDevices.count(advertisedAddress.toString()) != 0) { | |
found = true; | |
} | |
if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done. | |
ESP_LOGD(LOG_TAG, "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 will 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(LOG_TAG, (uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG); | |
// ESP_LOGW(LOG_TAG, "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); | |
advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); | |
advertisedDevice->setScan(this); | |
advertisedDevice->setAddressType(param->scan_rst.ble_addr_type); | |
if (!found) { // If we have previously seen this device, don't record it again. | |
m_scanResults.m_vectorAdvertisedDevices.insert(std::pair<std::string, BLEAdvertisedDevice*>(advertisedAddress.toString(), advertisedDevice)); | |
} | |
if (m_pAdvertisedDeviceCallbacks) { | |
m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); | |
} | |
if(found) | |
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 | |
/** | |
* @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; | |
} | |
} // setActiveScan | |
/** | |
* @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. | |
*/ | |
void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { | |
m_wantDuplicates = wantDuplicates; | |
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; | |
} // setAdvertisedDeviceCallbacks | |
/** | |
* @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 | |
/** | |
* @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 | |
/** | |
* @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) { | |
ESP_LOGD(LOG_TAG, ">> 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_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); | |
if (errRc != ESP_OK) { | |
ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); | |
m_semaphoreScanEnd.give(); | |
return false; | |
} | |
errRc = ::esp_ble_gap_start_scanning(duration); | |
if (errRc != ESP_OK) { | |
ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); | |
m_semaphoreScanEnd.give(); | |
return false; | |
} | |
m_stopped = false; | |
ESP_LOGD(LOG_TAG, "<< 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) { | |
if(start(duration, nullptr, is_continue)) { | |
m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. | |
} | |
return m_scanResults; | |
} // start | |
/** | |
* @brief Stop an in progress scan. | |
* @return N/A. | |
*/ | |
void BLEScan::stop() { | |
ESP_LOGD(LOG_TAG, ">> stop()"); | |
esp_err_t errRc = ::esp_ble_gap_stop_scanning(); | |
m_stopped = true; | |
m_semaphoreScanEnd.give(); | |
if (errRc != ESP_OK) { | |
ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); | |
return; | |
} | |
ESP_LOGD(LOG_TAG, "<< stop()"); | |
} // stop | |
// 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) { | |
ESP_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str()); | |
BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second; | |
m_scanResults.m_vectorAdvertisedDevices.erase(address.toString()); | |
// if(advertisedDevice != nullptr) | |
delete advertisedDevice; | |
} | |
/** | |
* @brief Dump the scan results to the log. | |
*/ | |
void BLEScanResults::dump() { | |
ESP_LOGD(LOG_TAG, ">> Dump scan results:"); | |
for (int i=0; i<getCount(); i++) { | |
ESP_LOGD(LOG_TAG, "- %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; | |
} | |
#endif /* CONFIG_BT_ENABLED */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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> | |
// #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. | |
*/ | |
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); | |
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); | |
private: | |
BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. | |
friend class BLEDevice; | |
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; | |
bool m_stopped; | |
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_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment