MTU(Maximum Transmission Unit)は、1回のGATTトランザクションでデバイス間で送信できる1つのBLEパケット内の有効データの最大サイズです。BLE Classic(4.x)では、MTUは23バイトに固定されており、小さなセンサー読み取りには十分ですが、ファイル転送やOTAアップデートには不十分です。Bluetooth Core Specification 4.2(2014)では、MTU Size Request手順が導入され、より大きなMTU(最大247バイト、BLE 5.0では最大251バイト)のネゴシエーションが可能になりました。適切なMTU設定は、大量のデータを送信するBLEアプリケーションのパフォーマンスの重要な要素の1つです。
重要なポイント
Maximum Transmission Unit(MTU)は、BLEのコンテキストにおいて、デバイスが1回のGATT要求で受け入れ可能なアプリケーションプロトコルデータユニット(APDU)の最大サイズです。MTUはATT(属性プロトコル)レベルで定義され、ATTヘッダー(1バイト)+ 有効データを含みます。デフォルトでは、すべてのBLEデバイスは23バイトのMTUをサポートします(23 = 1バイトのATTヘッダー + 22バイトのデータ)。
MTUは無線チャネルの物理的な制限ではなく、GATTレベルでのデバイス間の合意です。リンク層でのBLEパケットの物理サイズはより大きくできますが(BLE 4.0では最大27バイト、Data Length Extensionを使用したBLE 5.0では最大257バイト)、GATT層はトランザクションごとに送信されるデータ量を制限します。Data Length Extension(DLE)は、物理パケットを251バイトに増やす個別のリンク層メカニズムであり、別途ネゴシエーションする必要があります。
MTUとDLEの違い:ATT MTU — GATT要求ごとに送信されるデータ量、DLE — 1つのリンク層パケットに収まるデータ量。最大速度を得るには、両方のパラメータをネゴシエーションする必要があります。DLEがない場合、MTUが247バイトでも、データは複数の27バイトのリンク層パケットに断片化され、スループットが低下します。
MTU Size Request — GATT接続確立後にCentralが開始する手順です。Centralは自身のMTU容量(受け入れ可能な最大サイズ)を指定したMTU Requestを送信します。Peripheralは自身の値を含むMTU Responseで応答します。結果のMTUは2つの値の最小値になります。CentralがMTU 512を提案し、Peripheralが128のみをサポートする場合、接続はMTU 128を使用します。
import CoreBluetooth
// iOSで最大MTUを要求する
func requestMTU(central: CBCentralManager,
peripheral: CBPeripheral) {
peripheral.maximumWriteValueLength(
for: .withoutResponse
)
// iOSは接続時に自動的にMTUをネゴシエーションする
// BLE 5.0デバイスのMTU = 512
let mtu = peripheral.maximumWriteValueLength(
for: .withResponse
)
print("ネゴシエーションされたMTU: " +
String(mtu))
}
ネゴシエーションのタイミング:MTU Requestは、サービス検出(discoverServices)後、アクティブなデータ送信が開始される前に送信する必要があります。iOS Core Bluetoothでは、接続時にMTUが自動的にネゴシエーションされます。開発者が手動でMTU Requestを送信する必要はありません。Androidでは、requestMTUを明示的に呼び出す必要があります。一度ネゴシエーションされると、MTUはその接続に対して固定され、切断して再接続しない限り再ネゴシエーションはできません。
ATT(Attribute Protocol) — GATTが構築されるプロトコルです。ATTパケットの最大サイズは257バイト(ATT_MTU-1)です。このうち、1バイトがOpcode(操作タイプ)、1バイトがHandle、最大255バイトがValueです。したがって、ATT仕様で許可される最大MTUは257バイトですが、実際には一部のオーバーヘッドフィールドが依然として必要であるため、最大251バイトが使用されます。
MTUより大きいデータを送信するには、アプリケーションレベルで断片化(フラグメンテーション)を使用します。開発者は手動でデータを≤ MTUサイズのチャンクに分割し、順次送信します。各チャンクは個別のGATT Write Requestとして送信されます。受信側はチャンクを単一のバッファに組み立てます。GATTには組み込みの断片化サポートはありません。これは開発者の責任です。
| BLEバージョン | 最大MTU | 最大DLE | ATT MTU制限 |
|---|---|---|---|
| BLE 4.0 / 4.1 | 23バイト | 27バイト | ATT固定 |
| BLE 4.2 | 247バイト | 251バイト | 257バイト |
| BLE 5.0 | 251バイト | 251バイト | 257バイト |
| Android + iOS | 512 / 517 | 251バイト | ATT超過 |
興味深い事実:iOSとAndroidはそれぞれMTU 512および517バイトを要求しますが、この値はATT制限を超過しています。実際には、BLEスタックはこれらのデータを自動的に断片化し、それぞれ最大251バイトの複数の順次GATT要求として送信します。開発者にとって、その差は透過的です。iOSではwriteValueは512バイトまでの任意のサイズで動作します。
MTUサイズはBLE接続のスループットに直接影響します。MTU 23バイトの場合、理想的な条件下での最大有効転送レートは約7~10 KB/sです。MTUを247バイトに増やすと、速度は60~90 KB/s(DLEと最適な接続間隔を使用)に向上します。これは、画像、オーディオフラグメント、ログを送信するアプリケーションにとって特に重要です。
BLE転送パフォーマンスは、MTU(ATT要求ごとのデータ量)、接続間隔(交換イベントの頻度)、DLE(リンク層パケットごとのデータ量)の3つの要因に依存します。最大速度のための最適な設定:MTU = 247、DLE = 251、接続間隔 = 7.5 ms(最小値)。
Bluetooth SIG White Paper(2023)によると、接続間隔30 msでMTUを23から247バイトに増やすと、スループットが8 KB/sから42 KB/sに向上します。これは5倍の増加です。接続間隔7.5 msでは、スループットは88 KB/sに達します。高速性を必要としないアプリケーション(温度センサー、BLEビーコン)の場合、標準の23バイトMTUで十分です。
iOS Core Bluetoothは、Peripheralに接続する際にMTUを自動的にネゴシエーションします。開発者はmaximumWriteValueLengthを介して現在のMTUを照会できますが、手動で設定することはできません。iOSはBLE 5.0デバイスで最大512バイト、BLE 4.2で最大247バイトのMTUを使用します。大量のデータを書き込む場合は、配信保証のためにwriteType: .withResponseを使用してください。
// Android(Kotlin)でMTUを要求する
val bluetoothGatt: BluetoothGatt = ...
// MTU 517バイトを要求する
bluetoothGatt.requestMtu(517)
// コールバックで結果を処理する
override fun onMtuChanged(
gatt: BluetoothGatt,
mtu: Int,
status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
println("MTU negotiated: $mtu")
}
}
AndroidはBluetoothGatt.requestMtu(int)を提供し、517バイトまでの任意のMTUを要求できます。実際のMTUは周辺デバイスによって決定されます。デバイスが23バイトのみをサポートする場合、AndroidはMTU 23を返します。現在のMTUを確認するには、gatt.requestMtu(0)を使用します。これは変更を試みずに現在の値を返します。Android 12+は、TRANSPORT_LEを介した接続時の自動MTUネゴシエーションをサポートしています。
クロスプラットフォームフレームワーク(Flutter、React Native)は通常、requestMTUのAPIを提供します。FlutterBlue Plusライブラリでは、MTUは接続パラメータとして設定されます。RxAndroidBleでは、requestMtuメソッドを介して設定します。アプリケーションレベルでの断片化を回避するために、データ送信を開始する前に、サービス検出後すぐに最大MTUをネゴシエーションすることをお勧めします。
OTA(Over-The-Air)ファームウェアアップデートは、BLEで最もMTUを要求するシナリオです。IoTデバイスの一般的なファームウェアサイズは100~500 KBです。MTU 23バイト、接続間隔30 msの場合、100 KBの転送には約2~3分かかります。MTU 247バイト、DLE 251バイトの場合、20~40秒です。MTU 512バイト(iOS)の場合、10~15秒です。
OTAアップデートプロセスには通常、ファームウェアの≤ MTUサイズのパケットへの断片化、Notify/Writeを介した順次送信、各パケットのチェックサム検証、受信確認が含まれます。パケットが失われた場合、デバイスは再送信を要求します。OTAの信頼性は、MTUと接続間隔の適切な選択に大きく依存します。
OTAの推奨事項:最大MTU(247~512バイト)をネゴシエーションし、接続間隔を7.5~15 ms(デバイスがサポートする場合)に設定し、DLE(Data Length Extension)を使用して物理パケットを251バイトに増やします。バッファメモリが限られているデバイス(nRF52ベースのBLEモジュールなど)の場合は、チップ仕様で最大MTUを確認してください。
よくある質問
接続はデフォルトのMTU — 23バイトを使用します。ほとんどのIoTシナリオ(センサー読み取りの送信)ではこれで十分です。大量のデータを転送する場合、速度はネゴシエーションされた247バイトのMTUよりも5~10倍低下します。
いいえ、MTUは接続後に1回ネゴシエーションされ、切断して再接続しない限り変更できません。したがって、アクティブなデータ送信を開始する前に、サービス検出後すぐにMTUをネゴシエーションすることをお勧めします。
これらは各スタックの歴史的に確立された経験的な最大値です。実際のATT MTUは、仕様により依然として257バイトに制限されています。スタックは251バイトを超えるデータを自動的に複数のパケットに断片化するため、512と517の差は重要ではありません。
MTU — ATTレベルでのGATT要求のサイズ。DLE — リンク層での物理パケットのサイズ。DLEがない場合、各GATT要求(最大247バイト)は27バイトのパケットに断片化されます。DLEがある場合、単一のパケットとして送信されます。最大速度を得るには、両方のパラメータをネゴシエーションする必要があります。
心拍数や歩数を送信するフィットネストラッカーの場合、標準の23バイトMTUで十分です。ワークアウト履歴(10~50 KB)を転送する必要がある場合は、スマートフォンに接続する際のデータ同期を高速化するために、MTU 247バイトをネゴシエーションしてください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。