diff --git a/README.md b/README.md
index c0240c5..5e84cd8 100644
--- a/README.md
+++ b/README.md
@@ -405,88 +405,56 @@ UniversalBle.disableBluetooth();
```dart
int mtu = await bleDevice.requestMtu(256);
-```
+````
+
+> ⚠️ Note: Requesting an MTU is a *best-effort* operation.
+> On many platforms the final MTU is fully controlled by the OS and remote device.
#### Platform Limitations
-On most platforms, the MTU can only be queried but not manually set:
+MTU negotiation is largely platform- and stack-managed, and often cannot be
+explicitly controlled by applications:
-- **iOS/macOS**: System automatically sets MTU to 185 bytes maximum
-- **Android 14+**: System automatically sets MTU to 517 bytes for the first GATT client
-- **Windows**: MTU can only be queried
-- **Linux**: MTU can only be queried
-- **Web**: No mechanism to query or modify MTU size
+* **iOS / macOS**
+
+ * MTU is fully OS-managed; apps cannot request or set it.
+ * Historically ~185 bytes, but modern devices may negotiate larger MTUs
+ (≈247–517) automatically.
+
+* **Android**
+
+ * **Android ≤ 13**: Apps may request MTU once per connection (up to 517).
+ If never requested, the default MTU is 23.
+ * **Android 14+**: The first GATT client effectively drives MTU negotiation
+ to 517 (or the link’s maximum); subsequent MTU requests are ignored.
+
+* **Windows**
+
+ * MTU is automatically negotiated by the OS.
+ * Apps cannot set it; they can only query the effective PDU size.
+
+* **Linux (BlueZ)**
+
+ * MTU is negotiated automatically by default.
+ * The standard D-Bus GATT API does not expose MTU control.
+ * MTU can be requested via BlueZ tools or lower-level APIs, but most apps
+ treat it as stack-defined.
+
+* **Web**
+
+ * MTU is negotiated internally by the browser/OS.
+ * No API exists to query or modify the MTU size.
#### Best Practices
When developing cross-platform BLE applications and devices:
-- Design for default MTU size (23 bytes) as default
-- Dynamically adapt to use larger packet sizes when the system provides them
-- Take advantage of the increased throughput when available without requiring it
-- Implement data fragmentation for larger transfers
-- Handle platform-specific MTU size based on current value
+* Always design for the default ATT MTU (23 bytes)
+* Treat MTU requests as opportunistic, not guaranteed
+* Dynamically adapt packet sizes based on the negotiated MTU
+* Implement application-level fragmentation for larger payloads
+* Take advantage of higher MTUs when available, without depending on them
-#### Resetting State on Hot Restart
-
-During Flutter hot restart in debug mode, the app state is reset but native Bluetooth connections and scan operations may persist. This can lead to connection issues or stale state.
-
-
-Use the following helper function to properly clean up BLE state before your app restarts.
-
-```dart
-void main() async {
- WidgetsFlutterBinding.ensureInitialized();
- // Reset BLE state before app initialization
- await resetBleState();
- runApp(MyApp());
-}
-
-/// Resets BLE state by stopping scans and disconnecting all devices.
-/// Make sure you have Bluetooth permissions before calling this function.
-Future resetBleState() async {
- // Skip reset in release mode or on web
- if (!kDebugMode || kIsWeb) return;
-
- // Check Bluetooth availability
- AvailabilityState availabilityState =
- await UniversalBle.getBluetoothAvailabilityState();
-
- // Skip if Bluetooth is not powered on
- if (availabilityState != AvailabilityState.poweredOn) {
- debugPrint('Reset: Bluetooth is not powered on');
- return;
- }
-
- // Stop scanning
- if (await UniversalBle.isScanning()) {
- debugPrint('Reset: Stopping scan');
- await UniversalBle.stopScan();
- }
-
- // Disconnect all connected devices
- List withServices = [];
-
- // On Apple platforms, you must specify services to discover connected devices
- if (defaultTargetPlatform == TargetPlatform.macOS ||
- defaultTargetPlatform == TargetPlatform.iOS) {
- // Replace with your known device service UUIDs
- withServices = ["0x180A"];
- }
-
- List connectedDevices =
- await UniversalBle.getSystemDevices(withServices: withServices);
-
- for (var device in connectedDevices) {
- debugPrint('Reset: Disconnecting device: ${device.deviceId}');
- await UniversalBle.disconnect(device.deviceId);
- }
-
- debugPrint('Reset: Done');
-}
-```
-
-
## Command Queue
@@ -770,6 +738,67 @@ void main() async {
}
```
+## Resetting State on Hot Restart
+
+During Flutter hot restart in debug mode, the app state is reset but native Bluetooth connections and scan operations may persist. This can lead to connection issues or stale state.
+
+
+Use the following helper function to properly clean up BLE state before your app restarts.
+
+```dart
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ // Reset BLE state before app initialization
+ await resetBleState();
+ runApp(MyApp());
+}
+
+/// Resets BLE state by stopping scans and disconnecting all devices.
+/// Make sure you have Bluetooth permissions before calling this function.
+Future resetBleState() async {
+ // Skip reset in release mode or on web
+ if (!kDebugMode || kIsWeb) return;
+
+ // Check Bluetooth availability
+ AvailabilityState availabilityState =
+ await UniversalBle.getBluetoothAvailabilityState();
+
+ // Skip if Bluetooth is not powered on
+ if (availabilityState != AvailabilityState.poweredOn) {
+ debugPrint('Reset: Bluetooth is not powered on');
+ return;
+ }
+
+ // Stop scanning
+ if (await UniversalBle.isScanning()) {
+ debugPrint('Reset: Stopping scan');
+ await UniversalBle.stopScan();
+ }
+
+ // Disconnect all connected devices
+ List withServices = [];
+
+ // On Apple platforms, you must specify services to discover connected devices
+ if (defaultTargetPlatform == TargetPlatform.macOS ||
+ defaultTargetPlatform == TargetPlatform.iOS) {
+ // Replace with your known device service UUIDs
+ withServices = ["0x180A"];
+ }
+
+ List connectedDevices =
+ await UniversalBle.getSystemDevices(withServices: withServices);
+
+ for (var device in connectedDevices) {
+ debugPrint('Reset: Disconnecting device: ${device.deviceId}');
+ await UniversalBle.disconnect(device.deviceId);
+ }
+
+ debugPrint('Reset: Done');
+}
+```
+
+
+
## Low level API
For more granular control, you can use the [Low-Level API](README.low_level.md). This API is "Device ID"-based, offering greater flexibility by enabling direct calls without the need for object instances.
diff --git a/lib/src/extensions/ble_device_extension.dart b/lib/src/extensions/ble_device_extension.dart
index 55ca002..753b721 100644
--- a/lib/src/extensions/ble_device_extension.dart
+++ b/lib/src/extensions/ble_device_extension.dart
@@ -22,7 +22,13 @@ extension BleDeviceExtension on BleDevice {
/// Disconnects from the device.
Future disconnect() => UniversalBle.disconnect(deviceId);
- /// Requests a specific MTU (Maximum Transmission Unit) size for the connection.
+ /// Requests an MTU (Maximum Transmission Unit) value for the connection.
+ ///
+ /// **⚠️ Note:** Requesting an MTU is a *best-effort* operation. The final MTU is
+ /// often controlled by the OS and remote device. Returns the negotiated MTU value,
+ /// which may differ from `expectedMtu`.
+ ///
+ /// See [UniversalBle.requestMtu] for platform limitations and best practices.
Future requestMtu(int expectedMtu) =>
UniversalBle.requestMtu(deviceId, expectedMtu);
diff --git a/lib/src/universal_ble.dart b/lib/src/universal_ble.dart
index 05c614d..f485841 100644
--- a/lib/src/universal_ble.dart
+++ b/lib/src/universal_ble.dart
@@ -313,9 +313,21 @@ class UniversalBle {
);
}
- /// Request MTU value.
- /// It will **attempt** to set the MTU (Maximum Transmission Unit) but it is not guaranteed to succeed due to platform limitations.
- /// It will always return the current MTU.
+ /// Requests an MTU (Maximum Transmission Unit) value for the connection.
+ ///
+ /// **⚠️ Note:** Requesting an MTU is a *best-effort* operation. On many platforms
+ /// the final MTU is fully controlled by the OS and remote device. This method
+ /// returns the current/negotiated MTU value, which may differ from `expectedMtu`.
+ ///
+ /// **Platform Limitations:**
+ /// * **iOS/macOS**: MTU is OS-managed; apps cannot request it (~185-517 bytes auto-negotiated)
+ /// * **Android ≤13**: May request once per connection (up to 517), default is 23
+ /// * **Android 14+**: First GATT client drives MTU to 517; subsequent requests ignored
+ /// * **Windows/Linux**: MTU is automatically negotiated; apps can only query it
+ /// * **Web**: Not supported (no API available)
+ ///
+ /// **Best Practices:** Design for default ATT MTU (23 bytes), treat requests as
+ /// opportunistic, and implement fragmentation for larger payloads.
static Future requestMtu(
String deviceId,
int expectedMtu, {