62

I am able to send data upto 20 bytes by connecting to an external BLE device. How do I send data greater than 20 bytes. I have read that we have to either fragment the data or split characteristic to required parts. If I assume my data is 32 bytes, could you tell me changes I need to make in my code to get this working? Following are the required snippets from my code:

public boolean send(byte[] data) {
    if (mBluetoothGatt == null || mBluetoothGattService == null) {
        Log.w(TAG, "BluetoothGatt not initialized");
        return false;
    }

    BluetoothGattCharacteristic characteristic =
            mBluetoothGattService.getCharacteristic(UUID_SEND);

    if (characteristic == null) {
        Log.w(TAG, "Send characteristic not found");
        return false;
    }

    characteristic.setValue(data);
    characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
    return mBluetoothGatt.writeCharacteristic(characteristic);
}

This is the code I used for sending the data. The "send" function is used in the following onclick event.

sendValueButton = (Button) findViewById(R.id.sendValue);
    sendValueButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String text = dataEdit.getText().toString();                           
            yableeService.send(text.getBytes());
        }
    });

When the String text is greater than 20 bytes then only the first 20 bytes are received. How to rectify this?

To test sending multiple characteristics I tried this:

sendValueButton = (Button) findViewById(R.id.sendValue);
sendValueButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String text = "Test1";                           
        yableeService.send(text.getBytes());

        text = "Test2";                           
        yableeService.send(text.getBytes());

        text = "Test3";                           
        yableeService.send(text.getBytes());
    }
});

But I only received "Test3" i.e. the last characteristic. What mistake did I commit? I am new to BLE so please ignore any naiveness

Edit:

After accepting answer for anyone who views this later.

There are two ways to accomplish this. 1. Split up your data and write in a loop as the selected answer does. 2. Split up your data and write using callback i.e. onCharacterisitcWrite(). This will save you from errors if there were any during writing.

But most important between the writes use a Thread.sleep(200) if you are only writing and not waiting for a response from the firmware. This will ensure that all of your data reaches. Without the sleep I was always getting the last packet. If you notice the accepted answer he has also used sleep in between.

Ankit Aggarwal
  • 787
  • 1
  • 9
  • 15
  • Have you tried to split `data` into chunks of 20 bytes and create and send multiple `BluetoothGattCharacteristic` in a loop for every chunk? – Smutje Jun 10 '14 at 08:01
  • what I had tried separately was that in onclick I changed the text three times( some predefined constants) and used `yableeService.send(text.getBytes());` three times. But that only sent the first characteristic, nothing after that. If I did it wrong could you tell what I should have done? – Ankit Aggarwal Jun 10 '14 at 08:10
  • @Smutje: See my editted answer. What else can I do? – Ankit Aggarwal Jun 10 '14 at 12:35
  • Can you please share your complete code? – User6006 Apr 21 '16 at 11:12
  • You can just request a MTU update then send the entire packet of data at once. I believe you can sent a max of 512 bytes at the moment (in one packet). I believe there are 2 or more answers below that mention this. –  Feb 25 '18 at 04:35

10 Answers10

31

UPDATE : Please see more answer to know there is other way to achive your expect.

At 2014, BLE allows you transfer maximum of 20 bytes.

If you want to send more than 20 bytes, you should define array byte[] to contain how many packets you want.

Following example worked fine if you want to send less than 160 characters (160 bytes).

p/s : Let edit following as you want. Do not follow me exactly.

Actually, when we are using BLE, mobile side and firmware side need to set up the Key (ex. 0x03 ...) to define the connection gate among both sides.

The idea is :

  • When we continue to transfer packets, not is the last one. The gate is byte[1] = 0x01.

  • If we send the last one, The gate is byte[1] = 0x00.

The data contruction (20 bytes):

1 - Byte 1 - Define the Gate ID : ex. Message gate ID byte[0] = 0x03.

2 - Byte 2 - Define the recognization : Is the last packet 0x00 or continue sending packets 0x01.

3 - Byte 3 (Should be 18 bytes after eliminating Byte 1 & Byte 2) - Attach the message content in here.

please understand my logic before reading the code below.

Below is an example of sending a Message with many packets, each packet is an array of size 20 bytes.

private void sendMessage(BluetoothGattCharacteristic characteristic, String CHARACTERS){
        byte[] initial_packet = new byte[3];
        /**
         * Indicate byte
         */
        initial_packet[0] = BLE.INITIAL_MESSAGE_PACKET;
        if (Long.valueOf(
                String.valueOf(CHARACTERS.length() + initial_packet.length))
                > BLE.DEFAULT_BYTES_VIA_BLE) {
            sendingContinuePacket(characteristic, initial_packet, CHARACTERS);
        } else {
            sendingLastPacket(characteristic, initial_packet, CHARACTERS);
        }
    }

private void sendingContinuePacket(BluetoothGattCharacteristic characteristic,
            byte[] initial_packet, String CHARACTERS){
        /**
         * TODO If data length > Default data can sent via BLE : 20 bytes
         */
        // Check the data length is large how many times with Default Data (BLE)
        int times = Byte.valueOf(String.valueOf(
                CHARACTERS.length() / BLE.DEFAULT_BYTES_IN_CONTINUE_PACKET));

        Log.i(TAG, "CHARACTERS.length() " + CHARACTERS.length());
        Log.i(TAG, "times " + times);
        
        // TODO
        // 100 : Success
        // 101 : Error
        byte[] sending_continue_hex = new byte[BLE.DEFAULT_BYTES_IN_CONTINUE_PACKET];
        for (int time = 0; time <= times; time++) {
            /**
             * Wait second before sending continue packet 
             */
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (time == times) {
                Log.i(TAG, "LAST PACKET ");
                
                /**
                 * If you do not have enough characters to send continue packet,
                 * This is the last packet that will be sent to the band
                 */

                /**
                 * Packet length byte :
                 */
                /**
                 * Length of last packet
                 */
                int character_length = CHARACTERS.length()
                        - BLE.DEFAULT_BYTES_IN_CONTINUE_PACKET*times;
                
                initial_packet[1] = Byte.valueOf(String.valueOf(character_length
                        + BLE.INITIAL_MESSAGE_PACKET_LENGTH));
                initial_packet[2] = BLE.SENDING_LAST_PACKET;
                
                Log.i(TAG, "character_length " + character_length);

                /**
                 * Message
                 */
                // Hex file
                byte[] sending_last_hex = new byte[character_length];
                
                // Hex file : Get next bytes
                for (int i = 0; i < sending_last_hex.length; i++) {
                    sending_last_hex[i] = 
                            CHARACTERS.getBytes()[sending_continue_hex.length*time + i];
                }

                // Merge byte[]
                byte[] last_packet = 
                        new byte[character_length + BLE.INITIAL_MESSAGE_PACKET_LENGTH];
                System.arraycopy(initial_packet, 0, last_packet,
                        0, initial_packet.length);
                System.arraycopy(sending_last_hex, 0, last_packet, 
                        initial_packet.length, sending_last_hex.length);

                // Set value for characteristic
                characteristic.setValue(last_packet);
            } else {
                Log.i(TAG, "CONTINUE PACKET ");
                /**
                 * If you have enough characters to send continue packet,
                 * This is the continue packet that will be sent to the band
                 */
                /**
                 * Packet length byte
                 */
                int character_length = sending_continue_hex.length;
                
                /**
                 * TODO Default Length : 20 Bytes
                 */
                initial_packet[1] = Byte.valueOf(String.valueOf(
                        character_length + BLE.INITIAL_MESSAGE_PACKET_LENGTH));
                
                /**
                 * If sent data length > 20 bytes (Default : BLE allow send 20 bytes one time)
                 * -> set 01 : continue sending next packet
                 * else or if after sent until data length < 20 bytes
                 * -> set 00 : last packet
                 */
                initial_packet[2] = BLE.SENDING_CONTINUE_PACKET;
                /**
                 * Message
                 */
                // Hex file : Get first 17 bytes
                for (int i = 0; i < sending_continue_hex.length; i++) {
                    Log.i(TAG, "Send stt : " 
                            + (sending_continue_hex.length*time + i));
                    
                    // Get next bytes
                    sending_continue_hex[i] = 
                            CHARACTERS.getBytes()[sending_continue_hex.length*time + i];
                }

                // Merge byte[]
                byte[] sending_continue_packet = 
                        new byte[character_length + BLE.INITIAL_MESSAGE_PACKET_LENGTH];
                System.arraycopy(initial_packet, 0, sending_continue_packet, 
                        0, initial_packet.length);
                System.arraycopy(sending_continue_hex, 0, sending_continue_packet, 
                        initial_packet.length, sending_continue_hex.length);

                // Set value for characteristic
                characteristic.setValue(sending_continue_packet);
            }
            
            // Write characteristic via BLE
            mBluetoothGatt.writeCharacteristic(characteristic);
        }
    }

public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic,
            String data) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return false;
        }

        if (ActivityBLEController.IS_FIRST_TIME) {
            /**
             * In the first time, 
             * should send the Title
             */
            byte[] merge_title = sendTitle(data);

            // Set value for characteristic
            characteristic.setValue(merge_title);

            // Write characteristic via BLE
            mBluetoothGatt.writeCharacteristic(characteristic);

            // Reset
            ActivityBLEController.IS_FIRST_TIME = false;

            return true;
        } else {
            /**
             * In the second time, 
             * should send the Message
             */
            if (data.length() <= BLE.LIMIT_CHARACTERS) {
                sendMessage(characteristic, data);

                // Reset
                ActivityBLEController.IS_FIRST_TIME = true; 

                return true;
            } else {
                // Typed character
                typed_character = data.length();

                return false;
            }
        }
    }
Huy Tower
  • 7,769
  • 16
  • 61
  • 86
  • Could you also write your implementation of `onCharacteristicWrite()`? How have you implemented it? Also I did `characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);` and you have not done this. So do you wait for a reply from the ble device? If I don't want a device what should I do? – Ankit Aggarwal Jun 12 '14 at 13:48
  • @Alex Tran so any idea? – Ankit Aggarwal Jun 12 '14 at 17:27
  • @AnkitAggarwal : `characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)‌` this is not important ...I have not done ... But u need understand my logic before do it. ` So do you wait for a reply from the ble device?` No need wait for reponse from firmware side, your firmware side should be define the key to set up connection as store data after transfering until received full message content. ` If I don't want a device` It can not be. – Huy Tower Jun 13 '14 at 02:14
  • Yes @AlexTran I did not want to wait for a reply. My firmware sets up connection and then stores the entire data being sent. Thanks! – Ankit Aggarwal Jun 13 '14 at 06:08
  • @AnkitAggarwal : `I did not want to wait for a reply`. If you want reply, i think you need notice `Notify` feature, not `Write` feature. – Huy Tower Jun 13 '14 at 06:46
  • if i tried to send data like deepakkumargar.. its sends only deepakkum – Deepak Sep 23 '14 at 12:14
  • why this problem comes to me? – Deepak Sep 23 '14 at 12:16
  • 5
    For something like this, I feel like you should really get a reply via {@code}onCharacteristicWrite(){@code} instead of sleeping the thread for a random amount of time. You remove the risk of (a) not sleeping long enough and (b) sleeping too long, thus make your code very inefficient depending on how many bytes you are sending – AllDayAmazing Feb 03 '15 at 20:24
  • But most important between the writes use a Thread.sleep(200) if you are only writing and not waiting for a response from the firmware. – Huy Tower May 29 '15 at 04:34
  • 2
    Can someone else please explain this in other words and maybe with a small example? What is the package of BLE? If anybody has implemented it, please send links for code. – zed Apr 20 '16 at 07:24
  • @TranDucHuy: Would you please give the value of BLE.INITIAL_MESSAGE_PACKET, BLE.DEFAULT_BYTES_VIA_BLE, etc....It would definitely help save people time. – Antoine Murion Aug 11 '16 at 08:25
  • BLE.INITIAL_MESSAGE_PACKET, BLE.DEFAULT_BYTES_VIA_BLE: you need set value (you talk with firmware side to get the value). – Huy Tower Aug 13 '16 at 12:05
  • 2
    where is sendingLastPacket() and setTitle(). – D.J Dec 13 '16 at 04:42
  • >"BLE allows you transfer maximum of 20 bytes." Had no one ever evaluated how wrong this statement is? – axa Mar 27 '23 at 22:30
23

On Lollipop you can send up to 512 bytes. You need to use BluetoothGatt.requestMtu() with a value of 512. Also, as @Devunwired mentioned you need to wait until any previous operation is complete before calling this.

ThomasW
  • 16,981
  • 4
  • 79
  • 106
20

There are a lot of misleads here.

BLE is capable of sending much more than 20 bytes, and it can be done easily in android.

What you need to change is the link MTU that is set to 23 by default(only 20 of them can be used to set a value). Android provides fragmentation mechanism if the given packet to send is larger than the current link MTU(this is the purpose of the offset parameter in the onCharacteristicRead(...) API).

So you can make the MTU bigger, as a request from the central using: requestMtu(...) API. The latter will cause a callback call onMtuChanged at the peripheral side which will inform him of the new MTU. After this action is done, you can send bigger packets without issuing the Android fragmentation mechanism.

The alternatives are to build yourself your own fragmetation mechanism and not to send packets that are bigger then the MTU. Or rely on the Android mechanism and work with it using the 'offset' parameter.

D.J
  • 1,439
  • 1
  • 12
  • 23
Sielar
  • 292
  • 2
  • 10
  • 1
    This is only supported in Lollipop, and only for devices that support changing MTU size (not very common yet). Also, using the offset parameter has nothing to do with sending bigger chunks, it just overwrites the characteristic read at that offset. – zed Apr 20 '16 at 11:50
  • Surely, you mean `BluetoothGattServerCallback.onCharacteristicReadRequest()`, not `BluetoothGattCallback.onCharacteristicRead()`. – Albus Dumbledore May 09 '18 at 08:50
11

You need to request a MTU update. This is the maximum transmission unit. As it is now, BLE accepts up to 512 bytes in a single packet. However, without requesting this MTU update, your device will not send a packet over 23 bytes (currently).


Using your BluetoothGatt object call requestMtu()

Here is a link to the developer's page

enter image description here


BluetoothGattCallback will receive the onMtuChanged() event as shown below. Upon a successful MTU update, you can send the data as one packet. Here is a link to this developer page.

enter image description here


I typically call requestMtu() after connecting to the characteristic that I wish to write to. Good Luck.

  • 1
    FYI it does also change the maximumlength for read and notify not only for write. Thx very much for the help – save_jeff Apr 05 '20 at 09:49
10

You are correct that the BLE specification doesn't allow write operations to exceed 20 bytes. If you can't subdivide your payload over multiple characteristics (which is logically going to be easier to maintain), then your chunking mechanism is the other approach.

However, realize that the BLE stack hates when you try to queue up multiple operations. Each read/write is asynchronous, which the result coming via the onCharacteristicRead() or onCharacteristicWrite() callback on the BluetoothGattCallback instance. The code you've written attempts to send three characteristic write operations on top of each other, without waiting for the callback in between. Your code will need to follow a path more like:

send(Test1)
  -> Wait for onCharacteristicWrite()
  -> send(Test2)
    -> Wait for onCharacteristicWrite()
    -> send(Test3)
      -> Wait for onCharacteristicWrite()
Done!
devunwired
  • 62,780
  • 12
  • 127
  • 139
  • so how does onCharacteristicWrite() work? Could you add a code snippet implmenting onCharacteristicWrite() too? And could you also give an example with multiple characteristics (they will be added in the `send` method right?). I want to test the speeds to see which suits my work. Thanks! – Ankit Aggarwal Jun 10 '14 at 15:11
  • Sample application that should give you a good idea of how to wait for a callback before calling the next function: https://github.com/devunwired/accessory-samples/blob/master/BluetoothGatt/src/com/example/bluetoothgatt/MainActivity.java – devunwired Jun 10 '14 at 16:52
  • I had not implemented onCharacteristicWrite(). I went through your code and I see that you are reading after onCharacteristicWrite(). I don't want to do this, so can I just have @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //After writing the enable flag, next we read the initial value return; } – Ankit Aggarwal Jun 10 '14 at 17:20
  • 1
    You can do whatever you like in that callback. In your case, that would be to send the next write. – devunwired Jun 10 '14 at 17:43
  • I cannot send the next write as the onClickListener() and the "send" functions are in two different files and I want to keep onCharacteristicWrite() generic. So can I implement onCharacteristicWrite() and keep it empty with just a return statement (I have added that to the question to show)? I don't have the hardware with me now and I have to submit this tomorrow that's why I'm trying to be extra sure. – Ankit Aggarwal Jun 10 '14 at 18:13
  • So what I am saying is that all code remains same, I just implement a onCharacteristicWrite() with just a return statement. Will that solve the problem? – Ankit Aggarwal Jun 10 '14 at 18:15
  • And instead of `return mBluetoothGatt.writeCharacteristic(characteristic);` being the last line of `send()` I will do `boolean val = mBluetoothGatt.writeCharacteristic(characteristic);` and then `return val`. So before returning the `onCharacteristicWrite()` would be called. – Ankit Aggarwal Jun 10 '14 at 18:21
  • @Devunwired Also just to confirm, if I add a `Thread.sleep(1000)` between each will the 3 values be sent right? I know this is not optimal, but I just want to confirm? – Ankit Aggarwal Jun 12 '14 at 14:34
  • They probably will, since that would normally be long enough or the callbacks to fire in between. But definitely smarter to wait for each callback before sending the next message. – devunwired Jun 13 '14 at 21:14
  • 1
    @Devunwired Would the approach in your answer be faster than sending 20 bytes at a time on a single characteristic or would they end up being equivalent throughput? – Sean Aug 25 '14 at 23:11
8

You can actually trigger a BLE Long write if the device on the other end supports it.

You can do this by setting the write type to be BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT

In this case you can send more than 20 bytes.

Zac Siegel
  • 147
  • 1
  • 5
  • Any official documentation about this feature ? Long write is only supported with ATT write request (acknowledge from the peer device) ? – metch May 17 '16 at 18:49
  • 1
    There is no official docs but if you look at the device driver code in the AOSP project you can see the capability is supported. – Zac Siegel May 18 '16 at 17:06
  • 2
    Where can I find this code? Can you add a link to your answer please? Thank you! – Christopher Sep 04 '17 at 12:29
4

If you want to send large sets of data over BLE, then your best bet is to use two characteristics, one to send the bulk of your data and the other to send the last segment. This way you don't need to set the response to WRITE_NO_RESPONSE and use the callback to send the next segment all the way until you get to the last segment, at which point you will write that to the second characteristic which will let the device know that you are done writing the data and that it can concatenate all the data together to form one large data packet.

Zomb
  • 911
  • 1
  • 7
  • 12
  • 1
    I am sorry but I am new to ble. Could you explain your idea with a code snippet using what I have already written? It will make it clearer for me. – Ankit Aggarwal Jun 10 '14 at 15:06
  • Should I make another characteristic in the `send` method? Pls explain with code. – Ankit Aggarwal Jun 10 '14 at 15:07
  • You can't make another characteristic in the second method, the device you are sending it to needs to have another characteristic that it uses to listen for the last segment of data. The buffering of the data has to happen on the BLE device side. Is the device you are sending the data to designed to work with large packets of data? – Zomb Jun 10 '14 at 15:26
  • I am not sure. I will have to confirm that. But if that is not possible, then is the other answer possible? That is queueing up the data? – Ankit Aggarwal Jun 10 '14 at 15:30
  • You can queue the data, but the device needs to be able to understand that you are sending one huge chunk of data through multiple writes. If you can get the data split then you need to write it down like @Devunwired mentioned below. To figure out how it works, you have to understand the callback mechanism that Android has which includes how onCharacteristicWrite works. I would recommend working through the example here: http://developer.android.com/guide/topics/connectivity/bluetooth-le.html – Zomb Jun 10 '14 at 16:24
  • Also just to confirm, if I add a `Thread.sleep(1000)` the 3 values would be sent right? I know this is not optimal, but I just want to confirm? – Ankit Aggarwal Jun 12 '14 at 14:31
  • You should not be doing that. It makes you read and writes very slow and gives you absolutely no feedback if the write happened or not, and what errors are occurring. Without the callback system in place, you will not be able to subscribe to notifications either. – Zomb Jun 12 '14 at 23:25
  • Yes, thanks @Zomb. So finally I am using the `oncharacteristicWrite()` to check for errors, but since the process was too fast I was always receiving the last part of my data. So I have added a `Thread.sleep(100)` after each write which gives enough time to receive the data. Thanks again! – Ankit Aggarwal Jun 13 '14 at 06:05
  • @Zomb how do you inform the remote device that you will be sending two packets or more? – zed Apr 20 '16 at 11:41
  • 1
    @zed not sure I understand your question. The remote device is listening to both characteristics. If it sees a write on the first one, it will always assume that there is at least one more write that has to happen. As long as you keep writing to the first one, it will wait. When you have your last packet, you will write it to the second characteristic. – Zomb Apr 20 '16 at 14:33
  • @Zomb So if I want to write a characteristic that is 32 bytes long (e.g. "123456789-123456789-123456789-12"), I would do a `characteristic.setValue("123456789-123455678"); bluetoothGatt.writeCharacteristic(characteristic);`, how would I inform it that I want to send more data? If in `onCharacteristicWrite()` I write again, only the last set of bytes will be saved "9-123456789-12". – zed Apr 20 '16 at 14:39
  • 1
    You need two characteristics, so you would do char1.setValue("123456789-123455678") and btGatt.writeChar(char1). At this point in time your device caches the char1 value. You then do char2.setValue(9-123456789-12") and btGatt.writeChar(char2). Now you device knows that all data transmission is complete and it concatenates char1 and char2. – Zomb Apr 20 '16 at 14:46
4

Another solution with Queue and that allow unlimited messages of any size (manage the protocol by yourself to put message delimiters). No Sleep, no additional delays:

private volatile boolean isWriting; 
private Queue<String> sendQueue; //To be inited with sendQueue = new ConcurrentLinkedQueue<String>();

public int send(String data) {
    while (data.length()>20) {
        sendQueue.add(data.substring(0,20));
        data=data.substring(20);
    }
    sendQueue.add(data);
    if (!isWriting) _send();
    return ST_OK; //0
}

private boolean _send() {
    if (sendQueue.isEmpty()) {
        Log.d("TAG", "_send(): EMPTY QUEUE");
        return false;
    }
    Log.d(TAG, "_send(): Sending: "+sendQueue.peek());
    tx.setValue(sendQueue.poll().getBytes(Charset.forName("UTF-8")));
    isWriting = true; // Set the write in progress flag
    mGatt.writeCharacteristic(tx);
    return true;
}

@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicWrite(gatt, characteristic, status);
    if (status == BluetoothGatt.GATT_SUCCESS) {
        Log.d("TAG","onCharacteristicWrite(): Successful");
    }
    isWriting = false;
    _send();
}
MartinLoren
  • 61
  • 1
  • 6
2

I have a very similar answer to Huy Tower, but I have taken the time to write some nicer code to do it. This doesn't use an arbitrary delay, it instead uses the onCharacteristicWrite callback.I would reference his explanation as an understandign of how this works.

Firstly, two class scoped variables are declared:

private byte[][] byteMessageToWrite;
private int currentMessageProgress = 0;

Then three functions are declared as follows:

// Send a message
    public void sendMessage(String strMessage) {
        if (characteristic == null || strMessage == null || strMessage.isEmpty()) {
            // Do nothing if there is no device or message to send.
            return;
        }



        //Note that byte arrays after the first are written in the onCharacteristicWrite Callback

        byteMessageToWrite = getMessageByteArray(strMessage);
        writeBytes(byteMessageToWrite[0]);
        currentMessageProgress = 1;
    }

    //writes an array of bytes
    //note the max BLE message limit
    private void writeBytes(byte[] bytesToWrite) {
        characteristic.setValue(bytesToWrite);
    }

    // Note: BLE Only allows 20 bytes to be written to a characteristic at once. Have to write
    // multiple times if sending larger data. This function breaks a string up to do that.
    // The first byte is reserved as a key
    // Note, the second byte in every 20byte section is either a 1 or a 2. A 2 indicates that it is
    // The last message in the set
    private byte[][] getMessageByteArray(String message) {
        byte[] initBytes = message.getBytes(Charset.forName("UTF-8"));
        int currentIndex = 0;
        int msgLength = initBytes.length;

        int numMessages = (int) (Math.ceil((double) (Math.ceil((double) msgLength) / (double) (BLE_BYTE_LIMIT-2))));

        byte[][] retMessage = new byte[numMessages][20];

        for (int i = 0; i < numMessages; i++) {
            //set key
            retMessage[i][0] = 0x03;
            //set second byte (indicator of termination)
            if (i == numMessages - 1) {//final message, set byte 1 to 2
                retMessage[i][1] = 0x02;
            } else {//not final message
                retMessage[i][1] = 0x01;
            }
            //set other bytes
            for (int ii = 2; ii < BLE_BYTE_LIMIT; ii++) {// fill in the data
                int index = (i * (BLE_BYTE_LIMIT - 2)) + ii - 2;
                if(index>=msgLength){
                    // Have reached the end of the message, don't fill any other bytes
                    return retMessage;
                }
                retMessage[i][ii] = initBytes[index];
            }
        }
        return retMessage;
    }

Finally, in the OnCharacteristicWrite function, I have the following:

      @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
        if (currentMessageProgress < byteMessageToWrite.length) {
            //there are more bytes to write

            writeBytes(byteMessageToWrite[currentMessageProgress]);
            currentMessageProgress++;
        }
    }

I will also note that instead of using 0x00 and 0x01 as Huy suggested, I have chosen to use 0x01 and 0x02. The reason for this is that I found that my bluetooth device would not successfully read any packets with a 0x00. I cannot explain this.

user1814893
  • 301
  • 2
  • 6
1

This is the example of implementation using chunk method, but without using Thread.sleep , i found it is better and efficient for my application to send more than 20 bit data.

The packets will be send afteronCharacteristicWrite() triggered. i just found out this method will be triggered automatically after peripheral device (BluetoothGattServer) sends a sendResponse() method.

firstly we have to transform the packet data into chunk with this function:

public void sendData(byte [] data){
    int chunksize = 20; //20 byte chunk
    packetSize = (int) Math.ceil( data.length / (double)chunksize); //make this variable public so we can access it on the other function

    //this is use as header, so peripheral device know ho much packet will be received.
    characteristicData.setValue(packetSize.toString().getBytes());
    mGatt.writeCharacteristic(characteristicData);
    mGatt.executeReliableWrite();

    packets = new byte[packetSize][chunksize];
    packetInteration =0;
    Integer start = 0;
    for(int i = 0; i < packets.length; i++) {
        int end = start+chunksize;
        if(end>data.length){end = data.length;}
        packets[i] = Arrays.copyOfRange(data,start, end);
        start += chunksize;
    }

after our data ready, so i put my iteration on this function:

@Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if(packetInteration<packetSize){
        characteristicData.setValue(packets[packetInteration]);
        mGatt.writeCharacteristic(characteristicData);
            packetInteration++;
        }
    }
D.J
  • 1,439
  • 1
  • 12
  • 23
Doni
  • 576
  • 6
  • 9