11.5. Verbindungen

Sobald ein Central aus dem Advertising-Stream ein Peripheral auswählt und ihm eine Connect Request sendet, verlassen beide Seiten den Advertising-/Scanning-Modus und gehen in eine Verbindung über. Das Funkmodul plant seine Aktivität nun auf den Datenkanälen des Link Layers und springt pseudozufällig zwischen ihnen gemäß der zum Verbindungszeitpunkt vereinbarten Sequenz. Alles oberhalb des Link Layers – GATT, Sicherheit, L2CAP – läuft auf der hier aufgebauten Verbindung.

11.5.1. Das Verbindungsereignis

Zwei Geräte in einer BLE-Verbindung übertragen nicht kontinuierlich. Sie einigen sich auf ein Connection Interval, und bei jedem Intervall wecken beide Seiten das Funkmodul, tauschen alle anstehenden Pakete aus, bestätigen das Empfangene und gehen wieder schlafen. Jeder dieser Austausche wird als Connection Event bezeichnet.

Eine Zeitachse mit dem Central auf der oberen Spur und dem Peripheral auf der unteren Spur. An jeder Grenze des Connection Intervals zeigen beide Spuren einen kurzen Impuls mit der Beschriftung "TX/RX". Der Abstand zwischen den Impulsen ist mit "connection interval" beschriftet; die Dauer jedes Impulses ist "connection event". Das Peripheral hat einige übersprungene Ereignisse mit der Beschriftung "peripheral latency: peripheral may skip if idle".

Das Funkmodul auf jeder Seite ist nur während der kurzen Connection Events aktiv, alles andere schläft. Das Peripheral darf unter Peripheral Latency Ereignisse überspringen.

Die Werte, die dies steuern, werden zum Verbindungszeitpunkt ausgehandelt, und der Link Layer setzt sie durch. Sie sind für die Anwendung sowohl als anforderungsseitige Stellschrauben als auch als die zurückgemeldeten resultierenden Werte sichtbar.

  • Connection Interval. 7,5 ms bis 4 s, in Schritten von 1,25 ms. Das Central wählt den vom Peripheral gewünschten Wert, sofern die Anforderung nicht unangemessen ist. Kürzere Intervalle liefern Daten mit geringerer Latenz auf Kosten von mehr Funkaktivität; längere sparen Energie, machen aber jeden Round Trip langsamer.

  • Peripheral Latency. Eine nicht-negative Ganzzahl N. Das Peripheral darf bis zu N Connection Events überspringen, wenn es nichts zu senden hat, und schläft wieder, anstatt das Funkmodul für einen leeren Austausch zu wecken. Nützlich für Sensoren, die einmal pro Sekunde aufwachen, um zu melden, aber für die seltene sofortige Nachricht ein niedriges responsives Connection Interval wünschen.

  • Supervision Timeout. 100 ms bis 32 s. Wenn eine Seite für diese Dauer nichts von der anderen hört, gilt die Verbindung als verloren und beide Seiten kehren zum Advertising/Scanning zurück. Das Timeout muss länger sein als connection_interval * (1 + peripheral_latency) – der Link Layer weist Werte zurück, die dagegen verstoßen.

aioble.Device.connect() nimmt min_conn_interval_us und max_conn_interval_us entgegen, sodass das Central einen bestimmten Bereich anfordern kann; der tatsächlich vom Funkmodul gewählte Wert kann nach dem Verbindungsaufbau über die Link-Layer-Konfiguration ausgelesen werden.

11.5.2. Was „Central“ und „Peripheral“ innerhalb einer Verbindung bedeuten

Die zum Advertising-Zeitpunkt festgelegten Rollen bleiben nach dem Verbindungsaufbau bestehen:

  • Das Central steuert das Timing – es besitzt die Master-Seite der Hopping-Sequenz und der Connection Events.

  • Das Peripheral ist reaktiv. Es kann eine Änderung der Verbindungsparameter anfordern (etwa ein langsameres Intervall zum Energiesparen), aber das Central entscheidet, ob es zustimmt.

Die Rollen sind unabhängig davon, wer die GATT-Datenbank hostet, was eine separate Achse darstellt. Konventionsgemäß ist das Peripheral auch der GATT-Server und das Central der GATT-Client, aber BLE erlaubt es jeder Seite, GATT-Dienste zu hosten. Kameras folgen fast immer der Konvention: Peripheral + Server für „die Kamera veröffentlicht Daten“-Anwendungen, Central + Client für „die Kamera liest von einem Sensor“-Anwendungen.

11.5.3. Die Maximum Transmission Unit (MTU)

Der Link Layer trägt standardmäßig kurze Pakete – 27 Byte Nutzlast, von denen nur 23 für GATT verfügbar sind. Das reicht für einen kleinen Messwert oder einen kurzen Befehl, ist aber winzig im Vergleich zu allem Mehrbyteförmigen. Beide Seiten können diesen Wert nach oben aushandeln, bis zum Limit, das die Funk-Firmware unterstützt (typischerweise einige hundert Byte auf modernen Controllern).

Die aioble-API treibt die Aushandlung über aioble.DeviceConnection.exchange_mtu() an, und das Ergebnis wird über das Attribut mtu verfügbar. Größere MTUs bedeuten weniger Round Trips für jeden Wert größer als ~20 Byte, zu geringen Kosten an Pufferspeicher.

11.5.4. Lebensdauer

Eine Verbindung besteht, bis eines der folgenden eintritt:

  • eine Seite ruft disconnect() auf,

  • das Supervision Timeout greift (außer Reichweite, Funkmodul aus, Peer abgestürzt) oder

  • ein expliziter Link-Layer-Fehler auftritt (Verschlüsselungs-Mismatch, Pairing-Ablehnung).

Wenn die Verbindung abbricht, löst jede auf ihr in der Warteschlange befindliche oder laufende GATT-Operation aioble.DeviceDisconnectedError aus, und alle async with connection-Blöcke, in denen sich die Anwendung befindet, werden sauber verlassen. Ein Peripheral reagiert typischerweise, indem es zu aioble.advertise() zurückkehrt und auf das nächste Central wartet; ein Central reagiert entweder mit erneutem Scannen oder indem es den Verbindungsabbruch an die Anwendung weiterreicht.

Die Lebensdauer ist einer der Gründe, warum aioble nützlich ist. Eine synchrone BLE-API müsste Disconnect-Callbacks und Event-Masks offenlegen; die asyncio-Version verwandelt Verbindungsabbrüche in Exceptions innerhalb der Coroutine, die auf die Operation gewartet hat – genau wofür async with-Cleanup gemacht ist.