Android: Sending data >20 bytes by BLE

匿名 (未验证) 提交于 2019-12-03 02:11:02

问题:

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.

回答1:

BLE allow you transfer maximum is 20 bytes.

If you want send more 20 bytes, you should define array byte[] include how many packet you want.

Example worked fine if you want send < 160 characters (160 bytes).

p/s : Let edit followed as u want. Not followed me exactly.

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

The idea is :

  • When we still continue 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 minus Byte 1 & Byte 2) - Attach the message content in here.

Should understand my logic before reading the code in below please.

Below is example about send Message with many packets, each packet : byte[20].

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 can not have enough characters to send continue packet,                  * This is the last packet 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 have enough characters to send continue packet,                  * This is the continue packet 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;             }         }     } 


回答2:

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.



回答3:

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! 


回答4:

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.



回答5:

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.



回答6:

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.



回答7:

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++;         }     } 


回答8:

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


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.


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



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!