Skip to content

Commit 64bae68

Browse files
committed
Add a timer for scan responses
This ensures that the callback will be called within the configured time (in ms) when devices fail to respond to a scan response request within that time.
1 parent a0a4cdb commit 64bae68

File tree

4 files changed

+117
-6
lines changed

4 files changed

+117
-6
lines changed

src/NimBLEAdvertisedDevice.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ class NimBLEAdvertisedDevice {
162162
int8_t m_rssi{};
163163
uint8_t m_callbackSent{};
164164
uint8_t m_advLength{};
165-
165+
# if CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT
166+
ble_npl_time_t m_time{};
167+
# endif
166168
# if CONFIG_BT_NIMBLE_EXT_ADV
167169
bool m_isLegacyAdv{};
168170
uint8_t m_sid{};

src/NimBLEScan.cpp

+95-5
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,77 @@
2222
# include "NimBLEDevice.h"
2323
# include "NimBLELog.h"
2424

25+
# if defined(CONFIG_NIMBLE_CPP_IDF)
26+
# include "nimble/nimble_port.h"
27+
# else
28+
# include "nimble/porting/nimble/include/nimble/nimble_port.h"
29+
# endif
30+
2531
# include <string>
2632
# include <climits>
2733

34+
# define SR_TIMEOUT CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT
35+
2836
static const char* LOG_TAG = "NimBLEScan";
2937
static NimBLEScanCallbacks defaultScanCallbacks;
3038

39+
# if SR_TIMEOUT
40+
static ble_npl_event dummySrTimerEvent;
41+
static ble_gap_disc_desc dummyDesc{
42+
.event_type = BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP, .length_data = 0, .addr{0}, .rssi = 127, .data = nullptr, .direct_addr{0}};
43+
44+
extern "C" void ble_gap_rx_adv_report(ble_gap_disc_desc* desc);
45+
46+
static void sendDummyScanResponse(ble_npl_event* ev) {
47+
(void)ev;
48+
ble_gap_rx_adv_report(&dummyDesc);
49+
}
50+
51+
/**
52+
* @brief This will call the scan result callback when a scan response is not received
53+
* and will delete the device from the vector when maxResults is 0.
54+
*/
55+
void NimBLEScan::srTimerCb(ble_npl_event* event) {
56+
NimBLEScan* pScan = (NimBLEScan*)ble_npl_event_get_arg(event);
57+
ble_npl_time_t now = ble_npl_time_get();
58+
NimBLEAdvertisedDevice* curDev = nullptr;
59+
NimBLEAdvertisedDevice* nextDev = nullptr;
60+
61+
for (auto& dev : pScan->m_scanResults.m_deviceVec) {
62+
if (dev->m_callbackSent < 2 && dev->isScannable()) {
63+
if (!curDev || (now - dev->m_time > now - curDev->m_time)) {
64+
nextDev = curDev;
65+
curDev = dev;
66+
continue;
67+
}
68+
69+
if (!nextDev || now - dev->m_time > now - nextDev->m_time) {
70+
nextDev = dev;
71+
}
72+
}
73+
}
74+
75+
if (curDev) {
76+
memcpy(&dummyDesc.addr, curDev->getAddress().getBase(), sizeof(dummyDesc.addr));
77+
NIMBLE_LOGI(LOG_TAG, "Scan response timeout for: %s", curDev->getAddress().toString().c_str());
78+
ble_npl_eventq_put(nimble_port_get_dflt_eventq(), &dummySrTimerEvent);
79+
}
80+
81+
// Restart timer if there are more devices waiting for scan responses.
82+
if (nextDev) {
83+
auto nextTime = now - nextDev->m_time;
84+
if (nextTime >= SR_TIMEOUT) {
85+
nextTime = 1;
86+
} else {
87+
nextTime = SR_TIMEOUT - nextTime;
88+
}
89+
ble_npl_time_t ticks;
90+
ble_npl_time_ms_to_ticks(nextTime, &ticks);
91+
ble_npl_callout_reset(&pScan->m_srTimer, ticks);
92+
}
93+
}
94+
# endif
95+
3196
/**
3297
* @brief Scan constructor.
3398
*/
@@ -36,13 +101,22 @@ NimBLEScan::NimBLEScan()
36101
// default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
37102
m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
38103
m_pTaskData{nullptr},
39-
m_maxResults{0xFF} {}
104+
m_maxResults{0xFF} {
105+
# if SR_TIMEOUT
106+
ble_npl_callout_init(&m_srTimer, nimble_port_get_dflt_eventq(), NimBLEScan::srTimerCb, this);
107+
ble_npl_event_init(&dummySrTimerEvent, sendDummyScanResponse, NULL);
108+
# endif
109+
}
40110

41111
/**
42112
* @brief Scan destructor, release any allocated resources.
43113
*/
44114
NimBLEScan::~NimBLEScan() {
45115
clearResults();
116+
# if SR_TIMEOUT
117+
ble_npl_callout_deinit(&m_srTimer);
118+
ble_npl_event_deinit(&dummySrTimerEvent);
119+
# endif
46120
}
47121

48122
/**
@@ -109,6 +183,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
109183
advertisedDevice = new NimBLEAdvertisedDevice(event, event_type);
110184
pScan->m_scanResults.m_deviceVec.push_back(advertisedDevice);
111185
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
186+
# if SR_TIMEOUT
187+
advertisedDevice->m_time = ble_npl_time_get();
188+
# endif
112189
} else {
113190
advertisedDevice->update(event, event_type);
114191
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
@@ -132,6 +209,13 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
132209
advertisedDevice->m_callbackSent++;
133210
// got the scan response report the full data.
134211
pScan->m_pScanCallbacks->onResult(advertisedDevice);
212+
# if SR_TIMEOUT
213+
} else if (isLegacyAdv && !ble_npl_callout_is_active(&pScan->m_srTimer)) {
214+
// Start the timer to wait for the scan response.
215+
ble_npl_time_t ticks;
216+
ble_npl_time_ms_to_ticks(SR_TIMEOUT, &ticks);
217+
ble_npl_callout_reset(&pScan->m_srTimer, ticks);
218+
# endif
135219
}
136220

137221
// If not storing results and we have invoked the callback, delete the device.
@@ -144,13 +228,15 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
144228

145229
case BLE_GAP_EVENT_DISC_COMPLETE: {
146230
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason);
231+
# if SR_TIMEOUT
232+
ble_npl_callout_stop(&pScan->m_srTimer);
233+
# endif
234+
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
147235

148236
if (pScan->m_maxResults == 0) {
149237
pScan->clearResults();
150238
}
151239

152-
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
153-
154240
if (pScan->m_pTaskData != nullptr) {
155241
NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
156242
}
@@ -379,6 +465,10 @@ bool NimBLEScan::stop() {
379465
return false;
380466
}
381467

468+
# if SR_TIMEOUT
469+
ble_npl_callout_stop(&m_srTimer);
470+
# endif
471+
382472
if (m_maxResults == 0) {
383473
clearResults();
384474
}
@@ -483,11 +573,11 @@ void NimBLEScan::clearResults() {
483573
* @brief Dump the scan results to the log.
484574
*/
485575
void NimBLEScanResults::dump() const {
486-
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >=3
576+
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
487577
for (const auto& dev : m_deviceVec) {
488578
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
489579
}
490-
#endif
580+
# endif
491581
} // dump
492582

493583
/**

src/NimBLEScan.h

+7
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,18 @@ class NimBLEScan {
9797
static int handleGapEvent(ble_gap_event* event, void* arg);
9898
void onHostSync();
9999

100+
# if CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT
101+
static void srTimerCb(ble_npl_event* event);
102+
# endif
103+
100104
NimBLEScanCallbacks* m_pScanCallbacks;
101105
ble_gap_disc_params m_scanParams;
102106
NimBLEScanResults m_scanResults;
103107
NimBLETaskData* m_pTaskData;
104108
uint8_t m_maxResults;
109+
# if CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT
110+
ble_npl_callout m_srTimer;
111+
# endif
105112

106113
# if CONFIG_BT_NIMBLE_EXT_ADV
107114
uint8_t m_phy{SCAN_ALL};

src/nimconfig.h

+12
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@
176176
*/
177177
// #define CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS 1
178178

179+
/**
180+
* @brief Un-comment to change the timeout, in milliseconds.
181+
* @details This is the time to wait for a scan response before calling the onResult scan callback,
182+
* lower values increase callback rates but will lose data more often, higher values give full data more
183+
* often. Setting this to 0 will disable the scan response timeout.
184+
*/
185+
// #define CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT (30)
186+
179187
/**********************************
180188
End Arduino user-config
181189
**********************************/
@@ -255,6 +263,10 @@
255263
#define CONFIG_NIMBLE_STACK_USE_MEM_POOLS 0
256264
#endif
257265

266+
#ifndef CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT
267+
#define CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT (30)
268+
#endif
269+
258270
/** @brief Set if CCCD's and bond data should be stored in NVS */
259271
#define CONFIG_BT_NIMBLE_NVS_PERSIST 1
260272

0 commit comments

Comments
 (0)