diff --git a/examples/Bluetooth_5/NimBLE_extended_client/NimBLE_extended_client.ino b/examples/Bluetooth_5/NimBLE_extended_client/NimBLE_extended_client.ino index 738a5252..20f82455 100644 --- a/examples/Bluetooth_5/NimBLE_extended_client/NimBLE_extended_client.ino +++ b/examples/Bluetooth_5/NimBLE_extended_client/NimBLE_extended_client.ino @@ -16,7 +16,7 @@ #define SERVICE_UUID "ABCD" #define CHARACTERISTIC_UUID "1234" -static NimBLEAdvertisedDevice* advDevice; +static const NimBLEAdvertisedDevice* advDevice; static bool doConnect = false; static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever @@ -40,7 +40,7 @@ class ClientCallbacks : public NimBLEClientCallbacks { /* Define a class to handle the callbacks when advertisements are received */ class scanCallbacks: public NimBLEScanCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + void onResult(const NimBLEAdvertisedDevice* advertisedDevice) { Serial.printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str()); if(advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD"))) { @@ -55,8 +55,8 @@ class scanCallbacks: public NimBLEScanCallbacks { } /** Callback to process the results of the completed scan or restart it */ - void onScanEnd(NimBLEScanResults results) { - Serial.println("Scan Ended"); + void onScanEnd(const NimBLEScanResults& results, int reason) { + Serial.print("Scan Ended; reason = "); Serial.println(reason); } }; diff --git a/examples/NimBLE_Async_Client/NimBLE_Async_Client.ino b/examples/NimBLE_Async_Client/NimBLE_Async_Client.ino index 57b18d24..f852b54f 100644 --- a/examples/NimBLE_Async_Client/NimBLE_Async_Client.ino +++ b/examples/NimBLE_Async_Client/NimBLE_Async_Client.ino @@ -25,7 +25,7 @@ class ClientCallbacks : public NimBLEClientCallbacks { } clientCB; class scanCallbacks : public NimBLEScanCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + void onResult(const NimBLEAdvertisedDevice* advertisedDevice) { Serial.printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str()); if (advertisedDevice->haveName() && advertisedDevice->getName() == "NimBLE-Server") { Serial.println("Found Our Device"); @@ -48,8 +48,8 @@ class scanCallbacks : public NimBLEScanCallbacks { } } - void onScanEnd(NimBLEScanResults results) { - Serial.println("Scan Ended"); + void onScanEnd(const NimBLEScanResults& results, int reason) { + Serial.print("Scan Ended; reason = "); Serial.println(reason); NimBLEDevice::getScan()->start(scanTimeMs); } }; diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index 6c65649c..80c87ef2 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -10,7 +10,7 @@ #include -static NimBLEAdvertisedDevice* advDevice; +static const NimBLEAdvertisedDevice* advDevice; static bool doConnect = false; static uint32_t scanTime = 0 * 1000; // In milliseconds, 0 = scan forever @@ -85,7 +85,7 @@ class ClientCallbacks : public NimBLEClientCallbacks { /** Define a class to handle the callbacks when advertisments are received */ class scanCallbacks: public NimBLEScanCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + void onResult(const NimBLEAdvertisedDevice* advertisedDevice) { Serial.print("Advertised Device found: "); Serial.println(advertisedDevice->toString().c_str()); if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) @@ -101,8 +101,8 @@ class scanCallbacks: public NimBLEScanCallbacks { } /** Callback to process the results of the completed scan or restart it */ - void onScanEnd(NimBLEScanResults results) { - Serial.println("Scan Ended"); + void onScanEnd(const NimBLEScanResults& results, int reason) { + Serial.print("Scan Ended; reason = "); Serial.println(reason); } }; diff --git a/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino b/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino index 663d1b55..0fe44a3c 100644 --- a/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino +++ b/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino @@ -1,71 +1,49 @@ -/** Example of continuous scanning for BLE advertisements. - * This example will scan forever while consuming as few resources as possible - * and report all advertisments on the serial monitor. +/** + * Continuous Scan Example + * + * This example demonstrates how to continuously scan for BLE devices. + * When devices are found the onDiscovered and onResults callbacks will be called with the device data. + * The scan will not store the results, only the callbacks will be used + * When the scan timeout is reached the onScanEnd callback will be called and the scan will be restarted. + * This will clear the duplicate cache in the controller and allow the same devices to be reported again. * * Created: on January 31 2021 * Author: H2zero - * */ #include "NimBLEDevice.h" -NimBLEScan* pBLEScan; +static constexpr uint32_t scanTime = 30 * 1000; // 30 seconds scan time. -class scanCallbacks: public NimBLEScanCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { - Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); +class scanCallbacks : public NimBLEScanCallbacks { + // Initial discovery, advertisement data only. + void onDiscovered(const NimBLEAdvertisedDevice* advertisedDevice) override { + Serial.printf("Discovered Device: %s\n", advertisedDevice->toString().c_str()); } -}; - -void setup() { - Serial.begin(115200); - Serial.println("Scanning..."); - -/** *Optional* Sets the filtering mode used by the scanner in the BLE controller. - * - * Can be one of: - * CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default) - * Filter by device address only, advertisements from the same address will be reported only once. - * - * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1) - * Filter by data only, advertisements with the same data will only be reported once, - * even from different addresses. - * - * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2) - * Filter by address and data, advertisements from the same address will be reported only once, - * except if the data in the advertisement has changed, then it will be reported again. - * - * Can only be used BEFORE calling NimBLEDevice::init. -*/ - NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE); -/** *Optional* Sets the scan filter cache size in the BLE controller. - * When the number of duplicate advertisements seen by the controller - * reaches this value it will clear the cache and start reporting previously - * seen devices. The larger this number, the longer time between repeated - * device reports. Range 10 - 1000. (default 20) - * - * Can only be used BEFORE calling NimBLEDevice::init. - */ - NimBLEDevice::setScanDuplicateCacheSize(200); + // If active scanning the result here will have the scan response data. + // If not active scanning then this will be the same as onDiscovered. + void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override { + Serial.printf("Device result: %s\n", advertisedDevice->toString().c_str()); + } - NimBLEDevice::init(""); + void onScanEnd(const NimBLEScanResults& results, int reason) override { + Serial.printf("Scan ended reason = %d; restarting scan\n", reason); + NimBLEDevice::getScan()->start(scanTime, false, true); + } +} scanCallbacks; // create a callback class instance. - pBLEScan = NimBLEDevice::getScan(); //create new scan - // Set the callback for when devices are discovered, no duplicates. - pBLEScan->setScanCallbacks(new scanCallbacks(), false); - pBLEScan->setActiveScan(true); // Set active scanning, this will get more data from the advertiser. - pBLEScan->setInterval(97); // How often the scan occurs / switches channels; in milliseconds, - pBLEScan->setWindow(37); // How long to scan during the interval; in milliseconds. - pBLEScan->setMaxResults(0); // do not store the scan results, use callback only. +void setup() { + Serial.begin(115200); + NimBLEDevice::init(""); // Initialize the device, you can specify a device name if you want. + NimBLEScan* pBLEScan = NimBLEDevice::getScan(); // Create the scan object. + pBLEScan->setScanCallbacks(&scanCallbacks, false); // Set the callback for when devices are discovered, no duplicates. + pBLEScan->setActiveScan(true); // Set active scanning, this will get more data from the advertiser. + pBLEScan->setMaxResults(0); // Do not store the scan results, use callback only. + pBLEScan->start(scanTime, false, true); // duration, not a continuation of last scan, restart to get all devices again. + Serial.println("Scanning..."); } void loop() { - // If an error occurs that stops the scan, it will be restarted here. - if(pBLEScan->isScanning() == false) { - // Start scan with: duration = 0 seconds(forever), no scan end callback, not a continuation of a previous scan. - pBLEScan->start(0, false); - } - - delay(2000); -} \ No newline at end of file + delay(2000); +} diff --git a/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino b/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino index 9dceb4e6..3bf2c598 100644 --- a/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino +++ b/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino @@ -12,16 +12,16 @@ bool active = false; class scanCallbacks: public NimBLEScanCallbacks { - void onDiscovered(NimBLEAdvertisedDevice* advertisedDevice) { + void onDiscovered(const NimBLEAdvertisedDevice* advertisedDevice) { Serial.printf("Discovered Advertised Device: %s \n", advertisedDevice->toString().c_str()); } - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + void onResult(const NimBLEAdvertisedDevice* advertisedDevice) { Serial.printf("Advertised Device Result: %s \n", advertisedDevice->toString().c_str()); } - void onScanEnd(NimBLEScanResults results){ - Serial.println("Scan Ended"); + void onScanEnd(const NimBLEScanResults& results, int reason) { + Serial.print("Scan Ended; reason = "); Serial.println(reason); active = !active; pBLEScan->setActiveScan(active); Serial.printf("scan start, active = %u\n", active); diff --git a/examples/Refactored_original_examples/BLE_client/BLE_client.ino b/examples/Refactored_original_examples/BLE_client/BLE_client.ino index c6b0720c..afbbeecb 100644 --- a/examples/Refactored_original_examples/BLE_client/BLE_client.ino +++ b/examples/Refactored_original_examples/BLE_client/BLE_client.ino @@ -22,7 +22,9 @@ static boolean doConnect = false; static boolean connected = false; static boolean doScan = false; static BLERemoteCharacteristic* pRemoteCharacteristic; -static BLEAdvertisedDevice* myDevice; +/* const required now */ +/* static BLEAdvertisedDevice* myDevice;*/ +static const BLEAdvertisedDevice* myDevice; static void notifyCallback( BLERemoteCharacteristic* pBLERemoteCharacteristic, @@ -128,9 +130,9 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { * Called for each advertising BLE server. */ -/*** Only a reference to the advertised device is passed now +/*** Only a const pointer to the advertised device is passed now void onResult(BLEAdvertisedDevice advertisedDevice) { **/ - void onResult(BLEAdvertisedDevice* advertisedDevice) { + void onResult(const BLEAdvertisedDevice* advertisedDevice) { Serial.print("BLE Advertised Device found: "); Serial.println(advertisedDevice->toString().c_str()); diff --git a/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino b/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino index eb299075..c2c142f5 100644 --- a/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino +++ b/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino @@ -18,9 +18,9 @@ int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever BLEScan* pBLEScan; class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { - /*** Only a reference to the advertised device is passed now + /*** Only a const pointer to the advertised device is passed now void onResult(BLEAdvertisedDevice advertisedDevice) { **/ - void onResult(BLEAdvertisedDevice* advertisedDevice) { + void onResult(const BLEAdvertisedDevice* advertisedDevice) { /** Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); **/ Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); } diff --git a/src/NimBLEAdvertisedDevice.cpp b/src/NimBLEAdvertisedDevice.cpp index fed8bf67..cdcfd72e 100644 --- a/src/NimBLEAdvertisedDevice.cpp +++ b/src/NimBLEAdvertisedDevice.cpp @@ -757,6 +757,14 @@ bool NimBLEAdvertisedDevice::isConnectable() const { return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK); } // isConnectable +/** + * @brief Check if this device is advertising as scannable. + * @return True if the device is scannable. + */ +bool NimBLEAdvertisedDevice::isScannable() const { + return isLegacyAdvertisement() && (m_advType == BLE_HCI_ADV_TYPE_ADV_IND || m_advType == BLE_HCI_ADV_TYPE_ADV_SCAN_IND); +} // isScannable + /** * @brief Check if this advertisement is a legacy or extended type * @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x). diff --git a/src/NimBLEAdvertisedDevice.h b/src/NimBLEAdvertisedDevice.h index e5563e08..5a5660c7 100644 --- a/src/NimBLEAdvertisedDevice.h +++ b/src/NimBLEAdvertisedDevice.h @@ -82,6 +82,7 @@ class NimBLEAdvertisedDevice { bool haveType(uint16_t type) const; std::string toString() const; bool isConnectable() const; + bool isScannable() const; bool isLegacyAdvertisement() const; # if CONFIG_BT_NIMBLE_EXT_ADV uint8_t getSetId() const; diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index a8581516..907ec18c 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -723,15 +723,7 @@ void NimBLEDevice::onReset(int reason) { m_synced = false; - NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason, NimBLEUtils::returnCodeToString(reason)); - -# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - if (m_initialized) { - if (m_pScan != nullptr) { - m_pScan->onHostReset(); - } - } -# endif + NIMBLE_LOGE(LOG_TAG, "Host reset; reason=%d, %s", reason, NimBLEUtils::returnCodeToString(reason)); } // onReset /** diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 58b4988a..243dc900 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -15,39 +15,32 @@ #include "nimconfig.h" #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) -#include "NimBLEScan.h" -#include "NimBLEDevice.h" -#include "NimBLELog.h" +# include "NimBLEScan.h" +# include "NimBLEDevice.h" +# include "NimBLELog.h" -#include -#include - -static const char* LOG_TAG = "NimBLEScan"; +# include +# include +static const char* LOG_TAG = "NimBLEScan"; +static NimBLEScanCallbacks defaultScanCallbacks; /** - * @brief Scan constuctor. + * @brief Scan constructor. */ -NimBLEScan::NimBLEScan() { - m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL; - m_scan_params.passive = 1; // If set, don’t send scan requests to advertisers (i.e., don’t request additional advertising data). - m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec) - m_scan_params.window = 0; // The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval (units=0.625 msec) - m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode. - m_scan_params.filter_duplicates = 1; // If set, the controller ignores all but the first advertisement from each device. - m_pScanCallbacks = nullptr; - m_ignoreResults = false; - m_pTaskData = nullptr; - m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset - m_maxResults = 0xFF; -} - +NimBLEScan::NimBLEScan() + : m_pScanCallbacks{&defaultScanCallbacks}, + // default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates + m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1}, + m_duration{BLE_HS_FOREVER}, + m_pTaskData{nullptr}, + m_maxResults{0xFF} {} /** * @brief Scan destructor, release any allocated resources. */ NimBLEScan::~NimBLEScan() { - clearResults(); + clearResults(); } /** @@ -55,32 +48,26 @@ NimBLEScan::~NimBLEScan() { * @param [in] event The event type for this event. * @param [in] param Parameter data for this event. */ -/*STATIC*/ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { (void)arg; NimBLEScan* pScan = NimBLEDevice::getScan(); - switch(event->type) { - + switch (event->type) { case BLE_GAP_EVENT_EXT_DISC: case BLE_GAP_EVENT_DISC: { - if(pScan->m_ignoreResults) { - NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results"); - return 0; - } -#if CONFIG_BT_NIMBLE_EXT_ADV - const auto& disc = event->ext_disc; - const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK; - const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props; -#else - const auto& disc = event->disc; - const bool isLegacyAdv = true; - const auto event_type = disc.event_type; -#endif +# if CONFIG_BT_NIMBLE_EXT_ADV + const auto& disc = event->ext_disc; + const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK; + const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props; +# else + const auto& disc = event->disc; + const bool isLegacyAdv = true; + const auto event_type = disc.event_type; +# endif NimBLEAddress advertisedAddress(disc.addr); - // Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected - if(NimBLEDevice::isIgnored(advertisedAddress)) { + // stop processing if we don't want to see it or are already connected + if (NimBLEDevice::isIgnored(advertisedAddress)) { NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str()); return 0; } @@ -88,85 +75,79 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { NimBLEAdvertisedDevice* advertisedDevice = nullptr; // If we've seen this device before get a pointer to it from the vector - for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) { -#if CONFIG_BT_NIMBLE_EXT_ADV + for (const auto& dev : pScan->m_scanResults.m_deviceVec) { +# if CONFIG_BT_NIMBLE_EXT_ADV // Same address but different set ID should create a new advertised device. - if (it->getAddress() == advertisedAddress && it->getSetId() == disc.sid) { -#else - if (it->getAddress() == advertisedAddress) { -#endif - advertisedDevice = it; + if (dev->getAddress() == advertisedAddress && dev->getSetId() == disc.sid) +# else + if (dev->getAddress() == advertisedAddress) +# endif + { + advertisedDevice = dev; break; } } // If we haven't seen this device before; create a new instance and insert it in the vector. // Otherwise just update the relevant parameters of the already known device. - if (advertisedDevice == nullptr && - (!isLegacyAdv || event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) { + if (advertisedDevice == nullptr) { // Check if we have reach the scan results limit, ignore this one if so. // We still need to store each device when maxResults is 0 to be able to append the scan results if (pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF && - (pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults)) { + (pScan->m_scanResults.m_deviceVec.size() >= pScan->m_maxResults)) { return 0; } + if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + NIMBLE_LOGI(LOG_TAG, "Scan response without advertisement: %s", advertisedAddress.toString().c_str()); + } + advertisedDevice = new NimBLEAdvertisedDevice(event, event_type); - pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice); + pScan->m_scanResults.m_deviceVec.push_back(advertisedDevice); NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str()); - } else if (advertisedDevice != nullptr) { - advertisedDevice->update(event, event_type); - NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str()); } else { - // Scan response from unknown device - return 0; - } - - if (pScan->m_pScanCallbacks) { - if (advertisedDevice->m_callbackSent == 0 || !pScan->m_scan_params.filter_duplicates) { - advertisedDevice->m_callbackSent = 1; - pScan->m_pScanCallbacks->onDiscovered(advertisedDevice); + advertisedDevice->update(event, event_type); + if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str()); + } else { + NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str()); } + } - if (pScan->m_scan_params.filter_duplicates && advertisedDevice->m_callbackSent >= 2) { - return 0; - } + if (!advertisedDevice->m_callbackSent) { + pScan->m_pScanCallbacks->onDiscovered(advertisedDevice); + advertisedDevice->m_callbackSent++; + } - // If not active scanning or scan response is not available - // or extended advertisement scanning, report the result to the callback now. - if(pScan->m_scan_params.passive || !isLegacyAdv || - (advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND && - advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND)) - { - advertisedDevice->m_callbackSent = 2; - pScan->m_pScanCallbacks->onResult(advertisedDevice); + // If not active scanning or scan response is not available + // or extended advertisement scanning, report the result to the callback now. + if (pScan->m_scanParams.passive || !isLegacyAdv || !advertisedDevice->isScannable()) { + pScan->m_pScanCallbacks->onResult(advertisedDevice); + advertisedDevice->m_callbackSent++; + } else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + // got the scan response report the full data. + pScan->m_pScanCallbacks->onResult(advertisedDevice); + advertisedDevice->m_callbackSent++; + } - // Otherwise, wait for the scan response so we can report the complete data. - } else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { - advertisedDevice->m_callbackSent = 2; - pScan->m_pScanCallbacks->onResult(advertisedDevice); - } - // If not storing results and we have invoked the callback, delete the device. - if(pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent >= 2) { - pScan->erase(advertisedAddress); - } + // If not storing results and we have invoked the callback, delete the device. + if (pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent >= 2) { + pScan->erase(advertisedDevice); } return 0; } + case BLE_GAP_EVENT_DISC_COMPLETE: { - NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", - event->disc_complete.reason); + NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason); - if(pScan->m_maxResults == 0) { + if (pScan->m_maxResults == 0) { pScan->clearResults(); } - if (pScan->m_pScanCallbacks != nullptr) { - pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults); - } + pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason); - if(pScan->m_pTaskData != nullptr) { + if (pScan->m_pTaskData != nullptr) { NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason); } @@ -176,8 +157,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { default: return 0; } -} // gapEventHandler - +} // handleGapEvent /** * @brief Should we perform an active or passive scan? @@ -185,10 +165,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { * @param [in] active If true, we perform an active scan otherwise a passive scan. */ void NimBLEScan::setActiveScan(bool active) { - m_scan_params.passive = !active; + m_scanParams.passive = !active; } // setActiveScan - /** * @brief Set whether or not the BLE controller should only report results * from devices it has not already seen. @@ -197,20 +176,18 @@ void NimBLEScan::setActiveScan(bool active) { * duplicate devices once the limit is reached. */ void NimBLEScan::setDuplicateFilter(bool enabled) { - m_scan_params.filter_duplicates = enabled; + m_scanParams.filter_duplicates = enabled; } // setDuplicateFilter - /** - * @brief Set whether or not the BLE controller only report scan results - * from devices advertising in limited discovery mode, i.e. directed advertising. + * @brief Set whether or not the BLE controller only reports scan results + * from devices advertising in limited discovery mode. * @param [in] enabled If true, only limited discovery devices will be in scan results. */ void NimBLEScan::setLimitedOnly(bool enabled) { - m_scan_params.limited = enabled; + m_scanParams.limited = enabled; } // setLimited - /** * @brief Sets the scan filter policy. * @param [in] filter Can be one of: @@ -230,10 +207,9 @@ void NimBLEScan::setLimitedOnly(bool enabled) { * resolvable private address. */ void NimBLEScan::setFilterPolicy(uint8_t filter) { - m_scan_params.filter_policy = filter; + m_scanParams.filter_policy = filter; } // setFilterPolicy - /** * @brief Sets the max number of results to store. * @param [in] maxResults The number of results to limit storage to\n @@ -243,36 +219,36 @@ void NimBLEScan::setMaxResults(uint8_t maxResults) { m_maxResults = maxResults; } - /** * @brief Set the call backs to be invoked. * @param [in] pScanCallbacks Call backs to be invoked. - * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. + * @param [in] wantDuplicates True if we wish to be called back with duplicates, default: false. */ void NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates) { setDuplicateFilter(!wantDuplicates); + if (pScanCallbacks == nullptr) { + m_pScanCallbacks = &defaultScanCallbacks; + return; + } m_pScanCallbacks = pScanCallbacks; } // setScanCallbacks - /** * @brief Set the interval to scan. * @param [in] intervalMSecs The scan interval (how often) in milliseconds. */ void NimBLEScan::setInterval(uint16_t intervalMSecs) { - m_scan_params.itvl = intervalMSecs / 0.625; + m_scanParams.itvl = (intervalMSecs * 16) / 10; } // setInterval - /** * @brief Set the window to actively scan. - * @param [in] windowMSecs How long to actively scan. + * @param [in] windowMSecs How long during the interval to actively scan. */ void NimBLEScan::setWindow(uint16_t windowMSecs) { - m_scan_params.window = windowMSecs / 0.625; + m_scanParams.window = (windowMSecs * 16) / 10; } // setWindow - /** * @brief Get the status of the scanner. * @return true if scanning or scan starting. @@ -281,14 +257,15 @@ bool NimBLEScan::isScanning() { return ble_gap_disc_active(); } - /** * @brief Start scanning. * @param [in] duration The duration in milliseconds for which to scan. 0 == scan forever. - * @param [in] is_continue Set to true to save previous scan results, false to clear them. + * @param [in] isContinue Set to true to save previous scan results, false to clear them. + * @param [in] restart Set to true to restart the scan if already in progress. + * this is useful to clear the duplicate filter so all devices are reported again. * @return True if scan started or false if there was an error. */ -bool NimBLEScan::start(uint32_t duration, bool is_continue) { +bool NimBLEScan::start(uint32_t duration, bool isContinue, bool restart) { NIMBLE_LOGD(LOG_TAG, ">> start: duration=%" PRIu32, duration); if (ble_gap_conn_active()) { @@ -296,51 +273,52 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) { return false; } + if (isScanning()) { + if (restart) { + NIMBLE_LOGI(LOG_TAG, "Scan already in progress, restarting it"); + if (!stop()) { + return false; + } + } else { + NIMBLE_LOGI(LOG_TAG, "Scan already in progress"); + return true; + } + } + + if (!isContinue) { + clearResults(); + } + // Save the duration in the case that the host is reset so we can reuse it. m_duration = duration; // If 0 duration specified then we assume a continuous scan is desired. - if(duration == 0){ + if (duration == 0) { duration = BLE_HS_FOREVER; } - // Set the flag to ignore the results while we are deleting the vector - if(!is_continue) { - m_ignoreResults = true; - } - # if CONFIG_BT_NIMBLE_EXT_ADV ble_gap_ext_disc_params scan_params; - scan_params.passive = m_scan_params.passive; - scan_params.itvl = m_scan_params.itvl; - scan_params.window = m_scan_params.window; - int rc = ble_gap_ext_disc(NimBLEDevice::m_ownAddrType, - duration/10, + scan_params.passive = m_scanParams.passive; + scan_params.itvl = m_scanParams.itvl; + scan_params.window = m_scanParams.window; + int rc = ble_gap_ext_disc(NimBLEDevice::m_ownAddrType, + duration / 10, 0, - m_scan_params.filter_duplicates, - m_scan_params.filter_policy, - m_scan_params.limited, + m_scanParams.filter_duplicates, + m_scanParams.filter_policy, + m_scanParams.limited, &scan_params, &scan_params, NimBLEScan::handleGapEvent, NULL); -#else - int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType, - duration, - &m_scan_params, - NimBLEScan::handleGapEvent, - NULL); -#endif - switch(rc) { +# else + int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType, duration, &m_scanParams, NimBLEScan::handleGapEvent, NULL); +# endif + switch (rc) { case 0: - if(!is_continue) { - clearResults(); - } - break; - case BLE_HS_EALREADY: - // Clear the cache if already scanning in case an advertiser was missed. - clearDuplicateCache(); + NIMBLE_LOGD(LOG_TAG, "Scan started"); break; case BLE_HS_EBUSY: @@ -355,21 +333,14 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) { break; default: - NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "Error starting scan; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); break; } - m_ignoreResults = false; NIMBLE_LOGD(LOG_TAG, "<< start()"); - - if(rc != 0 && rc != BLE_HS_EALREADY) { - return false; - } - return true; + return rc == 0 || rc == BLE_HS_EALREADY; } // start - /** * @brief Stop an in progress scan. * @return True if successful. @@ -383,15 +354,11 @@ bool NimBLEScan::stop() { return false; } - if(m_maxResults == 0) { + if (m_maxResults == 0) { clearResults(); } - if (rc != BLE_HS_EALREADY && m_pScanCallbacks != nullptr) { - m_pScanCallbacks->onScanEnd(m_scanResults); - } - - if(m_pTaskData != nullptr) { + if (m_pTaskData != nullptr) { NimBLEUtils::taskRelease(*m_pTaskData); } @@ -399,56 +366,46 @@ bool NimBLEScan::stop() { return true; } // stop - -/** - * @brief Clears the duplicate scan filter cache. - */ -void NimBLEScan::clearDuplicateCache() { -#ifdef CONFIG_IDF_TARGET_ESP32 // Not available for ESP32C3 - esp_ble_scan_dupilcate_list_flush(); -#endif -} - - /** * @brief Delete peer device from the scan results vector. * @param [in] address The address of the device to delete from the results. - * @details After disconnecting, it may be required in the case we were connected to a device without a public address. */ -void NimBLEScan::erase(const NimBLEAddress &address) { +void NimBLEScan::erase(const NimBLEAddress& address) { NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str()); - - for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) { - if((*it)->getAddress() == address) { + for (auto it = m_scanResults.m_deviceVec.begin(); it != m_scanResults.m_deviceVec.end(); ++it) { + if ((*it)->getAddress() == address) { delete *it; - m_scanResults.m_advertisedDevicesVector.erase(it); + m_scanResults.m_deviceVec.erase(it); break; } } } - /** - * @brief Called when host reset, we set a flag to stop scanning until synced. + * @brief Delete peer device from the scan results vector. + * @param [in] device The device to delete from the results. */ -void NimBLEScan::onHostReset() { - m_ignoreResults = true; +void NimBLEScan::erase(const NimBLEAdvertisedDevice* device) { + NIMBLE_LOGD(LOG_TAG, "erase device: %s", device->getAddress().toString().c_str()); + for (auto it = m_scanResults.m_deviceVec.begin(); it != m_scanResults.m_deviceVec.end(); ++it) { + if ((*it) == device) { + delete *it; + m_scanResults.m_deviceVec.erase(it); + break; + } + } } - /** * @brief If the host reset and re-synced this is called. * If the application was scanning indefinitely with a callback, restart it. */ void NimBLEScan::onHostSync() { - m_ignoreResults = false; - - if(m_duration == 0 && m_pScanCallbacks != nullptr) { + if (m_duration == 0 && m_pScanCallbacks != &defaultScanCallbacks) { start(0, false); } } - /** * @brief Start scanning and block until scanning has been completed. * @param [in] duration The duration in milliseconds for which to scan. @@ -456,14 +413,19 @@ void NimBLEScan::onHostSync() { * @return The scan results. */ NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) { - if(duration == 0) { + if (duration == 0) { NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever"); } + if (m_pTaskData != nullptr) { + NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); + return m_scanResults; + } + NimBLETaskData taskData; m_pTaskData = &taskData; - if(start(duration, is_continue)) { + if (start(duration, is_continue)) { NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); } @@ -471,7 +433,6 @@ NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) { return m_scanResults; } // getResults - /** * @brief Get the results of the scan. * @return NimBLEScanResults object. @@ -480,82 +441,87 @@ NimBLEScanResults NimBLEScan::getResults() { return m_scanResults; } - /** - * @brief Clear the results of the scan. + * @brief Clear the stored results of the scan. */ void NimBLEScan::clearResults() { - for(auto &it: m_scanResults.m_advertisedDevicesVector) { - delete it; + for (const auto& dev : m_scanResults.m_deviceVec) { + delete dev; } - m_scanResults.m_advertisedDevicesVector.clear(); - clearDuplicateCache(); -} - + std::vector().swap(m_scanResults.m_deviceVec); +} // clearResults /** * @brief Dump the scan results to the log. */ -void NimBLEScanResults::dump() { - NIMBLE_LOGD(LOG_TAG, ">> Dump scan results:"); - for (int i=0; itoString().c_str()); } } // dump - /** * @brief Get the count of devices found in the last scan. * @return The number of devices found in the last scan. */ -int NimBLEScanResults::getCount() { - return m_advertisedDevicesVector.size(); +int NimBLEScanResults::getCount() const { + return m_deviceVec.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. + * @param [in] idx The index of the device. * @return The device at the specified index. */ -NimBLEAdvertisedDevice NimBLEScanResults::getDevice(uint32_t i) { - return *m_advertisedDevicesVector[i]; +const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(uint32_t idx) const { + return m_deviceVec[idx]; } - /** * @brief Get iterator to the beginning of the vector of advertised device pointers. * @return An iterator to the beginning of the vector of advertised device pointers. */ -std::vector::iterator NimBLEScanResults::begin() { - return m_advertisedDevicesVector.begin(); +std::vector::const_iterator NimBLEScanResults::begin() const { + return m_deviceVec.begin(); } - /** * @brief Get iterator to the end of the vector of advertised device pointers. * @return An iterator to the end of the vector of advertised device pointers. */ -std::vector::iterator NimBLEScanResults::end() { - return m_advertisedDevicesVector.end(); +std::vector::const_iterator NimBLEScanResults::end() const { + return m_deviceVec.end(); } - /** * @brief Get a pointer to the specified device at the given address. * If the address is not found a nullptr is returned. * @param [in] address The address of the device. * @return A pointer to the device at the specified address. */ -NimBLEAdvertisedDevice *NimBLEScanResults::getDevice(const NimBLEAddress &address) { - for(size_t index = 0; index < m_advertisedDevicesVector.size(); index++) { - if(m_advertisedDevicesVector[index]->getAddress() == address) { - return m_advertisedDevicesVector[index]; +const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(const NimBLEAddress& address) const { + for (const auto& dev : m_deviceVec) { + if (dev->getAddress() == address) { + return dev; } } return nullptr; } +static const char* CB_TAG = "NimBLEScanCallbacks"; + +void NimBLEScanCallbacks::onDiscovered(const NimBLEAdvertisedDevice* pAdvertisedDevice) { + NIMBLE_LOGD(CB_TAG, "Discovered: %s", pAdvertisedDevice->toString().c_str()); +} + +void NimBLEScanCallbacks::onResult(const NimBLEAdvertisedDevice* pAdvertisedDevice) { + NIMBLE_LOGD(CB_TAG, "Result: %s", pAdvertisedDevice->toString().c_str()); +} + +void NimBLEScanCallbacks::onScanEnd(const NimBLEScanResults& results, int reason) { + NIMBLE_LOGD(CB_TAG, "Scan ended; reason %d, num results: %d", reason, results.getCount()); +} + #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */ diff --git a/src/NimBLEScan.h b/src/NimBLEScan.h index 47dd5c4b..426b9b04 100644 --- a/src/NimBLEScan.h +++ b/src/NimBLEScan.h @@ -17,16 +17,16 @@ #include "nimconfig.h" #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) -#include "NimBLEAdvertisedDevice.h" -#include "NimBLEUtils.h" +# include "NimBLEAdvertisedDevice.h" +# include "NimBLEUtils.h" -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "host/ble_gap.h" -#else -#include "nimble/nimble/host/include/host/ble_gap.h" -#endif +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif -#include +# include class NimBLEDevice; class NimBLEScan; @@ -42,17 +42,17 @@ class NimBLEAddress; * index (starting at 0) of the desired device. */ class NimBLEScanResults { -public: - void dump(); - int getCount(); - NimBLEAdvertisedDevice getDevice(uint32_t i); - std::vector::iterator begin(); - std::vector::iterator end(); - NimBLEAdvertisedDevice *getDevice(const NimBLEAddress &address); - -private: + public: + void dump() const; + int getCount() const; + const NimBLEAdvertisedDevice* getDevice(uint32_t idx) const; + const NimBLEAdvertisedDevice* getDevice(const NimBLEAddress& address) const; + std::vector::const_iterator begin() const; + std::vector::const_iterator end() const; + + private: friend NimBLEScan; - std::vector m_advertisedDevicesVector; + std::vector m_deviceVec; }; /** @@ -61,67 +61,65 @@ class NimBLEScanResults { * Scanning is associated with a %BLE client that is attempting to locate BLE servers. */ class NimBLEScan { -public: - bool start(uint32_t duration, bool is_continue = false); - bool isScanning(); - void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false); - void setActiveScan(bool active); - void setInterval(uint16_t intervalMSecs); - void setWindow(uint16_t windowMSecs); - void setDuplicateFilter(bool enabled); - void setLimitedOnly(bool enabled); - void setFilterPolicy(uint8_t filter); - void clearDuplicateCache(); - bool stop(); - void clearResults(); - NimBLEScanResults getResults(); - NimBLEScanResults getResults(uint32_t duration, bool is_continue = false); - void setMaxResults(uint8_t maxResults); - void erase(const NimBLEAddress &address); - - -private: + public: + bool start(uint32_t duration, bool isContinue = false, bool restart = true); + bool isScanning(); + void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false); + void setActiveScan(bool active); + void setInterval(uint16_t intervalMSecs); + void setWindow(uint16_t windowMSecs); + void setDuplicateFilter(bool enabled); + void setLimitedOnly(bool enabled); + void setFilterPolicy(uint8_t filter); + bool stop(); + void clearResults(); + NimBLEScanResults getResults(); + NimBLEScanResults getResults(uint32_t duration, bool is_continue = false); + void setMaxResults(uint8_t maxResults); + void erase(const NimBLEAddress& address); + void erase(const NimBLEAdvertisedDevice* device); + + private: friend class NimBLEDevice; NimBLEScan(); ~NimBLEScan(); - static int handleGapEvent(ble_gap_event* event, void* arg); - void onHostReset(); - void onHostSync(); - - NimBLEScanCallbacks* m_pScanCallbacks; - ble_gap_disc_params m_scan_params; - bool m_ignoreResults; - NimBLEScanResults m_scanResults; - uint32_t m_duration; - NimBLETaskData *m_pTaskData; - uint8_t m_maxResults; + static int handleGapEvent(ble_gap_event* event, void* arg); + void onHostSync(); + + NimBLEScanCallbacks* m_pScanCallbacks; + ble_gap_disc_params m_scanParams; + NimBLEScanResults m_scanResults; + uint32_t m_duration; + NimBLETaskData* m_pTaskData; + uint8_t m_maxResults; }; /** * @brief A callback handler for callbacks associated device scanning. */ class NimBLEScanCallbacks { -public: + public: virtual ~NimBLEScanCallbacks() {} /** * @brief Called when a new device is discovered, before the scan result is received (if applicable). * @param [in] advertisedDevice The device which was discovered. */ - virtual void onDiscovered(NimBLEAdvertisedDevice* advertisedDevice) {}; + virtual void onDiscovered(const NimBLEAdvertisedDevice* advertisedDevice); /** * @brief Called when a new scan result is complete, including scan response data (if applicable). * @param [in] advertisedDevice The device for which the complete result is available. */ - virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) {}; + virtual void onResult(const NimBLEAdvertisedDevice* advertisedDevice); /** * @brief Called when a scan operation ends. * @param [in] scanResults The results of the scan that ended. + * @param [in] reason The reason code for why the scan ended. */ - virtual void onScanEnd(NimBLEScanResults scanResults) {}; + virtual void onScanEnd(const NimBLEScanResults& scanResults, int reason); }; #endif /* CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER */