Description
I've experienced hang (loop forever inside BT stack) when trying to call ble.active(False)
from within a scheduled function. This seems to happen when there are alive connections.
I've looked at the code, and I tracked down this to the following reason:
In bluetooth_nimble_port_shutdown(void)
, ble_hs_stop()
is first invoked; it attempts to terminate any live connection i.e. it initiates the termination process, and registers a listener to be called later on when the connection eventually has terminated.
Then bluetooth_nimble_port_shutdown(void)
polls over a volatile flag that is supposed to be set by the former listener.. But this flag gets never set, and the loop lasts forever.
This seems to be due to the fact that the BLE stack doesn't run because we are looping inside a scheduled function, so that the listener never gets invoked.
There is also a timeout mechanism for this, which rely on a "npl_callout", but it seems not working in stm32 port, because those "callouts" seem scheduled by nimble_port_run(void)
which seems never called on stm32 port. It even looks like that micropython build succeeds just because the compile optimize-out it, otherwise it fails...
So I see two problems here:
- The listener that should be invoked when connections are terminated doesn't ever run.
- The timeout on connection termination doesn't work.
BTW FWIW my current workaround is to invoke mp_bluetooth_hci_poll()
inside the above loop, after MICROPY_EVENT_POLL_HOOK
- It seems to work, but I'm not sure it's really OK in all cases - and to check for a timeout in the very same loop; I now have something like this
while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
MICROPY_EVENT_POLL_HOOK;
mp_bluetooth_hci_poll();
if (mp_hal_ticks_ms() - initial_time > 1000)
ble_hs_stop_done_workaround();
}
ble_hs_stop_done_workaround()
is just a wrapper around ble_hs_stop_done()