12

I am currently implementing an iOS application that uses CoreBluetooth to transfer data between 2 devices. For example, to send data from the central to the peripheral, I use this code:

NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:nil];
NSLog(@"Writing data of length %d", [data length]);
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];

This works absolutely fine, but the thing is that I have read multiple times now that characteristics can transfer a maximum of 20 bytes at once. Also, when I look at Apple's BTLE Example Code they implement a mechanism that splits the data into chunks of 20 bytes. But the above code prints something like Writing data of length 124 - the data arrives fine at the other device, though.

So: Why is this working at all? And even more so, I am afraid that this could break at some point, especially when non-iOS devices come into play.

BlackWolf
  • 5,239
  • 5
  • 33
  • 60
  • Devices are both iOS7 or iOS6? – Rashad Jun 22 '14 at 10:39
  • @Rashad iOS 7.0 and iOS 7.1.1. And like I said, I am especially interested if this will break with non-iOS devices (which I need to consider) – BlackWolf Jun 22 '14 at 11:48
  • @BlackWolf, did you find any answer to your question. I am unable to find a way to check the ATT_MTU for iPhone. In IOS 9, there is a method - (NSUInteger)maximumWriteValueLengthForType:(CBCharacteristicWriteType)type NS_AVAILABLE(NA, 9_0); , this gives the ATT_MTU. but I dont know if MTU given by this method is valid for all iOS versions. –  Dec 18 '15 at 07:52
  • @BhupeshPruthi As Peter Kämpf mentioned, it seems that devices automatically handshake the ATT_MTU size and use the lower of both. As Jens Schwarzer mentioned, there is a Prepare-Write procedure for larger data, that not all devices support. What I take from this is that to be absolutely sure you shouldn't exceed 20 bytes per write. I therefore ended up actually implementing a chunking mechanism. If I deducted this wrongly and there is a better way please let me know :-) – BlackWolf Apr 06 '16 at 08:39
  • First I thought its 20 byte, but my app is able to transmit 156 bytes of data in one payload. Its working very well. The app supports both classic and BTLE. Its for all iPhones and iPads. Apples says that its default 20 bytes, not maximum. Its up between peripheral and Central to decide the MTU. Also if you will send 20 Bytes in each payload, it will take very long to send x about of data. I am using the app everyday and data is definitely more than 20 bytes in my payloads. –  Apr 06 '16 at 12:45
  • 2
    so how can i increase that size – Mr.G Aug 16 '16 at 10:10
  • "app is able to transmit 156 bytes of data" - and what happens if you try to send more than that? Error? Corrupted data? About chunking - is `writeValue` synchronous and does it actually block while sending or does it just send data to some cache and return? On Android, some cache is being used, but what about iOS? If `writeValue` is async, it might fail at some point even with chunking if you send data faster than BLE connection is able to deal with (and if cache is full, if it's used at all). – JustAMartin May 10 '17 at 13:00

5 Answers5

3

The BLE standard requires 23 bytes as the minimum ATT_MTU (Attribute Protocol Maximum Transmission Unit) which all BLE devices must support. The maximum ATT_MTU is 255 bytes, however, and has been doubled again for BLE 4.2.

BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A]:

All L2CAP implementations shall support a minimum MTU of […] 23 octets over the LE-U logical link; however, some protocols and profiles explicitly require support for a larger MTU.

When establishing a connection, both devices will exchange their ATT_MTU size, and the smaller of both values is used. When Apple started with BLE, they would only support the minimum, but have since expanded the possible size. That is why your 124 bytes work, but the older documentation and sample code uses a much smaller ATT_MTU.

Peter Kämpf
  • 694
  • 11
  • 21
2

I'm using iOS 7.1.1 and have also found that I can send as much as 132 bytes reliably from an iPhone to an iPad using BLE. I also heard that 20 bytes was the max but it sure doesn't seem like it is

Olivia Stork
  • 4,660
  • 5
  • 27
  • 40
2

On iOS 9+ you can use this method on each subscribed CBCentral to determine how much data to send per chunk:

[central maximumUpdateValueLength];
dead_can_dance
  • 129
  • 1
  • 9
2

When you write data to the characteristic of remote device by 



func writeValue(_ data: Data, for characteristic: CBCharacteristic, type: CBCharacteristicWriteType) https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518747-writevalue 



you should use

func maximumWriteValueLength(for type: CBCharacteristicWriteType) -> Int https://developer.apple.com/documentation/corebluetooth/cbperipheral/1620312-maximumwritevaluelength



to figure out the size of chunks to split your data into.

Petro Novosad
  • 119
  • 1
  • 3
1

As a starting point the so-called ATT_MTU size determines the number of bytes that can be written. This value is exchanged/negotiating between the two devices. Unfortunately, this value doesn't seem to be exposed in Apple's CoreBluetooth interface :(

But I have just used a BLE sniffer and in my case it was 158 bytes. But then again this value can change over time...

If you write more data than ATT_MTU then the BlueTooth stack may use a so-called Prepare-Write procedure, but not all stacks supports that. Have not tested if Apple supports this...

:)

Jens Schwarzer
  • 2,840
  • 1
  • 22
  • 35