micropython-lib
micropython-lib copied to clipboard
aioble peripheral may need throttling to avoid random disconnects
I am using AIOBLE (latest version as per December 2022) on ESP32-C3 with Micropython 1.19 (LOLIN D32 board). My app uses 3 BT LE services (peripheral role),:
- a custom service for requests (with 1 write-only and 1notify-only characteristics)
- a battery_service
- a device_information service
I observed frequent client disconnects to client (I tested with another ESP32 and with NRFConnect app on Android). The problem seems to happen more frequently when many requests were done in a rapid succession. In such conditions, the peripheral stopped responding, and disconnected the clients after a while (10-20s). Once the clients reconnected, everything worked again.
I was able to solve the problem after one week of testing, by imposing a minimum delay between all characteristics notifications (in my case 50 ms) : now I don't have any disconnects and everything is stable.
Concept code:
since_last = time.ticks_ms() - __last_notification_ms
while _MIN_BLUETOOTH_DELAY_MS > since_last > 0 and not force:
await asyncio.sleep_ms(_MIN_BLUETOOTH_DELAY_MS - since_last)
# Need to recheck because another transmission may have happened during await period
since_last = time.ticks_ms() - __last_notification_ms
data = __get_notification_data()
__status_characteristic.write(data, True)
__last_notification_ms = time.ticks_ms()
I guess there is a minimum delay for physical transmission over bluetooth and handling by RTOS and if we push too many notifications in a short period, something goes wrong. I wrote this issue for informing other AIOBLE users and for suggesting defining a rate limit inside the aioble library, or a warning, because this was quite difficult to troubleshoot.
Thanks @PBrunot
It would be worth getting to the root cause of why this causes disconnects, but yes this seems to be becoming a bit of a common gotcha. It might be possible to make the server-side char.write and char.notify detect this case and raise a "busy" exception. (i.e. it should be impossible to write Python code that causes a failure like this).
FWIW, using an L2CAP connection-oriented-channel for streaming data is a much better alternative to "streaming" notifications, because flow control is built-in.
Is L2CAP available on esp32?
Is L2CAP available on esp32?
Ah, good point. Now that we've moved to synchronous events on the ESP32 there should be nothing blocking enabling it though, just needs testing.
Awesome, yeah I thought you'd done some work in this area recently :-D
I'm also getting these frequent (after 5-10 minutes) disconnections. I'm pushing a reasonable quantity of traffic, maybe 50-100 notifications/sec over 7 characteristics, using aioble, and receive an OSError exception (22 EINVAL). Throttling makes no difference. After some quick perusal of the code I see some references to a ring buffer - could it be that it's not dealing with this filling up correctly?