[BREAKING] - Refactor NimBLEScan

* General code cleanup
* `NimBLEScan::start` will no longer clear cache or results if scanning is already in progress.
* `NimBLEScan::clearResults` will now reset the vector capacity to 0.
* `NimBLEScan::stop` will no longer call the `onScanEnd` callback as the caller should know its been stopped when this is called.
* `NimBLEScan::clearDuplicateCache` has been removed as it was problematic and only for the esp32. Stop and start the scanner for the same effect.
* `NimBLEScan::start` takes a new bool parameter `restart`, default `true`, that will restart an already in progress scan and clear the duplicate filter so all devices will be discovered again.
* Scan response data that is received without advertisement first will now create the device and send a callback.
* Added new method: `NimBLEAdvertisedDevice::isScannable()` that returns true if the device is scannable.
* Added default callbacks for `NimBLEScanCallbacks`
* `NimBLEScanCallbacks` function signatures updated:
* - `onDiscovered` now takes a `const NimBLEAdvertisedDevice*`
* - `onResult` now takes a `const NimBLEAdvertisedDevice*`
* - `onScanEnd` now takes a `const NimBLEScanResults&` and `int reason`
* Added new erase overload: `NimBLEScan::erase(const NimBLEAdvertisedDevice* device)`
* `NimBLEScanResults::getDevice` methods now return `const NimBLEAdvertisedDevice*`
* `NimBLEScanResults` iterators are now `const_iterator`
This commit is contained in:
h2zero 2024-11-18 09:37:46 -07:00 committed by h2zero
parent 5f2730de02
commit 298f1b0ecd
5 changed files with 242 additions and 276 deletions

View file

@ -757,6 +757,14 @@ bool NimBLEAdvertisedDevice::isConnectable() const {
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK); return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
} // isConnectable } // 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 * @brief Check if this advertisement is a legacy or extended type
* @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x). * @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).

View file

@ -82,6 +82,7 @@ class NimBLEAdvertisedDevice {
bool haveType(uint16_t type) const; bool haveType(uint16_t type) const;
std::string toString() const; std::string toString() const;
bool isConnectable() const; bool isConnectable() const;
bool isScannable() const;
bool isLegacyAdvertisement() const; bool isLegacyAdvertisement() const;
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId() const; uint8_t getSetId() const;

View file

@ -723,15 +723,7 @@ void NimBLEDevice::onReset(int reason) {
m_synced = false; m_synced = false;
NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason, NimBLEUtils::returnCodeToString(reason)); NIMBLE_LOGE(LOG_TAG, "Host reset; 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
} // onReset } // onReset
/** /**

View file

@ -23,25 +23,18 @@
# include <climits> # include <climits>
static const char* LOG_TAG = "NimBLEScan"; static const char* LOG_TAG = "NimBLEScan";
static NimBLEScanCallbacks defaultScanCallbacks;
/** /**
* @brief Scan constuctor. * @brief Scan constructor.
*/ */
NimBLEScan::NimBLEScan() { NimBLEScan::NimBLEScan()
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL; : m_pScanCallbacks{&defaultScanCallbacks},
m_scan_params.passive = 1; // If set, dont send scan requests to advertisers (i.e., dont request additional advertising data). // default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
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_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
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_duration{BLE_HS_FOREVER},
m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode. m_pTaskData{nullptr},
m_scan_params.filter_duplicates = 1; // If set, the controller ignores all but the first advertisement from each device. m_maxResults{0xFF} {}
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;
}
/** /**
* @brief Scan destructor, release any allocated resources. * @brief Scan destructor, release any allocated resources.
@ -55,19 +48,13 @@ NimBLEScan::~NimBLEScan() {
* @param [in] event The event type for this event. * @param [in] event The event type for this event.
* @param [in] param Parameter data for this event. * @param [in] param Parameter data for this event.
*/ */
/*STATIC*/
int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
(void)arg; (void)arg;
NimBLEScan* pScan = NimBLEDevice::getScan(); NimBLEScan* pScan = NimBLEDevice::getScan();
switch (event->type) { switch (event->type) {
case BLE_GAP_EVENT_EXT_DISC: case BLE_GAP_EVENT_EXT_DISC:
case BLE_GAP_EVENT_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 # if CONFIG_BT_NIMBLE_EXT_ADV
const auto& disc = event->ext_disc; const auto& disc = event->ext_disc;
const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK; const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
@ -79,7 +66,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
# endif # endif
NimBLEAddress advertisedAddress(disc.addr); 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 // stop processing if we don't want to see it or are already connected
if (NimBLEDevice::isIgnored(advertisedAddress)) { if (NimBLEDevice::isIgnored(advertisedAddress)) {
NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str()); NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str());
return 0; return 0;
@ -88,83 +75,77 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
NimBLEAdvertisedDevice* advertisedDevice = nullptr; NimBLEAdvertisedDevice* advertisedDevice = nullptr;
// If we've seen this device before get a pointer to it from the vector // If we've seen this device before get a pointer to it from the vector
for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) { for (const auto& dev : pScan->m_scanResults.m_deviceVec) {
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
// Same address but different set ID should create a new advertised device. // Same address but different set ID should create a new advertised device.
if (it->getAddress() == advertisedAddress && it->getSetId() == disc.sid) { if (dev->getAddress() == advertisedAddress && dev->getSetId() == disc.sid)
# else # else
if (it->getAddress() == advertisedAddress) { if (dev->getAddress() == advertisedAddress)
# endif # endif
advertisedDevice = it; {
advertisedDevice = dev;
break; break;
} }
} }
// If we haven't seen this device before; create a new instance and insert it in the vector. // 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. // Otherwise just update the relevant parameters of the already known device.
if (advertisedDevice == nullptr && if (advertisedDevice == nullptr) {
(!isLegacyAdv || event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) {
// Check if we have reach the scan results limit, ignore this one if so. // 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 // 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 && 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; 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); 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()); 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 { } else {
// Scan response from unknown device advertisedDevice->update(event, event_type);
return 0; 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_pScanCallbacks) { if (!advertisedDevice->m_callbackSent) {
if (advertisedDevice->m_callbackSent == 0 || !pScan->m_scan_params.filter_duplicates) {
advertisedDevice->m_callbackSent = 1;
pScan->m_pScanCallbacks->onDiscovered(advertisedDevice); pScan->m_pScanCallbacks->onDiscovered(advertisedDevice);
} advertisedDevice->m_callbackSent++;
if (pScan->m_scan_params.filter_duplicates && advertisedDevice->m_callbackSent >= 2) {
return 0;
} }
// If not active scanning or scan response is not available // If not active scanning or scan response is not available
// or extended advertisement scanning, report the result to the callback now. // or extended advertisement scanning, report the result to the callback now.
if(pScan->m_scan_params.passive || !isLegacyAdv || if (pScan->m_scanParams.passive || !isLegacyAdv || !advertisedDevice->isScannable()) {
(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); 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) { } else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = 2; // got the scan response report the full data.
pScan->m_pScanCallbacks->onResult(advertisedDevice); pScan->m_pScanCallbacks->onResult(advertisedDevice);
advertisedDevice->m_callbackSent++;
} }
// If not storing results and we have invoked the callback, delete the device. // If not storing results and we have invoked the callback, delete the device.
if (pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent >= 2) { if (pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent >= 2) {
pScan->erase(advertisedAddress); pScan->erase(advertisedDevice);
}
} }
return 0; return 0;
} }
case BLE_GAP_EVENT_DISC_COMPLETE: { case BLE_GAP_EVENT_DISC_COMPLETE: {
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason);
event->disc_complete.reason);
if (pScan->m_maxResults == 0) { if (pScan->m_maxResults == 0) {
pScan->clearResults(); pScan->clearResults();
} }
if (pScan->m_pScanCallbacks != nullptr) { pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults);
}
if (pScan->m_pTaskData != nullptr) { if (pScan->m_pTaskData != nullptr) {
NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason); NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
@ -176,8 +157,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
default: default:
return 0; return 0;
} }
} // gapEventHandler } // handleGapEvent
/** /**
* @brief Should we perform an active or passive scan? * @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. * @param [in] active If true, we perform an active scan otherwise a passive scan.
*/ */
void NimBLEScan::setActiveScan(bool active) { void NimBLEScan::setActiveScan(bool active) {
m_scan_params.passive = !active; m_scanParams.passive = !active;
} // setActiveScan } // setActiveScan
/** /**
* @brief Set whether or not the BLE controller should only report results * @brief Set whether or not the BLE controller should only report results
* from devices it has not already seen. * from devices it has not already seen.
@ -197,20 +176,18 @@ void NimBLEScan::setActiveScan(bool active) {
* duplicate devices once the limit is reached. * duplicate devices once the limit is reached.
*/ */
void NimBLEScan::setDuplicateFilter(bool enabled) { void NimBLEScan::setDuplicateFilter(bool enabled) {
m_scan_params.filter_duplicates = enabled; m_scanParams.filter_duplicates = enabled;
} // setDuplicateFilter } // setDuplicateFilter
/** /**
* @brief Set whether or not the BLE controller only report scan results * @brief Set whether or not the BLE controller only reports scan results
* from devices advertising in limited discovery mode, i.e. directed advertising. * from devices advertising in limited discovery mode.
* @param [in] enabled If true, only limited discovery devices will be in scan results. * @param [in] enabled If true, only limited discovery devices will be in scan results.
*/ */
void NimBLEScan::setLimitedOnly(bool enabled) { void NimBLEScan::setLimitedOnly(bool enabled) {
m_scan_params.limited = enabled; m_scanParams.limited = enabled;
} // setLimited } // setLimited
/** /**
* @brief Sets the scan filter policy. * @brief Sets the scan filter policy.
* @param [in] filter Can be one of: * @param [in] filter Can be one of:
@ -230,10 +207,9 @@ void NimBLEScan::setLimitedOnly(bool enabled) {
* resolvable private address. * resolvable private address.
*/ */
void NimBLEScan::setFilterPolicy(uint8_t filter) { void NimBLEScan::setFilterPolicy(uint8_t filter) {
m_scan_params.filter_policy = filter; m_scanParams.filter_policy = filter;
} // setFilterPolicy } // setFilterPolicy
/** /**
* @brief Sets the max number of results to store. * @brief Sets the max number of results to store.
* @param [in] maxResults The number of results to limit storage to\n * @param [in] maxResults The number of results to limit storage to\n
@ -243,36 +219,37 @@ void NimBLEScan::setMaxResults(uint8_t maxResults) {
m_maxResults = maxResults; m_maxResults = maxResults;
} }
/** /**
* @brief Set the call backs to be invoked. * @brief Set the call backs to be invoked.
* @param [in] pScanCallbacks 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.
* @param [in] deleteCallbacks True if we should delete the callbacks when the scan object is deleted, default: false.
*/ */
void NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates) { void NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates) {
setDuplicateFilter(!wantDuplicates); setDuplicateFilter(!wantDuplicates);
if (pScanCallbacks == nullptr) {
m_pScanCallbacks = &defaultScanCallbacks;
return;
}
m_pScanCallbacks = pScanCallbacks; m_pScanCallbacks = pScanCallbacks;
} // setScanCallbacks } // setScanCallbacks
/** /**
* @brief Set the interval to scan. * @brief Set the interval to scan.
* @param [in] intervalMSecs The scan interval (how often) in milliseconds. * @param [in] intervalMSecs The scan interval (how often) in milliseconds.
*/ */
void NimBLEScan::setInterval(uint16_t intervalMSecs) { void NimBLEScan::setInterval(uint16_t intervalMSecs) {
m_scan_params.itvl = intervalMSecs / 0.625; m_scanParams.itvl = (intervalMSecs * 16) / 10;
} // setInterval } // setInterval
/** /**
* @brief Set the window to actively scan. * @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) { void NimBLEScan::setWindow(uint16_t windowMSecs) {
m_scan_params.window = windowMSecs / 0.625; m_scanParams.window = (windowMSecs * 16) / 10;
} // setWindow } // setWindow
/** /**
* @brief Get the status of the scanner. * @brief Get the status of the scanner.
* @return true if scanning or scan starting. * @return true if scanning or scan starting.
@ -281,14 +258,15 @@ bool NimBLEScan::isScanning() {
return ble_gap_disc_active(); return ble_gap_disc_active();
} }
/** /**
* @brief Start scanning. * @brief Start scanning.
* @param [in] duration The duration in milliseconds for which to scan. 0 == scan forever. * @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. * @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); NIMBLE_LOGD(LOG_TAG, ">> start: duration=%" PRIu32, duration);
if (ble_gap_conn_active()) { if (ble_gap_conn_active()) {
@ -296,6 +274,22 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) {
return false; 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. // Save the duration in the case that the host is reset so we can reuse it.
m_duration = duration; m_duration = duration;
@ -304,43 +298,28 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) {
duration = BLE_HS_FOREVER; 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 # if CONFIG_BT_NIMBLE_EXT_ADV
ble_gap_ext_disc_params scan_params; ble_gap_ext_disc_params scan_params;
scan_params.passive = m_scan_params.passive; scan_params.passive = m_scanParams.passive;
scan_params.itvl = m_scan_params.itvl; scan_params.itvl = m_scanParams.itvl;
scan_params.window = m_scan_params.window; scan_params.window = m_scanParams.window;
int rc = ble_gap_ext_disc(NimBLEDevice::m_ownAddrType, int rc = ble_gap_ext_disc(NimBLEDevice::m_ownAddrType,
duration / 10, duration / 10,
0, 0,
m_scan_params.filter_duplicates, m_scanParams.filter_duplicates,
m_scan_params.filter_policy, m_scanParams.filter_policy,
m_scan_params.limited, m_scanParams.limited,
&scan_params, &scan_params,
&scan_params, &scan_params,
NimBLEScan::handleGapEvent, NimBLEScan::handleGapEvent,
NULL); NULL);
# else # else
int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType, int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType, duration, &m_scanParams, NimBLEScan::handleGapEvent, NULL);
duration,
&m_scan_params,
NimBLEScan::handleGapEvent,
NULL);
# endif # endif
switch (rc) { switch (rc) {
case 0: case 0:
if(!is_continue) {
clearResults();
}
break;
case BLE_HS_EALREADY: case BLE_HS_EALREADY:
// Clear the cache if already scanning in case an advertiser was missed. NIMBLE_LOGD(LOG_TAG, "Scan started");
clearDuplicateCache();
break; break;
case BLE_HS_EBUSY: case BLE_HS_EBUSY:
@ -355,21 +334,14 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) {
break; break;
default: default:
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", NIMBLE_LOGE(LOG_TAG, "Error starting scan; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
rc, NimBLEUtils::returnCodeToString(rc));
break; break;
} }
m_ignoreResults = false;
NIMBLE_LOGD(LOG_TAG, "<< start()"); NIMBLE_LOGD(LOG_TAG, "<< start()");
return rc == 0 || rc == BLE_HS_EALREADY;
if(rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
return true;
} // start } // start
/** /**
* @brief Stop an in progress scan. * @brief Stop an in progress scan.
* @return True if successful. * @return True if successful.
@ -387,10 +359,6 @@ bool NimBLEScan::stop() {
clearResults(); 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); NimBLEUtils::taskRelease(*m_pTaskData);
} }
@ -399,56 +367,46 @@ bool NimBLEScan::stop() {
return true; return true;
} // stop } // 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. * @brief Delete peer device from the scan results vector.
* @param [in] address The address of the device to delete from the results. * @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()); NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str());
for (auto it = m_scanResults.m_deviceVec.begin(); it != m_scanResults.m_deviceVec.end(); ++it) {
for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) {
if ((*it)->getAddress() == address) { if ((*it)->getAddress() == address) {
delete *it; delete *it;
m_scanResults.m_advertisedDevicesVector.erase(it); m_scanResults.m_deviceVec.erase(it);
break; 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() { void NimBLEScan::erase(const NimBLEAdvertisedDevice* device) {
m_ignoreResults = true; 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. * @brief If the host reset and re-synced this is called.
* If the application was scanning indefinitely with a callback, restart it. * If the application was scanning indefinitely with a callback, restart it.
*/ */
void NimBLEScan::onHostSync() { void NimBLEScan::onHostSync() {
m_ignoreResults = false; if (m_duration == 0 && m_pScanCallbacks != &defaultScanCallbacks) {
if(m_duration == 0 && m_pScanCallbacks != nullptr) {
start(0, false); start(0, false);
} }
} }
/** /**
* @brief Start scanning and block until scanning has been completed. * @brief Start scanning and block until scanning has been completed.
* @param [in] duration The duration in milliseconds for which to scan. * @param [in] duration The duration in milliseconds for which to scan.
@ -460,6 +418,11 @@ NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) {
NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever"); 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; NimBLETaskData taskData;
m_pTaskData = &taskData; m_pTaskData = &taskData;
@ -471,7 +434,6 @@ NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) {
return m_scanResults; return m_scanResults;
} // getResults } // getResults
/** /**
* @brief Get the results of the scan. * @brief Get the results of the scan.
* @return NimBLEScanResults object. * @return NimBLEScanResults object.
@ -480,82 +442,87 @@ NimBLEScanResults NimBLEScan::getResults() {
return m_scanResults; return m_scanResults;
} }
/** /**
* @brief Clear the results of the scan. * @brief Clear the stored results of the scan.
*/ */
void NimBLEScan::clearResults() { void NimBLEScan::clearResults() {
for(auto &it: m_scanResults.m_advertisedDevicesVector) { for (const auto& dev : m_scanResults.m_deviceVec) {
delete it; delete dev;
} }
m_scanResults.m_advertisedDevicesVector.clear(); std::vector<NimBLEAdvertisedDevice*>().swap(m_scanResults.m_deviceVec);
clearDuplicateCache(); } // clearResults
}
/** /**
* @brief Dump the scan results to the log. * @brief Dump the scan results to the log.
*/ */
void NimBLEScanResults::dump() { void NimBLEScanResults::dump() const {
NIMBLE_LOGD(LOG_TAG, ">> Dump scan results:"); for (const auto& dev : m_deviceVec) {
for (int i=0; i<getCount(); i++) { NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
NIMBLE_LOGI(LOG_TAG, "- %s", getDevice(i).toString().c_str());
} }
} // dump } // dump
/** /**
* @brief Get the count of devices found in the last scan. * @brief Get the count of devices found in the last scan.
* @return The number of devices found in the last scan. * @return The number of devices found in the last scan.
*/ */
int NimBLEScanResults::getCount() { int NimBLEScanResults::getCount() const {
return m_advertisedDevicesVector.size(); return m_deviceVec.size();
} // getCount } // getCount
/** /**
* @brief Return the specified device at the given index. * @brief Return the specified device at the given index.
* The index should be between 0 and getCount()-1. * 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. * @return The device at the specified index.
*/ */
NimBLEAdvertisedDevice NimBLEScanResults::getDevice(uint32_t i) { const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(uint32_t idx) const {
return *m_advertisedDevicesVector[i]; return m_deviceVec[idx];
} }
/** /**
* @brief Get iterator to the beginning of the vector of advertised device pointers. * @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. * @return An iterator to the beginning of the vector of advertised device pointers.
*/ */
std::vector<NimBLEAdvertisedDevice*>::iterator NimBLEScanResults::begin() { std::vector<NimBLEAdvertisedDevice*>::const_iterator NimBLEScanResults::begin() const {
return m_advertisedDevicesVector.begin(); return m_deviceVec.begin();
} }
/** /**
* @brief Get iterator to the end of the vector of advertised device pointers. * @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. * @return An iterator to the end of the vector of advertised device pointers.
*/ */
std::vector<NimBLEAdvertisedDevice*>::iterator NimBLEScanResults::end() { std::vector<NimBLEAdvertisedDevice*>::const_iterator NimBLEScanResults::end() const {
return m_advertisedDevicesVector.end(); return m_deviceVec.end();
} }
/** /**
* @brief Get a pointer to the specified device at the given address. * @brief Get a pointer to the specified device at the given address.
* If the address is not found a nullptr is returned. * If the address is not found a nullptr is returned.
* @param [in] address The address of the device. * @param [in] address The address of the device.
* @return A pointer to the device at the specified address. * @return A pointer to the device at the specified address.
*/ */
NimBLEAdvertisedDevice *NimBLEScanResults::getDevice(const NimBLEAddress &address) { const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(const NimBLEAddress& address) const {
for(size_t index = 0; index < m_advertisedDevicesVector.size(); index++) { for (const auto& dev : m_deviceVec) {
if(m_advertisedDevicesVector[index]->getAddress() == address) { if (dev->getAddress() == address) {
return m_advertisedDevicesVector[index]; return dev;
} }
} }
return nullptr; 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 */ #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */

View file

@ -43,16 +43,16 @@ class NimBLEAddress;
*/ */
class NimBLEScanResults { class NimBLEScanResults {
public: public:
void dump(); void dump() const;
int getCount(); int getCount() const;
NimBLEAdvertisedDevice getDevice(uint32_t i); const NimBLEAdvertisedDevice* getDevice(uint32_t idx) const;
std::vector<NimBLEAdvertisedDevice*>::iterator begin(); const NimBLEAdvertisedDevice* getDevice(const NimBLEAddress& address) const;
std::vector<NimBLEAdvertisedDevice*>::iterator end(); std::vector<NimBLEAdvertisedDevice*>::const_iterator begin() const;
NimBLEAdvertisedDevice *getDevice(const NimBLEAddress &address); std::vector<NimBLEAdvertisedDevice*>::const_iterator end() const;
private: private:
friend NimBLEScan; friend NimBLEScan;
std::vector<NimBLEAdvertisedDevice*> m_advertisedDevicesVector; std::vector<NimBLEAdvertisedDevice*> m_deviceVec;
}; };
/** /**
@ -62,7 +62,7 @@ private:
*/ */
class NimBLEScan { class NimBLEScan {
public: public:
bool start(uint32_t duration, bool is_continue = false); bool start(uint32_t duration, bool isContinue = false, bool restart = true);
bool isScanning(); bool isScanning();
void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false); void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false);
void setActiveScan(bool active); void setActiveScan(bool active);
@ -71,14 +71,13 @@ public:
void setDuplicateFilter(bool enabled); void setDuplicateFilter(bool enabled);
void setLimitedOnly(bool enabled); void setLimitedOnly(bool enabled);
void setFilterPolicy(uint8_t filter); void setFilterPolicy(uint8_t filter);
void clearDuplicateCache();
bool stop(); bool stop();
void clearResults(); void clearResults();
NimBLEScanResults getResults(); NimBLEScanResults getResults();
NimBLEScanResults getResults(uint32_t duration, bool is_continue = false); NimBLEScanResults getResults(uint32_t duration, bool is_continue = false);
void setMaxResults(uint8_t maxResults); void setMaxResults(uint8_t maxResults);
void erase(const NimBLEAddress& address); void erase(const NimBLEAddress& address);
void erase(const NimBLEAdvertisedDevice* device);
private: private:
friend class NimBLEDevice; friend class NimBLEDevice;
@ -86,12 +85,10 @@ private:
NimBLEScan(); NimBLEScan();
~NimBLEScan(); ~NimBLEScan();
static int handleGapEvent(ble_gap_event* event, void* arg); static int handleGapEvent(ble_gap_event* event, void* arg);
void onHostReset();
void onHostSync(); void onHostSync();
NimBLEScanCallbacks* m_pScanCallbacks; NimBLEScanCallbacks* m_pScanCallbacks;
ble_gap_disc_params m_scan_params; ble_gap_disc_params m_scanParams;
bool m_ignoreResults;
NimBLEScanResults m_scanResults; NimBLEScanResults m_scanResults;
uint32_t m_duration; uint32_t m_duration;
NimBLETaskData* m_pTaskData; NimBLETaskData* m_pTaskData;
@ -109,19 +106,20 @@ public:
* @brief Called when a new device is discovered, before the scan result is received (if applicable). * @brief Called when a new device is discovered, before the scan result is received (if applicable).
* @param [in] advertisedDevice The device which was discovered. * @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). * @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. * @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. * @brief Called when a scan operation ends.
* @param [in] scanResults The results of the scan that ended. * @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 */ #endif /* CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER */