-
Notifications
You must be signed in to change notification settings - Fork 583
Tutorial: MTU negotiation
BLE transmission happen in packets. The default size of such a packet is called a Maximum Transfer Unit. The default is 23 bytes. 3 bytes are nominally reserved for lower level data so the user is able to send/read 20 bytes at a time in every such packet.
There is however a possibility to negotiate a higher MTU. It was introduced in Android 5.0 to the API though it was possible to start the negotiation from the peripheral side even before.
There is decent explanation how to optimally choose MTU value by Chris Coleman originally available here. Long story short – optimal MTU in terms of throughput [given default Packet Data Unit (PDU) / no data length extension] is:
n * 27 - 4
Where n
is an integer value. So these are the values:
23, 50, 77, 104, 131, 158, 185, 212, 239, 266, 293, 320, 347, 374, 401, 428, 455, 482, 509...
Below is an example how a MTU negotiating Observable.Transformer<RxBleConnection, RxBleConnection>
could look like:
private ObservableTransformer<RxBleConnection, RxBleConnection> mtuNegotiationObservableTransformer = upstream -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return upstream.doOnSubscribe(ignoredDisposable -> Log.i("MTU", "MTU negotiation is not supported"));
}
return upstream
.doOnSubscribe(ignoredDisposable -> Log.i("MTU", "MTU negotiation is supported"))
.flatMapSingle(connection ->
connection.requestMtu(GATT_MTU_MAXIMUM)
.doOnSubscribe(ignoredDisposable -> Log.i("MTU", "Negotiating MTU started"))
.doOnSuccess(mtu -> Log.i("MTU", "Negotiated MTU: " + mtu))
.ignoreElement()
.andThen(Single.just(connection)));
};
Usage:
Disposable d = bleDevice.establishConnection(false)
.compose(mtuNegotiationObservableTransformer)
.subscribe(
rxBleConnection -> {
},
throwable -> {
}
);
val MtuNegotiationObservableTransformer = object : ObservableTransformer<RxBleConnection, RxBleConnection> {
override fun apply(upstream: Observable<RxBleConnection>): ObservableSource<RxBleConnection> {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
upstream.doOnSubscribe { Log.i("MTU", "MTU negotiation is not supported") }
} else {
upstream.doOnSubscribe { Log.i("MTU", "MTU negotiation is supported") }
.flatMapSingle { connection ->
connection.requestMtu(GATT_MTU_MAXIMUM)
.doOnSubscribe { Log.i("MTU", "Negotiating MTU started") }
.doOnSuccess { Log.i("MTU", "Negotiated MTU: $it") }
.ignoreElement()
.andThen(Single.just(connection))
}
}
}
}
Usage:
val disposable = bleDevice.establishConnection(false)
.compose(MtuNegotiationObservableTransformer)
.subscribe(
{ },
{ }
)