14.5. Connections¶
Once a central picks a peripheral from the advertising stream and sends it a connect request, both sides drop out of advertising / scanning mode and into a connection. The radio now schedules its activity on the data channels of the link layer, hopping pseudo-randomly between them on the sequence agreed at connection time. Everything above the link layer – GATT, security, L2CAP – runs on top of the connection that is established here.
14.5.1. The connection event¶
Two devices in a BLE connection do not stream continuously. They agree on a connection interval, and at every interval both sides wake up the radio, exchange whatever packets are queued, acknowledge what they have received, and go back to sleep. Each of those exchanges is called a connection event.
The radio on each side is awake only during the brief connection events, with everything else asleep. The peripheral may skip events under peripheral latency.¶
The numbers that govern this are negotiated at connect time and the link layer enforces them. They are visible to the application both as request-side knobs and as the resulting values reported back.
Connection interval. 7.5 ms to 4 s, in 1.25 ms steps. The central picks the value the peripheral asks for unless the request is unreasonable. Shorter intervals deliver data with lower latency at the cost of more radio activity; longer ones save power but make every round trip slower.
Peripheral latency. A non-negative integer N. The peripheral is allowed to skip up to N connection events with nothing to send, going back to sleep instead of waking the radio for an empty exchange. Useful for sensors that wake to report once a second but want a low responsive connection interval for the rare immediate message.
Supervision timeout. 100 ms to 32 s. If either side hears nothing from the other for this long, the link is declared lost and both sides go back to advertising / scanning. The timeout must be longer than
connection_interval * (1 + peripheral_latency)– the link layer rejects values that violate that.
aioble.Device.connect() takes
min_conn_interval_us and max_conn_interval_us so
the central can request a particular range; the actual
value the radio settled on can be read back through the
link-layer configuration after the connection is up.
14.5.2. What “central” and “peripheral” mean inside a connection¶
The roles set at advertising time stick after the connection comes up:
The central drives the timing – it owns the master side of the hopping sequence and the connection events.
The peripheral is reactive. It can request a change to the connection parameters (a slower interval to save power, say) but the central decides whether to accept.
The roles are independent of who hosts the GATT database, which is a separate axis. By convention the peripheral is also the GATT server and the central is the GATT client, but BLE allows either side to host GATT services. Cameras almost always follow the convention: peripheral + server for “the camera publishes data” applications, central + client for “the camera reads from a sensor” applications.
14.5.3. The maximum transmission unit (MTU)¶
The link layer carries packets that are short by default – 27 bytes of payload, of which only 23 are available to GATT. That is enough for a small reading or a short command but tiny next to anything multi-byte. Both sides can negotiate this upward, up to the limit the radio firmware supports (typically a few hundred bytes on modern controllers).
The aioble API drives the negotiation through
aioble.DeviceConnection.exchange_mtu() and the
result becomes available on the
mtu attribute. Larger
MTUs mean fewer round trips for any value bigger than
~20 bytes, at a small cost in buffer memory.
14.5.4. Lifetime¶
A connection lasts until one of:
either side calls
disconnect(),the supervision timeout fires (out-of-range, radio off, peer crashed), or
an explicit link-layer failure (encryption mismatch, pairing rejection).
When the connection drops, every GATT operation queued
or in flight on it raises
aioble.DeviceDisconnectedError, and any
async with connection blocks the application is in
exit cleanly. A peripheral typically responds by going
back to aioble.advertise() and waiting for the
next central; a central responds by either scanning
again or surfacing the disconnect to the application.
The lifetime is one of the reasons aioble is
useful. A synchronous BLE API would have to expose
disconnect callbacks and event masks; the asyncio
version turns disconnects into exceptions inside the
coroutine that was waiting for the operation, which is
what async with cleanup is built for.