micropython-lib icon indicating copy to clipboard operation
micropython-lib copied to clipboard

aioble peripheral may need throttling to avoid random disconnects

Open PBrunot opened this issue 2 years ago • 5 comments

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.

PBrunot avatar Jan 07 '23 19:01 PBrunot

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.

jimmo avatar Jan 17 '23 03:01 jimmo

Is L2CAP available on esp32?

andrewleech avatar Jan 17 '23 03:01 andrewleech

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.

jimmo avatar Jan 17 '23 03:01 jimmo

Awesome, yeah I thought you'd done some work in this area recently :-D

andrewleech avatar Jan 17 '23 03:01 andrewleech

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?

RantyDave avatar Jan 30 '24 22:01 RantyDave