Skip to content

Commit 3cb9adb

Browse files
committed
Add extended scan features.
* Added new method `NimBLEScan::setScanPhy` to enable/disable the PHY's to scan on. * Added new method `NimBLEScan::setScanPeriod` which will allow for setting a scan restart timer in the controller. * Updated `NimBLEScan::start` to allow the command to be sent with updated parameters if already scanning. * Added extended scan example. * Removed storing and restarting of the scan on host reset as it is more appropriate to call the scanEnded callback instead.
1 parent db2fe36 commit 3cb9adb

File tree

6 files changed

+168
-48
lines changed

6 files changed

+168
-48
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.5)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
set(SUPPORTED_TARGETS esp32c3 esp32s3 esp32c6 esp32h2 esp32c2)
7+
project(NimBLE_extended_scan)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
set(COMPONENT_SRCS "main.cpp")
2+
set(COMPONENT_ADD_INCLUDEDIRS ".")
3+
4+
register_component()
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* NimBLE Extended Scanner Demo:
3+
*
4+
* Demonstrates the Bluetooth 5.x scanning capabilities of the NimBLE library.
5+
*
6+
* Created: on November 28, 2024
7+
* Author: H2zero
8+
*
9+
*/
10+
11+
#include <NimBLEDevice.h>
12+
13+
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever
14+
static NimBLEScan::Phy scanPhy = NimBLEScan::Phy::SCAN_ALL;
15+
16+
// Define a class to handle the callbacks when advertisements are received
17+
class scanCallbacks: public NimBLEScanCallbacks {
18+
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
19+
printf("Advertised Device found: %s\n PHY1: %d\n PHY2: %d\n", advertisedDevice->toString().c_str(),
20+
advertisedDevice->getPrimaryPhy(), advertisedDevice->getSecondaryPhy());
21+
}
22+
23+
// Callback to process the results of the completed scan or restart it
24+
void onScanEnd(const NimBLEScanResults& scanResults, int reason) {
25+
printf("Scan Ended, reason: %d; found %d devices\n", reason, scanResults.getCount());
26+
27+
// Try Different PHY's
28+
switch (scanPhy) {
29+
case NimBLEScan::Phy::SCAN_ALL:
30+
printf("Scanning only 1M PHY\n");
31+
scanPhy = NimBLEScan::Phy::SCAN_1M;
32+
break;
33+
case NimBLEScan::Phy::SCAN_1M:
34+
printf("Scanning only CODED PHY\n");
35+
scanPhy = NimBLEScan::Phy::SCAN_CODED;
36+
break;
37+
case NimBLEScan::Phy::SCAN_CODED:
38+
printf("Scanning all PHY's\n");
39+
scanPhy = NimBLEScan::Phy::SCAN_ALL;
40+
break;
41+
}
42+
43+
NimBLEScan* pScan = NimBLEDevice::getScan();
44+
pScan->setPhy(scanPhy);
45+
pScan->start(scanTime);
46+
}
47+
} scanCb;
48+
49+
extern "C" void app_main (void) {
50+
printf("Starting Extended Scanner\n");
51+
52+
// Initialize NimBLE, no device name specified as we are not advertising
53+
NimBLEDevice::init("");
54+
NimBLEScan* pScan = NimBLEDevice::getScan();
55+
56+
// Set the callbacks that the scanner will call on events.
57+
pScan->setScanCallbacks(&scanCb);
58+
59+
// Use active scanning to obtain scan response data from advertisers
60+
pScan->setActiveScan(true);
61+
62+
// Set the initial PHY's to scan on, default is SCAN_ALL
63+
pScan->setPhy(scanPhy);
64+
65+
// Start scanning for scanTime, 0 = forever
66+
pScan->start(scanTime);
67+
printf("Scanning for peripherals\n");
68+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Override some defaults so BT stack is enabled
2+
# in this example
3+
4+
#
5+
# BT config
6+
#
7+
CONFIG_BT_ENABLED=y
8+
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
9+
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
10+
CONFIG_BTDM_CTRL_MODE_BTDM=n
11+
CONFIG_BT_BLUEDROID_ENABLED=n
12+
CONFIG_BT_NIMBLE_ENABLED=y
13+
CONFIG_BT_NIMBLE_EXT_ADV=y

src/NimBLEScan.cpp

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ NimBLEScan::NimBLEScan()
3232
: m_pScanCallbacks{&defaultScanCallbacks},
3333
// default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
3434
m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
35-
m_duration{BLE_HS_FOREVER},
3635
m_pTaskData{nullptr},
3736
m_maxResults{0xFF} {}
3837

@@ -173,11 +172,14 @@ void NimBLEScan::setActiveScan(bool active) {
173172
/**
174173
* @brief Set whether or not the BLE controller should only report results
175174
* from devices it has not already seen.
176-
* @param [in] enabled If true, scanned devices will only be reported once.
177-
* @details The controller has a limited buffer and will start reporting
178-
* duplicate devices once the limit is reached.
175+
* @param [in] enabled If set to 1 (true), scanned devices will only be reported once.
176+
* If set to 0 duplicates will be reported each time they are seen.
177+
* If using extended scanning this can be set to 2 which will reset the duplicate filter
178+
* at the end of each scan period if the scan period is set.
179+
* @note The controller has a limited buffer and will start reporting
180+
duplicate devices once the limit is reached.
179181
*/
180-
void NimBLEScan::setDuplicateFilter(bool enabled) {
182+
void NimBLEScan::setDuplicateFilter(uint8_t enabled) {
181183
m_scanParams.filter_duplicates = enabled;
182184
} // setDuplicateFilter
183185

@@ -219,7 +221,7 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
219221
*/
220222
void NimBLEScan::setMaxResults(uint8_t maxResults) {
221223
m_maxResults = maxResults;
222-
}
224+
} // setMaxResults
223225

224226
/**
225227
* @brief Set the call backs to be invoked.
@@ -237,18 +239,20 @@ void NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool want
237239

238240
/**
239241
* @brief Set the interval to scan.
240-
* @param [in] intervalMSecs The scan interval (how often) in milliseconds.
242+
* @param [in] intervalMs The scan interval in milliseconds.
243+
* @details The interval is the time between the start of two consecutive scan windows.
244+
* When a new interval starts the controller changes the channel it's scanning on.
241245
*/
242-
void NimBLEScan::setInterval(uint16_t intervalMSecs) {
243-
m_scanParams.itvl = (intervalMSecs * 16) / 10;
246+
void NimBLEScan::setInterval(uint16_t intervalMs) {
247+
m_scanParams.itvl = (intervalMs * 16) / 10;
244248
} // setInterval
245249

246250
/**
247251
* @brief Set the window to actively scan.
248-
* @param [in] windowMSecs How long during the interval to actively scan.
252+
* @param [in] windowMs How long during the interval to actively scan in milliseconds.
249253
*/
250-
void NimBLEScan::setWindow(uint16_t windowMSecs) {
251-
m_scanParams.window = (windowMSecs * 16) / 10;
254+
void NimBLEScan::setWindow(uint16_t windowMs) {
255+
m_scanParams.window = (windowMs * 16) / 10;
252256
} // setWindow
253257

254258
/**
@@ -259,6 +263,29 @@ bool NimBLEScan::isScanning() {
259263
return ble_gap_disc_active();
260264
}
261265

266+
# if CONFIG_BT_NIMBLE_EXT_ADV
267+
/**
268+
* @brief Set the PHYs to scan.
269+
* @param [in] phyMask The PHYs to scan, a bit mask of:
270+
* * NIMBLE_CPP_SCAN_1M
271+
* * NIMBLE_CPP_SCAN_CODED
272+
*/
273+
void NimBLEScan::setPhy(Phy phyMask) {
274+
m_phy = phyMask;
275+
} // setScanPhy
276+
277+
/**
278+
* @brief Set the extended scanning period.
279+
* @param [in] periodMs The scan period in milliseconds
280+
* @details The period is the time between the start of two consecutive scan periods.
281+
* This works as a timer to restart scanning at the specified amount of time in periodMs.
282+
* @note The duration used when this is set must be less than period.
283+
*/
284+
void NimBLEScan::setPeriod(uint32_t periodMs) {
285+
m_period = (periodMs + 500) / 1280; // round up 1.28 second units
286+
} // setScanPeriod
287+
# endif
288+
262289
/**
263290
* @brief Start scanning.
264291
* @param [in] duration The duration in milliseconds for which to scan. 0 == scan forever.
@@ -269,53 +296,46 @@ bool NimBLEScan::isScanning() {
269296
*/
270297
bool NimBLEScan::start(uint32_t duration, bool isContinue, bool restart) {
271298
NIMBLE_LOGD(LOG_TAG, ">> start: duration=%" PRIu32, duration);
272-
273-
if (ble_gap_conn_active()) {
274-
NIMBLE_LOGE(LOG_TAG, "Connection in progress, cannot start scan");
275-
return false;
276-
}
277-
278299
if (isScanning()) {
279300
if (restart) {
280301
NIMBLE_LOGI(LOG_TAG, "Scan already in progress, restarting it");
281302
if (!stop()) {
282303
return false;
283304
}
284-
} else {
285-
NIMBLE_LOGI(LOG_TAG, "Scan already in progress");
286-
return true;
287-
}
288-
}
289305

290-
if (!isContinue) {
291-
clearResults();
306+
if (!isContinue) {
307+
clearResults();
308+
}
309+
}
310+
} else { // Don't clear results while scanning is active
311+
if (!isContinue) {
312+
clearResults();
313+
}
292314
}
293315

294-
// Save the duration in the case that the host is reset so we can reuse it.
295-
m_duration = duration;
296-
297-
// If 0 duration specified then we assume a continuous scan is desired.
298-
if (duration == 0) {
299-
duration = BLE_HS_FOREVER;
300-
}
316+
// If scanning is already active, call the functions anyway as the parameters can be changed.
301317

302318
# if CONFIG_BT_NIMBLE_EXT_ADV
303319
ble_gap_ext_disc_params scan_params;
304320
scan_params.passive = m_scanParams.passive;
305321
scan_params.itvl = m_scanParams.itvl;
306322
scan_params.window = m_scanParams.window;
307323
int rc = ble_gap_ext_disc(NimBLEDevice::m_ownAddrType,
308-
duration / 10,
309-
0,
324+
duration / 10, // 10ms units
325+
m_period,
310326
m_scanParams.filter_duplicates,
311327
m_scanParams.filter_policy,
312328
m_scanParams.limited,
313-
&scan_params,
314-
&scan_params,
329+
m_phy & SCAN_1M ? &scan_params : NULL,
330+
m_phy & SCAN_CODED ? &scan_params : NULL,
315331
NimBLEScan::handleGapEvent,
316332
NULL);
317333
# else
318-
int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType, duration, &m_scanParams, NimBLEScan::handleGapEvent, NULL);
334+
int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType,
335+
duration ? duration : BLE_HS_FOREVER,
336+
&m_scanParams,
337+
NimBLEScan::handleGapEvent,
338+
NULL);
319339
# endif
320340
switch (rc) {
321341
case 0:
@@ -403,9 +423,7 @@ void NimBLEScan::erase(const NimBLEAdvertisedDevice* device) {
403423
* If the application was scanning indefinitely with a callback, restart it.
404424
*/
405425
void NimBLEScan::onHostSync() {
406-
if (m_duration == 0 && m_pScanCallbacks != &defaultScanCallbacks) {
407-
start(0, false);
408-
}
426+
m_pScanCallbacks->onScanEnd(m_scanResults, BLE_HS_ENOTSYNCED);
409427
}
410428

411429
/**

src/NimBLEScan.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
* Created on: Jul 1, 2017
1212
* Author: kolban
1313
*/
14-
#ifndef COMPONENTS_NIMBLE_SCAN_H_
15-
#define COMPONENTS_NIMBLE_SCAN_H_
14+
#ifndef NIMBLE_CPP_SCAN_H_
15+
#define NIMBLE_CPP_SCAN_H_
1616

1717
#include "nimconfig.h"
1818
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
@@ -66,9 +66,9 @@ class NimBLEScan {
6666
bool isScanning();
6767
void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false);
6868
void setActiveScan(bool active);
69-
void setInterval(uint16_t intervalMSecs);
70-
void setWindow(uint16_t windowMSecs);
71-
void setDuplicateFilter(bool enabled);
69+
void setInterval(uint16_t intervalMs);
70+
void setWindow(uint16_t windowMs);
71+
void setDuplicateFilter(uint8_t enabled);
7272
void setLimitedOnly(bool enabled);
7373
void setFilterPolicy(uint8_t filter);
7474
bool stop();
@@ -79,6 +79,12 @@ class NimBLEScan {
7979
void erase(const NimBLEAddress& address);
8080
void erase(const NimBLEAdvertisedDevice* device);
8181

82+
# if CONFIG_BT_NIMBLE_EXT_ADV
83+
enum Phy { SCAN_1M = 0x01, SCAN_CODED = 0x02, SCAN_ALL = 0x03 };
84+
void setPhy(Phy phyMask);
85+
void setPeriod(uint32_t periodMs);
86+
# endif
87+
8288
private:
8389
friend class NimBLEDevice;
8490

@@ -90,9 +96,13 @@ class NimBLEScan {
9096
NimBLEScanCallbacks* m_pScanCallbacks;
9197
ble_gap_disc_params m_scanParams;
9298
NimBLEScanResults m_scanResults;
93-
uint32_t m_duration;
9499
NimBLETaskData* m_pTaskData;
95100
uint8_t m_maxResults;
101+
102+
# if CONFIG_BT_NIMBLE_EXT_ADV
103+
uint8_t m_phy{SCAN_ALL};
104+
uint16_t m_period{0};
105+
# endif
96106
};
97107

98108
/**
@@ -122,5 +132,5 @@ class NimBLEScanCallbacks {
122132
virtual void onScanEnd(const NimBLEScanResults& scanResults, int reason);
123133
};
124134

125-
#endif /* CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER */
126-
#endif /* COMPONENTS_NIMBLE_SCAN_H_ */
135+
#endif // CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER
136+
#endif // NIMBLE_CPP_SCAN_H_

0 commit comments

Comments
 (0)