Skip to content

Commit f47363e

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 f47363e

File tree

4 files changed

+123
-6
lines changed

4 files changed

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

+101-5
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,83 @@
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+
/**
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 in to run in the host task that will send
58+
* dummy (null) data to the scan event handler as a scan response 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+
if (curDev) {
82+
memcpy(&dummyDesc.addr, curDev->getAddress().getBase(), sizeof(dummyDesc.addr));
83+
NIMBLE_LOGI(LOG_TAG, "Scan response timeout for: %s", curDev->getAddress().toString().c_str());
84+
ble_npl_eventq_put(nimble_port_get_dflt_eventq(), &dummySrTimerEvent);
85+
}
86+
87+
// Restart timer if there are more devices waiting for scan responses.
88+
if (nextDev) {
89+
auto nextTime = now - nextDev->m_time;
90+
if (nextTime >= SR_TIMEOUT) {
91+
nextTime = 1;
92+
} else {
93+
nextTime = SR_TIMEOUT - nextTime;
94+
}
95+
ble_npl_time_t ticks;
96+
ble_npl_time_ms_to_ticks(nextTime, &ticks);
97+
ble_npl_callout_reset(&pScan->m_srTimer, ticks);
98+
}
99+
}
100+
# endif
101+
31102
/**
32103
* @brief Scan constructor.
33104
*/
@@ -36,13 +107,22 @@ NimBLEScan::NimBLEScan()
36107
// default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
37108
m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
38109
m_pTaskData{nullptr},
39-
m_maxResults{0xFF} {}
110+
m_maxResults{0xFF} {
111+
# if SR_TIMEOUT
112+
ble_npl_callout_init(&m_srTimer, nimble_port_get_dflt_eventq(), NimBLEScan::srTimerCb, this);
113+
ble_npl_event_init(&dummySrTimerEvent, sendDummyScanResponse, NULL);
114+
# endif
115+
}
40116

41117
/**
42118
* @brief Scan destructor, release any allocated resources.
43119
*/
44120
NimBLEScan::~NimBLEScan() {
45121
clearResults();
122+
# if SR_TIMEOUT
123+
ble_npl_callout_deinit(&m_srTimer);
124+
ble_npl_event_deinit(&dummySrTimerEvent);
125+
# endif
46126
}
47127

48128
/**
@@ -109,6 +189,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
109189
advertisedDevice = new NimBLEAdvertisedDevice(event, event_type);
110190
pScan->m_scanResults.m_deviceVec.push_back(advertisedDevice);
111191
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
192+
# if SR_TIMEOUT
193+
advertisedDevice->m_time = ble_npl_time_get();
194+
# endif
112195
} else {
113196
advertisedDevice->update(event, event_type);
114197
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
@@ -132,6 +215,13 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
132215
advertisedDevice->m_callbackSent++;
133216
// got the scan response report the full data.
134217
pScan->m_pScanCallbacks->onResult(advertisedDevice);
218+
# if SR_TIMEOUT
219+
} else if (isLegacyAdv && !ble_npl_callout_is_active(&pScan->m_srTimer)) {
220+
// Start the timer to wait for the scan response.
221+
ble_npl_time_t ticks;
222+
ble_npl_time_ms_to_ticks(SR_TIMEOUT, &ticks);
223+
ble_npl_callout_reset(&pScan->m_srTimer, ticks);
224+
# endif
135225
}
136226

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

145235
case BLE_GAP_EVENT_DISC_COMPLETE: {
146236
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason);
237+
# if SR_TIMEOUT
238+
ble_npl_callout_stop(&pScan->m_srTimer);
239+
# endif
240+
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
147241

148242
if (pScan->m_maxResults == 0) {
149243
pScan->clearResults();
150244
}
151245

152-
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
153-
154246
if (pScan->m_pTaskData != nullptr) {
155247
NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
156248
}
@@ -379,6 +471,10 @@ bool NimBLEScan::stop() {
379471
return false;
380472
}
381473

474+
# if SR_TIMEOUT
475+
ble_npl_callout_stop(&m_srTimer);
476+
# endif
477+
382478
if (m_maxResults == 0) {
383479
clearResults();
384480
}
@@ -483,11 +579,11 @@ void NimBLEScan::clearResults() {
483579
* @brief Dump the scan results to the log.
484580
*/
485581
void NimBLEScanResults::dump() const {
486-
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >=3
582+
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
487583
for (const auto& dev : m_deviceVec) {
488584
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
489585
}
490-
#endif
586+
# endif
491587
} // dump
492588

493589
/**

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)