Skip to content

Conversation

@laurensvalk
Copy link
Member

This makes the scan/connect/pair function more robust. In our other drivers, the scan_and_connect_func was already a protothread that the user could would await until completion. In the bluetooth_btstack.c driver, this was a little different. Instead, the protothread would kick off a state machine in the btstack event handler, and await until it got into the connected state.

This worked well enough for some cases, but there was no mechanism to bubble up intermediate errors so we could not recover from bad states. It was also quite hard to follow, because the btstack event handler does not map to the same states. For example, GATT_EVENT_QUERY_COMPLETE means something different if you're reading, writing, or discovering a characteristic, and then it would have to be mapped to a specific peripheral (once we support more than one, see below.)

We can rewrite it so it is more like this, where we drive the Bluetooth process forward on each normal event as well as each btstack event:

gap_start_scan();
PBIO_OS_AWAIT_UNTIL(state, timed_out || canceled || received_expected_report(event));
PBIO_OS_AWAIT_UNTIL(state, timed_out || canceled || received_expected_response_report(event));
gap_connect()
PBIO_OS_AWAIT_UNTIL(state, timed_out || canceled || was_connected(event));
request_pair()
PBIO_OS_AWAIT_UNTIL(state, timed_out || canceled || did_pair(event) || did_reencrypt(event));

Between each step, we can return errors if they occur, and each step is cancellable by the user or by timeout.

Operations that are not cancellable still don't get the hub stuck. If we an irrecoverable Bluetooth state and the user stops the program, the hub will gracefully shut down.


This also further generalizes the btstack driver towards allowing more than one peripheral in the future, a process that has been started in commits prior to this PR. In this step, we further reduce the dependency on hardcoded singletons by moving the btstack peripheral state instance into the peripheral object. The tasks are also written to use the peripheral reference rather than the singleton. Later, we can pass the specific instance to each task.

We should be explicitly resetting only what needs resetting to avoid introducing subtle bugs every time we add something to the peripheral struct that should not be erased.
At the moment we support only one peripheral, so a lot of code relies on global singletons. In the btstack driver, there is a specific handset instance that we want to generalize, so start migrating from there.
We can properly return errors to the user this way, and write chronological
code. Besides catching more operational errors, we can also let the higher
level code decide when and where to disconnect, rather than doing that from
some but not all places within these tasks.

This will also help when we support multiple peripherals in parallel.
As in the last commit, improve error handling and allow for multiple peripherals in the future.
Make them consistent with read and discovery tasks.
This replaces the state machine logic of the scan and connect task with placing logic synchronously in a protothread. At every stage, we can detect, report and return errors, and handle cancellation correctly.

This fixes a longstanding issue where the Xbox Controller would sometimes not pair to a SPIKE Prime Hub, even though a second attempt usually worked. Now we can catch the return codes from the security manager and re-try pairing within the task, or retry the whole task a second time. We also allow re-encryption now rather than disconnecting when this is attempted.

Extensive USB logging is added to catch issues and flow of the connection process.
Some functions have an early exit because no changes are needed. In these cases, we'll want to set their error state to success so that asserting their error does not raise if an error was set and raised previously.
@coveralls
Copy link

Coverage Status

coverage: 50.406% (-0.1%) from 50.533%
when pulling 136126d on btstack-scan-connect-bond
into e702c64 on master.

@laurensvalk laurensvalk merged commit 136126d into master Jan 9, 2026
34 checks passed
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.

3 participants