Repository navigation
Conversation
When connect() timed out, it usually returned a useless Connection, with connected False, instead of raising an exception. The timeout was used both by NimBLE and by connect()'s own wait loop. NimBLE reports its timeout only after the controller cancels the connection attempt, so the wait loop usually stopped first. The connection status was then still invalid, which matched an unused connection slot, so connect() returned that. NimBLE also kept trying to connect, so the next connect() raised "Already in progress". When NimBLE did report first, its timeout came out as "Unknown BLE error: 13". Wait for NimBLE's BLE_GAP_EVENT_CONNECT instead, which it always sends, and raise "Failed to connect: timeout" on BLE_HS_ETIMEOUT. On Ctrl-C, cancel the attempt and wait for the cancellation. Reset the status before ble_gap_connect(), and end the wait if the MTU exchange can't be started. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A peer such as Zephyr starts the MTU exchange itself right after connecting. NimBLE answers it before _new_connection() runs, and then ble_gattc_exchange_mtu() returns BLE_HS_EALREADY, so _mtu_reply() is never called. The BLE_GAP_EVENT_MTU went to _connect_event(), which ignores it, so connection->mtu stayed 0 and max_packet_length reported 20 instead of the negotiated MTU minus 3. Get the MTU with ble_att_mtu() in that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Diagnosis and code by Claude, reviewed by @dhalbert.
Problem
connect()timed out, it usually returned a uselessConnection, withconnectedFalse, instead of raising an exception.timeoutargument of_bleio.Adapter.connect()was used both by NimBLE and byconnect()'s own wait loop. NimBLE reports its timeout only after the controller cancels the connection attempt, so the wait loop usually stopped first.connect()returned that.connect()raised "Already in progress".connect()also left NimBLE connecting.connect()waited out its full timeout and returned a uselessConnection, though the real connection was up.BLE_HS_EALREADY. So_mtu_reply(), which ends the wait, was never called.max_packet_lengthwas 20.Fix
connect()now waits for NimBLE'sBLE_GAP_EVENT_CONNECT, which NimBLE always sends, and raisesBluetoothError: Failed to connect: timeoutonBLE_HS_ETIMEOUT. This matches nordic.ble_gap_conn_cancel()and waits for the cancellation.ble_gap_connect(), so an early event can't be lost.ble_att_mtu().Testing
Metro ESP32-S3 with this PR as central, nRF54LM20 DK on zephyr-cp as an advertising peripheral:
connect()to an absent address, 3 s timeout (twice)BluetoothError: Failed to connect: timeoutafter 3.0 sconnect()to the DK right after each timeoutconnect()to the DKmax_packet_length248; before: uselessConnectionafter the full timeout,max_packet_length20connect()to an absent addressKeyboardInterrupt; nextconnect()to the DK and next absent timeout both normal