Skip to content

_bleio HCI: fix advertising and ATT responses on ESP32-C6 AirLift controllers - #11432

Open
dhalbert wants to merge 2 commits into
adafruit:mainfrom
dhalbert:bleio-hci-extended-advertising
Open

dhalbert wants to merge 2 commits into
adafruit:mainfrom
dhalbert:bleio-hci-extended-advertising

Conversation

@dhalbert

Copy link
Copy Markdown
Collaborator

Diagnosis and code by Claude, with hardware testing by @dhalbert on a Fruit Jam (ESP32-C6 AirLift) and a Metro M4 AirLift Lite (ESP32), both running NINA-FW 3.3.0.

The problem

start_advertising() on the Fruit Jam failed with HCI status error: 0c (Command Disallowed). The devices/ble_hci adapter sent LE Read Maximum Advertising Data Length at enable time whenever the controller reported the LE Extended Advertising feature. That is an extended advertising command, and per Core Spec Vol 4 Part E §3.1.1 a controller must then refuse every legacy advertising command until the next HCI Reset; NimBLE enforces this in ble_ll_is_valid_adv_mode(). The ESP32's BLE 4.2 controller has no extended-advertising feature bit, so it never took that path.

With advertising working, connections then hung in service discovery: the controller was dropping every ATT response.

The fixes

  • On controllers with the LE Extended Advertising feature, use the extended command set for all advertising: LE Set Extended Advertising Parameters / Data / Scan Response Data / Enable. The set uses legacy PDUs (BT_HCI_LE_ADV_PROP_LEGACY), so the 31-byte data limit and visibility to 4.x centrals are unchanged. Legacy commands remain in use on 4.x controllers.
  • The previously unreachable extended path never sent advertising or scan-response data and left prim_adv_phy, filter_policy and the peer address type unset in hci_le_set_extended_advertising_parameters(); these are now filled in, and stop_advertising() uses the extended disable.
  • hci_send_acl_pkt() sent host-to-controller ACL data with packet boundary flag 0b10 (first automatically flushable). NimBLE controllers discard any host ACL packet with a PB flag above 0b01 as malformed, silently. Changed to 0b00 (first non-flushable), which is what BlueZ and Zephyr send; the ESP32 controller accepted either.

Testing

Fruit Jam and Metro M4 AirLift Lite, adafruit_ble UART example (ProvideServicesAdvertisement + UARTService): advertise, connect and echo from Bluefruit Connect on iOS; from Linux, bluetoothctl discovers all attributes of the UART service on both boards. On the Fruit Jam also checked stop_advertising(), timeout= expiry, non-connectable and scannable advertisements.

Not addressed

  • anonymous=True fails with HCI status error: 12 on both controllers: the adapter never sets a random address. Pre-existing and separate.
  • Advertising data over 31 bytes on 5.x controllers would need HCI fragmentation and lifting check_data_fit(); bleio_hci is peripheral-only, so no scanning or central changes were needed.

dhalbert and others added 2 commits September 20, 2026 21:08
A controller that supports LE Extended Advertising refuses legacy
advertising commands with Command Disallowed once any extended command
has been issued (Core Spec Vol 4 Part E 3.1.1). The adapter sent
`LE Read Maximum Advertising Data Length` at enable time and then
legacy `LE Set Advertising Parameters`, so `start_advertising()` failed
with HCI status 0x0C on the ESP32-C6 AirLift (Fruit Jam).

On such controllers, use the extended command set for all advertising,
with legacy PDUs so the 31-byte limit and 4.x central compatibility are
unchanged. Also complete the extended path: send advertising and scan
response data, fill in `prim_adv_phy`, `filter_policy` and the peer
address type, and disable via the extended enable command.

Fixes adafruit#11322

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`hci_send_acl_pkt()` tagged host-to-controller ACL data with PB flag
0b10 (first automatically flushable). NimBLE controllers such as the
ESP32-C6 treat any PB above 0b01 as a bad packet and drop it silently,
so every ATT response was lost and peers hung in service discovery.
Use 0b00 (first non-flushable), as BlueZ and Zephyr do. The ESP32 BTDM
controller accepted either value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dhalbert
dhalbert requested a review from tannewt September 21, 2026 01:34
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.

AirLift BLE on Adafruit Fruit Jam does not accept advertising commands

1 participant