Skip to content

Commit b8438ac

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 117ec4e commit b8438ac

File tree

4 files changed

+125
-6
lines changed

4 files changed

+125
-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

+103-5
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,85 @@
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{}, .rssi = 127, .data = nullptr, .direct_addr{}};
43+
44+
extern "C" void ble_gap_rx_adv_report(ble_gap_disc_desc* desc);
45+
46+
/**
47+
* @brief Sends dummy (null) scan response data to the scan event handler in order to
48+
* provide the scan result to the callbacks when a device hasn't responded to the
49+
* scan request in time. This is called by the host task from the default event queue.
50+
*/
51+
static void sendDummyScanResponse(ble_npl_event* ev) {
52+
(void)ev;
53+
ble_gap_rx_adv_report(&dummyDesc);
54+
}
55+
56+
/**
57+
* @brief This will schedule an event to run in the host task that will call sendDummyScanResponse
58+
* which will send a null data scan response to the scan event handler if the device
59+
* hasn't responded to a scan response request within the timeout period.
60+
*/
61+
void NimBLEScan::srTimerCb(ble_npl_event* event) {
62+
NimBLEScan* pScan = (NimBLEScan*)ble_npl_event_get_arg(event);
63+
NimBLEAdvertisedDevice* curDev = nullptr;
64+
NimBLEAdvertisedDevice* nextDev = nullptr;
65+
ble_npl_time_t now = ble_npl_time_get();
66+
67+
for (auto& dev : pScan->m_scanResults.m_deviceVec) {
68+
if (dev->m_callbackSent < 2 && dev->isScannable()) {
69+
if (!curDev || (now - dev->m_time > now - curDev->m_time)) {
70+
nextDev = curDev;
71+
curDev = dev;
72+
continue;
73+
}
74+
75+
if (!nextDev || now - dev->m_time > now - nextDev->m_time) {
76+
nextDev = dev;
77+
}
78+
}
79+
}
80+
81+
// Add the event to the host queue
82+
if (curDev) {
83+
memcpy(&dummyDesc.addr, curDev->getAddress().getBase(), sizeof(dummyDesc.addr));
84+
NIMBLE_LOGI(LOG_TAG, "Scan response timeout for: %s", curDev->getAddress().toString().c_str());
85+
ble_npl_eventq_put(nimble_port_get_dflt_eventq(), &dummySrTimerEvent);
86+
}
87+
88+
// Restart the timer for the next device that we are expecting a scan response from
89+
if (nextDev) {
90+
auto nextTime = now - nextDev->m_time;
91+
if (nextTime >= SR_TIMEOUT) {
92+
nextTime = 1;
93+
} else {
94+
nextTime = SR_TIMEOUT - nextTime;
95+
}
96+
97+
ble_npl_time_t ticks;
98+
ble_npl_time_ms_to_ticks(nextTime, &ticks);
99+
ble_npl_callout_reset(&pScan->m_srTimer, ticks);
100+
}
101+
}
102+
# endif
103+
31104
/**
32105
* @brief Scan constructor.
33106
*/
@@ -36,13 +109,22 @@ NimBLEScan::NimBLEScan()
36109
// default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
37110
m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
38111
m_pTaskData{nullptr},
39-
m_maxResults{0xFF} {}
112+
m_maxResults{0xFF} {
113+
# if SR_TIMEOUT
114+
ble_npl_callout_init(&m_srTimer, nimble_port_get_dflt_eventq(), NimBLEScan::srTimerCb, this);
115+
ble_npl_event_init(&dummySrTimerEvent, sendDummyScanResponse, NULL);
116+
# endif
117+
}
40118

41119
/**
42120
* @brief Scan destructor, release any allocated resources.
43121
*/
44122
NimBLEScan::~NimBLEScan() {
45123
clearResults();
124+
# if SR_TIMEOUT
125+
ble_npl_callout_deinit(&m_srTimer);
126+
ble_npl_event_deinit(&dummySrTimerEvent);
127+
# endif
46128
}
47129

48130
/**
@@ -109,6 +191,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
109191
advertisedDevice = new NimBLEAdvertisedDevice(event, event_type);
110192
pScan->m_scanResults.m_deviceVec.push_back(advertisedDevice);
111193
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
194+
# if SR_TIMEOUT
195+
advertisedDevice->m_time = ble_npl_time_get();
196+
# endif
112197
} else {
113198
advertisedDevice->update(event, event_type);
114199
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
@@ -132,6 +217,13 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
132217
advertisedDevice->m_callbackSent++;
133218
// got the scan response report the full data.
134219
pScan->m_pScanCallbacks->onResult(advertisedDevice);
220+
# if SR_TIMEOUT
221+
} else if (isLegacyAdv && !ble_npl_callout_is_active(&pScan->m_srTimer)) {
222+
// Start the timer to wait for the scan response.
223+
ble_npl_time_t ticks;
224+
ble_npl_time_ms_to_ticks(SR_TIMEOUT, &ticks);
225+
ble_npl_callout_reset(&pScan->m_srTimer, ticks);
226+
# endif
135227
}
136228

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

145237
case BLE_GAP_EVENT_DISC_COMPLETE: {
146238
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason);
239+
# if SR_TIMEOUT
240+
ble_npl_callout_stop(&pScan->m_srTimer);
241+
# endif
242+
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
147243

148244
if (pScan->m_maxResults == 0) {
149245
pScan->clearResults();
150246
}
151247

152-
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
153-
154248
if (pScan->m_pTaskData != nullptr) {
155249
NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
156250
}
@@ -379,6 +473,10 @@ bool NimBLEScan::stop() {
379473
return false;
380474
}
381475

476+
# if SR_TIMEOUT
477+
ble_npl_callout_stop(&m_srTimer);
478+
# endif
479+
382480
if (m_maxResults == 0) {
383481
clearResults();
384482
}
@@ -483,11 +581,11 @@ void NimBLEScan::clearResults() {
483581
* @brief Dump the scan results to the log.
484582
*/
485583
void NimBLEScanResults::dump() const {
486-
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >=3
584+
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
487585
for (const auto& dev : m_deviceVec) {
488586
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
489587
}
490-
#endif
588+
# endif
491589
} // dump
492590

493591
/**

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)