Skip to content

BLE workflow: add a guard service so user-created services don't break the web editor - #11333

Open
dhalbert wants to merge 1 commit into
adafruit:mainfrom
dhalbert:ble-guard-service
Open

BLE workflow: add a guard service so user-created services don't break the web editor#11333
dhalbert wants to merge 1 commit into
adafruit:mainfrom
dhalbert:ble-guard-service

Conversation

@dhalbert

@dhalbert dhalbert commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Diagnosis by Claude and @dhalbert. Code by Claude with refactoring and comment rewrites by @dhalbert. PR post by Claude with considerable editing by @dhalbert.

Problem

Creating a _bleio.Service from user code while the web editor is connected hung the editor's REPL terminal on Linux, macOS and Windows. File transfer kept working. The problem is that a peripheral reports its last primary service with End Group Handle 0xffff, and hosts cache that literally, with the 0xffff. When BLE workflow is on, two services are added: File and Serial. The Serial Service then is recorded by the host with 0xffff as its ending handle.

If CircuitPython user code adds another service, the start and end handles will fall inside the cached handle range for the Serial Service, since 0xffff includes everything. The host then treats the addition as a change to the Serial Service, since the new handles fall inside the range it cached for it.

Because the Serial Service handle range changed, Chrome invalidates its service object, and the terminal stops working. (Also there's no Web Bluetooth event provided by Chrome to report that.) In addition, BlueZ (used by Linux) has a bug that drops the service from its cache until a real link-level reconnect.

The serial service was affected only because it was created last. If the file and serial services were reversed, it would have been the file service (verified by testing).

Fix

  • Create an unused primary service (adaf0004, one read-only characteristic adaf0005, three handles) after the file transfer and serial services in supervisor_start_bluetooth(). This is the service a later user-created service will overlap.
  • The unused characteristic is required: on espressif a service is not registered with NimBLE until its first characteristic is added.
  • Minor code cleanups:
    • Move circuitpython_base_uuid from serial.c to bluetooth.c, since the guard uses it too.
    • Simplify conditional compilation guards in bluetooth.c.

Testing

Tested Feather nRF52840 Express and Metro ESP32-S3, using code.circuitpython.org in Chrome, on Linux, macOS and Windows:

  • Do a fresh pair.
  • Connect to the editor.
  • import a file that creates a Battery Service.
  • Confirm the terminal and file browser both still work.

Before the change the terminal died on all three hosts; after it both keep working. btmon logs confirmed the guard is the service the hosts discard and the new service is placed after it.

Ctrl-D after creating a user service still drops the BLE connection. That is a pre-existing problem: you have to reconnect. But now you can do that successfully.

A peripheral reports its last primary service with End Group Handle
0xffff, and hosts cache that literally. A service that user code adds
later lands inside that cached range, so hosts treat it as a change to
the last workflow service: Chrome invalidates the page's object for it
with no event to say so, and BlueZ drops it from its cache. On every
host the web editor's REPL terminal went dead as soon as user code
created a `_bleio.Service`.

Create a placeholder service after the file transfer and serial
services so that it, not a workflow service, is the one affected.
Verified on nordic and espressif against BlueZ, CoreBluetooth and
WinRT. Also move `circuitpython_base_uuid` from `serial.c` to
`bluetooth.c`, since the guard uses it too, and factor the repeated
`CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE` into
`BLE_WORKFLOW`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

1 participant