Skip to content

Espressif BLE fixes: advertising duration, _bleio.adapter.connected#11036

Draft
dhalbert wants to merge 1 commit into
adafruit:mainfrom
dhalbert:espressif-ble-fixes
Draft

Espressif BLE fixes: advertising duration, _bleio.adapter.connected#11036
dhalbert wants to merge 1 commit into
adafruit:mainfrom
dhalbert:espressif-ble-fixes

Conversation

@dhalbert
Copy link
Copy Markdown
Collaborator

@dhalbert dhalbert commented May 28, 2026

I think this may fix #10007.

Advertising duration

The advertising duration value for "forever" is different for ble_gap_adv_start() and ble_gap_ext_adv_start(). It is BLE_HS_FOREVER and 0, respectively. The code was using BLE_HS_FOREVER In 6.0.1, this value is actually checked, and gives a parameter range error.

connected state check

Code was added in #9289 that required that the MTU value be non-zero when returning true for _bleio.adapter.connected.

if (connection->conn_handle != BLEIO_HANDLE_INVALID) {

if (connection->conn_handle != BLEIO_HANDLE_INVALID && connection->mtu != 0) {

The MTU is zero when a connection has first been made but the MTU is not yet negotiated.

The MTU check above causes a brief interval, for both ble.connected() and ble.advertising() to be False. The transition between advertising and connected did not appear atomic. This compares with the nordic implementation where this glitch is not present.

I removed this MTU check and connections still work. I was testing with an HID keyboard example (derived from https://learn.adafruit.com/ble-hid-keyboard-buttons-with-circuitpython) which requires pairing. It is necessary to forget the CircuitPython device on the central side if its firmware is reloaded, at least for an iOS central.

@tannewt Do you remember why you added the MTU check in #9289? It's true that the connection is not complete until the MTU is negotiated. But we have a lot of example code something like this:

ble.start_advertising(advertisement)

while True:
    if not ble.connected and not ble.advertising:
        print("BLE disconnected, restarting advertising...")
        ble.start_advertising(advertisement)
    ...

So what happens is that when the central has connected initially but the MTU is not yet negotiated, the if statement is briefly False, which makes the program think erroneously that there was a disconnect, and it starts advertising again. On Espressif, this quickly causes "Nimble: out of memory".

If there was a ble.connection_initiated ble.connection_in_progress that could be used instead. But otherwise there's no state available to know that. And we'd have to change a lot of code.

@dhalbert dhalbert requested a review from tannewt May 28, 2026 19:09
1. The advertising duration value for "forever" is different for `ble_gap_adv_start()` and `ble_gap_ext_adv_start()`. It is `BLE_HS_FOREVER` and `0`, respectively.
2. Code was added in adafruit#9289 that required that the MTU value was non-zero when returning true for `_bleio.adapter.connected`. This caused a brief interval, while a central was connecting, for a peripheral with both `ble.connected()` and `ble.advertising()` being `False`. The transition between the two states did not appear atomic. This compares with `nordic` where it was.
@dhalbert dhalbert force-pushed the espressif-ble-fixes branch from 2fca313 to 2722f6b Compare May 28, 2026 19:10
@tannewt
Copy link
Copy Markdown
Member

tannewt commented May 29, 2026

I think we can consider it connected when we are negotiating MTU. It's been a while since I did BLE but I think our MTU negotiation is just to get the most bytes over the air at the time. It is optional and not required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nimble out of memory when trying to connect to multiple devices on ESP32 S3

2 participants