1

I am sending an image from an Android app to an iOS app over BLE. According to some articles, the max size for sending via BLE is 1 KB, hence I am splitting my image into 1 KB chunks and sending it to the Swift App with a 40ms delay.

However, the chunk size seems to be too big. After debugging, I noticed that the characteristic's value that I am reading from within Swift can only be up to 187 bytes. Hence, when I am reducing my chunk size in Android to that number or smaller, the transfer succeeds, otherwise it does not since the data is cut off.

Does anybody know why it is limited to that size although it should rather be 1 KB? Or, if configurable how I can increase the size?

Here's my Android code:

private void writeImageToCharacteristic() {
    // determine number of chunks and checksum
    final int chunkSize = 1000; // fails with 1000, succeeds with 180
    int numberOfChunks = imageInByte.length / chunkSize;
    numberOfChunks = imageInByte.length == numberOfChunks * chunkSize ? numberOfChunks : numberOfChunks + 1;

    // get the characteristic
    BluetoothGattCharacteristic imgCharacteristic = mBluetoothGattServer
            .getService(SERVICE_UUID)
            .getCharacteristic(IMAGE_CHARACTERISTIC_UUID);

    Log.d(TAG, "Image size in byte: " + imageInByte.length);
    Log.d(TAG, "Number of chunks: " + numberOfChunks);
    Log.d(TAG, "Start sending...");
    updateStateLabel("State: sending image data...");

    // first message contains information about the image
    final String msg = String.valueOf(numberOfChunks);
    imgCharacteristic.setValue(msg.getBytes());
    mBluetoothGattServer.notifyCharacteristicChanged(mConnectedDevice, imgCharacteristic, false);

    // create the chunks (and checksums if configured)
    List<byte[]> messages = new ArrayList<>();
    for(int i = 0; i < numberOfChunks; ++i) {
        final int start = i * chunkSize;
        final int end = start + chunkSize > imageInByte.length ? imageInByte.length : start + chunkSize;
        final byte[] chunk = Arrays.copyOfRange(imageInByte, start, end);
        messages.add(chunk);

        if(includeChecksums) {
            final String chunkCheckSum = calculateCheckSum(chunk);
            messages.add(chunkCheckSum.getBytes());
        }
    }

    // send the data to the device by writing
    // it to the characteristic with a delay of 40 ms
    for(byte[] aMsg : messages) {
        SystemClock.sleep(40);
        imgCharacteristic.setValue(aMsg);
        mBluetoothGattServer.notifyCharacteristicChanged(mConnectedDevice, imgCharacteristic, false);
    }

    Log.d(TAG, "Finished sending");
    updateStateLabel("State: finished sending.");
}

And here is the Swift code that reads it:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    switch characteristic.uuid {
        case Constants.ImageCharacteristicUUID:
            let imgData = characteristic.value!
            // here, the characteristic.value seems to be limited to 187 bytes - why?
            processImageData(data: imgData!)
        default:
            print("Unhandled Characteristic UUID: \(characteristic.uuid)")
    }
}

Appreciating any help.

phoebus
  • 1,280
  • 1
  • 16
  • 36
  • The 1 kB limit is just completely wrong. A characteristic value can be max 512 bytes. How much can be sent in each packet is further defined by the MTU. – Emil Apr 16 '18 at 21:03
  • Thank you for your correction. I will look it up how to change the MTU – phoebus Apr 17 '18 at 09:37

0 Answers0