19
19
using Plugin . BLE . Abstractions . Contracts ;
20
20
using Plugin . BLE . Abstractions . EventArgs ;
21
21
using System ;
22
+ using System . Collections . Generic ;
22
23
using System . Diagnostics ;
23
24
using System . Text . RegularExpressions ;
24
25
using System . Threading ;
@@ -44,7 +45,7 @@ public class BluetoothInterface : IConnectionInterface
44
45
45
46
private static readonly int LENGTH_COUNTER = 16 ;
46
47
47
- private static readonly int REQUESTED_MTU = 255 ;
48
+ private static readonly int REQUESTED_MTU = 512 ; // Maximum ATT layer MTU size.
48
49
49
50
private static readonly string ERROR_INVALID_MAC_GUID = "Invalid MAC address or GUID, it has to follow the format 00112233AABB or " +
50
51
"00:11:22:33:AA:BB for the MAC address or 01234567-0123-0123-0123-0123456789AB for the GUID" ;
@@ -413,7 +414,7 @@ public void WriteData(byte[] data)
413
414
/// <param name="length">The number of bytes to write.</param>
414
415
/// <exception cref="XBeeException">If there is any error writing data.</exception>
415
416
/// <seealso cref="WriteData(byte[])"/>
416
- public void WriteData ( byte [ ] data , int offset , int length )
417
+ public async void WriteData ( byte [ ] data , int offset , int length )
417
418
{
418
419
// Wait for other device operations.
419
420
deviceSemaphore . Wait ( WRITE_TIMEOUT ) ;
@@ -423,24 +424,27 @@ public void WriteData(byte[] data, int offset, int length)
423
424
throw new XBeeException ( ERROR_CONNECTION_CLOSED ) ;
424
425
}
425
426
426
- // Prepare data to write.
427
- Debug . WriteLine ( "----- WriteData " + HexUtils . ByteArrayToHexString ( data ) ) ;
428
- byte [ ] buffer = new byte [ length ] ;
429
- Array . Copy ( data , offset , buffer , 0 , length ) ;
430
- byte [ ] dataToWrite = encrypt ? encryptor . TransformFinalBlock ( buffer , 0 , buffer . Length ) : buffer ;
431
-
432
427
// Abort the write operation if the write timeout expires.
433
428
CancellationTokenSource cancelToken = new CancellationTokenSource ( WRITE_TIMEOUT ) ;
434
429
bool success = false ;
435
- Task writeTask = Task . Run ( async ( ) =>
430
+ await Task . Run ( async ( ) =>
436
431
{
437
432
// According to BLE.Plugin API documentation, every write operation
438
433
// must be executed in the main thread.
439
434
await MainThread . InvokeOnMainThreadAsync ( async ( ) =>
440
435
{
441
436
try
442
437
{
443
- success = await txCharacteristic . WriteAsync ( dataToWrite , cancellationToken : cancelToken . Token ) ;
438
+ // Split the data in slices with a max length of the current MTU minus 3.
439
+ foreach ( byte [ ] slice in SliceData ( data ) )
440
+ {
441
+ // Write the slices in the TX characteristic. Stop if one of them fails.
442
+ Debug . WriteLine ( "----- WriteData " + HexUtils . ByteArrayToHexString ( slice ) ) ;
443
+ byte [ ] dataToWrite = encrypt ? encryptor . TransformFinalBlock ( slice , 0 , slice . Length ) : slice ;
444
+ success = await txCharacteristic . WriteAsync ( dataToWrite , cancellationToken : cancelToken . Token ) ;
445
+ if ( ! success )
446
+ throw new XBeeException ( ERROR_WRITE ) ;
447
+ }
444
448
}
445
449
catch ( Exception )
446
450
{
@@ -452,8 +456,7 @@ await MainThread.InvokeOnMainThreadAsync(async () =>
452
456
}
453
457
} ) ;
454
458
} ) ;
455
- // Wait for write task to complete.
456
- Task . WaitAll ( writeTask ) ;
459
+
457
460
// Free semaphore.
458
461
deviceSemaphore . Release ( ) ;
459
462
// Check for error.
@@ -463,6 +466,43 @@ await MainThread.InvokeOnMainThreadAsync(async () =>
463
466
}
464
467
}
465
468
469
+
470
+ /// <summary>
471
+ /// Returns a list with the data slices of the given byte array. The
472
+ /// maximum length of each slice is the negotiated MTU minus 3 bytes.
473
+ ///
474
+ /// According to the Bluetooth core specification, the attribute protocol
475
+ /// needs 3 bytes for the opcode (write command) and the attribute handle
476
+ /// (which attribute to write to). So, the actual data length is always
477
+ /// ATT_MTU minus 3.
478
+ /// </summary>
479
+ /// <param name="data">Data to get the slices from.</param>
480
+ /// <returns>A list with the data slices.</returns>
481
+ private List < byte [ ] > SliceData ( byte [ ] data )
482
+ {
483
+ List < byte [ ] > slices = new List < byte [ ] > ( ) ;
484
+
485
+ if ( data . Length <= ( mtu - 3 ) )
486
+ {
487
+ slices . Add ( data ) ;
488
+ }
489
+ else
490
+ {
491
+ int i = 0 ;
492
+ while ( i < data . Length )
493
+ {
494
+ int remainingLength = data . Length - i ;
495
+ int bufferLength = remainingLength < ( mtu - 3 ) ? remainingLength : ( mtu - 3 ) ;
496
+ byte [ ] buffer = new byte [ bufferLength ] ;
497
+ Array . Copy ( data , i , buffer , 0 , bufferLength ) ;
498
+ slices . Add ( buffer ) ;
499
+ i += bufferLength ;
500
+ }
501
+ }
502
+
503
+ return slices ;
504
+ }
505
+
466
506
/// <summary>
467
507
/// Returns the connection type of this bluetooth XBee interface.
468
508
/// </summary>
0 commit comments