11.5. 接続¶
セントラルがアドバタイズストリームの中からペリフェラルを選び、それに 接続リクエスト を送ると、双方はアドバタイズ/スキャンモードから抜けて 接続 へ移行します。ここからラジオはリンク層のデータチャネル上で活動をスケジュールし、接続時に合意したシーケンスに従ってチャネル間を擬似ランダムにホッピングします。リンク層より上位のすべて(GATT、セキュリティ、L2CAP)は、ここで確立された接続の上で動作します。
11.5.1. 接続イベント¶
BLE接続中の2つのデバイスは、連続的にストリーミングするわけではありません。両者は 接続インターバル を取り決め、各インターバルごとに双方がラジオを起こし、キューにあるパケットを交換し、受信したものを確認応答してから、再びスリープに戻ります。これらの交換のそれぞれを 接続イベント と呼びます。
各側のラジオは、短い接続イベントの間だけ起きており、それ以外はすべてスリープしています。ペリフェラルは ペリフェラルレイテンシ の下でイベントをスキップする場合があります。¶
これを管理する数値は接続時にネゴシエートされ、リンク層がそれらを強制します。これらはアプリケーションからは、リクエスト側のつまみとしても、報告される結果値としても見ることができます。
接続インターバル。 7.5 ms から 4 s まで、1.25 ms 刻みです。リクエストが不合理でない限り、セントラルはペリフェラルが要求する値を選びます。インターバルが短いほどラジオ活動が増える代わりに低いレイテンシでデータを届けられ、長いほど電力を節約できる一方で各往復が遅くなります。
ペリフェラルレイテンシ。 負でない整数 N です。ペリフェラルは送信するものが何もないとき、空の交換のためにラジオを起こす代わりに再びスリープに戻り、最大 N 個の接続イベントをスキップできます。1秒に1回起きて報告するが、まれな即時メッセージのために低い 応答性の高い 接続インターバルを望むセンサーに役立ちます。
スーパービジョンタイムアウト。 100 ms から 32 s までです。いずれかの側がこの時間だけ相手から何も受信しなければ、リンクは失われたと宣言され、双方ともアドバタイズ/スキャンに戻ります。タイムアウトは
connection_interval * (1 + peripheral_latency)より長くなければなりません。リンク層はこれに違反する値を拒否します。
aioble.Device.connect() は min_conn_interval_us と max_conn_interval_us を取り、セントラルが特定の範囲を要求できるようにします。ラジオが実際に落ち着いた値は、接続が確立した後にリンク層構成を通じて読み戻せます。
11.5.2. 接続内での「セントラル」と「ペリフェラル」の意味¶
アドバタイズ時に設定された役割は、接続が確立した後もそのまま維持されます。
セントラル はタイミングを駆動します。ホッピングシーケンスのマスター側と接続イベントを所有します。
ペリフェラル は反応的です。接続パラメータの変更(たとえば電力節約のための遅いインターバル)を要求できますが、それを受け入れるかどうかはセントラルが決定します。
これらの役割は、誰がGATTデータベースをホストするかとは独立しており、それは別の軸です。慣例として、ペリフェラルはGATTの サーバー でもあり、セントラルはGATTの クライアント ですが、BLEではどちらの側でもGATTサービスをホストできます。カメラはほぼ常に慣例に従います。「カメラがデータを公開する」アプリケーションではペリフェラル+サーバー、「カメラがセンサーから読み取る」アプリケーションではセントラル+クライアントです。
11.5.3. 最大転送単位(MTU)¶
リンク層が運ぶパケットはデフォルトでは短く、ペイロードは27バイトで、そのうちGATTが利用できるのは23バイトだけです。これは小さな読み取り値や短いコマンドには十分ですが、マルチバイトのものに対しては小さすぎます。双方はこれを、ラジオのファームウェアがサポートする上限(最新のコントローラでは通常数百バイト)まで引き上げるようネゴシエートできます。
aioble API は aioble.DeviceConnection.exchange_mtu() を通じてネゴシエーションを駆動し、その結果は mtu 属性で利用できるようになります。MTUが大きいほど、約20バイトより大きい値に対する往復回数が減りますが、わずかなバッファメモリのコストがかかります。
11.5.4. ライフタイム¶
接続は次のいずれかが起こるまで続きます。
いずれかの側が
disconnect()を呼び出す、スーパービジョンタイムアウトが発火する(範囲外、ラジオオフ、ピアのクラッシュ)、または
明示的なリンク層の失敗(暗号化の不一致、ペアリングの拒否)。
接続が切れると、その上でキューに入っているか処理中のすべてのGATT操作は aioble.DeviceDisconnectedError を送出し、アプリケーションが入っているすべての async with connection ブロックはクリーンに終了します。ペリフェラルは通常、aioble.advertise() に戻って次のセントラルを待つことで応答します。セントラルは再びスキャンするか、切断をアプリケーションに表出させることで応答します。
このライフタイムは aioble が役立つ理由の1つです。同期的なBLE APIでは切断コールバックとイベントマスクを公開する必要がありますが、asyncio版は切断を、操作を待っていたコルーチン内部の例外に変えます。これはまさに async with のクリーンアップが想定しているものです。