Skip to content

Commit 4d5fcea

Browse files
committed
Set Client connect/disconnect data before stack calls.
Connect should set m_pTaskData before calling ble_gap_connect in case of an early event. Disconnect should check if the timer has already started before starting the timer so it does not reset it. The timer should also be started before calling ble_gap_terminate in case of an early event that would cause the timer to start after disconnection, resetting the host unnecessarily.
1 parent 806b948 commit 4d5fcea

File tree

1 file changed

+52
-33
lines changed

1 file changed

+52
-33
lines changed

src/NimBLEClient.cpp

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -182,25 +182,30 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
182182
return false;
183183
}
184184

185-
if(isConnected() || m_pTaskData != nullptr) {
185+
if(isConnected() || m_connEstablished || m_pTaskData != nullptr) {
186186
NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d",
187187
std::string(m_peerAddress).c_str(), getConnId());
188188
return false;
189189
}
190190

191+
ble_addr_t peerAddr_t;
192+
memcpy(&peerAddr_t.val, address.getNative(),6);
193+
peerAddr_t.type = address.getType();
194+
if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) {
195+
NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists",
196+
address.toString().c_str());
197+
return false;
198+
}
199+
191200
if(address == NimBLEAddress("")) {
192201
NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)");
193202
return false;
194-
} else if(m_peerAddress != address) {
203+
} else {
195204
m_peerAddress = address;
196205
}
197206

198-
ble_addr_t peerAddr_t;
199-
memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6);
200-
peerAddr_t.type = m_peerAddress.getType();
201-
202-
203207
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
208+
m_pTaskData = &taskData;
204209
int rc = 0;
205210

206211
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
@@ -213,39 +218,43 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
213218
NimBLEClient::handleGapEvent, this);
214219
switch (rc) {
215220
case 0:
216-
m_pTaskData = &taskData;
217221
break;
218222

219223
case BLE_HS_EBUSY:
220224
// Scan was still running, stop it and try again
221225
if (!NimBLEDevice::getScan()->stop()) {
222-
return false;
226+
rc = BLE_HS_EUNKNOWN;
223227
}
224228
break;
225229

226230
case BLE_HS_EDONE:
227231
// A connection to this device already exists, do not connect twice.
228232
NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s",
229233
std::string(m_peerAddress).c_str());
230-
return false;
234+
break;
231235

232236
case BLE_HS_EALREADY:
233237
// Already attemting to connect to this device, cancel the previous
234238
// attempt and report failure here so we don't get 2 connections.
235239
NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling",
236240
std::string(m_peerAddress).c_str());
237241
ble_gap_conn_cancel();
238-
return false;
242+
break;
239243

240244
default:
241245
NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s",
242246
std::string(m_peerAddress).c_str(),
243247
rc, NimBLEUtils::returnCodeToString(rc));
244-
return false;
248+
break;
245249
}
246250

247251
} while (rc == BLE_HS_EBUSY);
248252

253+
if(rc != 0) {
254+
m_pTaskData = nullptr;
255+
return false;
256+
}
257+
249258
// Wait for the connect timeout time +1 second for the connection to complete
250259
if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) {
251260
m_pTaskData = nullptr;
@@ -255,7 +264,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
255264
disconnect();
256265
} else {
257266
// workaround; if the controller doesn't cancel the connection
258-
// at the timeout cancel it here.
267+
// at the timeout, cancel it here.
259268
NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling");
260269
ble_gap_conn_cancel();
261270
}
@@ -269,7 +278,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
269278
// If the failure was not a result of a disconnection
270279
// make sure we disconnect now to avoid dangling connections
271280
if(isConnected()) {
272-
ble_gap_terminate(m_conn_id, BLE_ERR_REM_USER_CONN_TERM);
281+
disconnect();
273282
}
274283
return false;
275284
} else {
@@ -325,26 +334,35 @@ bool NimBLEClient::secureConnection() {
325334
*/
326335
int NimBLEClient::disconnect(uint8_t reason) {
327336
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
328-
329337
int rc = 0;
330-
if(isConnected()){
338+
if(isConnected()) {
339+
// If the timer was already started, ignore this call.
340+
if(ble_npl_callout_is_active(&m_dcTimer)) {
341+
NIMBLE_LOGI(LOG_TAG, "Already disconnecting, timer started");
342+
return BLE_HS_EALREADY;
343+
}
344+
345+
ble_gap_conn_desc desc;
346+
if(ble_gap_conn_find(m_conn_id, &desc) != 0){
347+
NIMBLE_LOGI(LOG_TAG, "Connection ID not found");
348+
return BLE_HS_EALREADY;
349+
}
350+
351+
// We use a timer to detect a controller error in the event that it does
352+
// not inform the stack when disconnection is complete.
353+
// This is a common error in certain esp-idf versions.
354+
// The disconnect timeout time is the supervison timeout time + 1 second.
355+
// In the case that the event happenss shortly after the supervision timeout
356+
// we don't want to prematurely reset the host.
357+
ble_npl_time_t ticks;
358+
ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks);
359+
ble_npl_callout_reset(&m_dcTimer, ticks);
360+
331361
rc = ble_gap_terminate(m_conn_id, reason);
332-
if (rc == 0) {
333-
ble_addr_t peerAddr_t;
334-
memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6);
335-
peerAddr_t.type = m_peerAddress.getType();
336-
337-
// Set the disconnect timeout to the supervison timeout time + 1 second
338-
// In case the event triggers shortly after the supervision timeout.
339-
// We don't want to prematurely reset the host.
340-
ble_gap_conn_desc desc;
341-
if(ble_gap_conn_find_by_addr(&peerAddr_t, &desc) == 0){
342-
ble_npl_time_t ticks;
343-
ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks);
344-
ble_npl_callout_reset(&m_dcTimer, ticks);
345-
NIMBLE_LOGD(LOG_TAG, "DC TIMEOUT = %dms", (desc.supervision_timeout + 100) * 10);
362+
if (rc != 0) {
363+
if(rc != BLE_HS_EALREADY) {
364+
ble_npl_callout_stop(&m_dcTimer);
346365
}
347-
} else if (rc != BLE_HS_EALREADY) {
348366
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s",
349367
rc, NimBLEUtils::returnCodeToString(rc));
350368
}
@@ -797,14 +815,15 @@ uint16_t NimBLEClient::getMTU() {
797815
break;
798816
}
799817

800-
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
801-
802818
// Stop the disconnect timer since we are now disconnected.
803819
ble_npl_callout_stop(&client->m_dcTimer);
804820

805821
// Remove the device from ignore list so we will scan it again
806822
NimBLEDevice::removeIgnored(client->m_peerAddress);
807823

824+
// No longer connected, clear the connection ID.
825+
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
826+
808827
// If we received a connected event but did not get established (no PDU)
809828
// then a disconnect event will be sent but we should not send it to the
810829
// app for processing. Instead we will ensure the task is released

0 commit comments

Comments
 (0)