我可以通过连接外部BLE设备发送最多20个字节的数据。如何发送大于20个字节的数据。我已经读过,我们必须将数据分段或将特征分割为所需的部分。如果我假设我的数据是32个字节,你能否告诉我需要在我的代码中进行更改以使其工作?以下是我的代码所需的片段:
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);
}
这是我用来发送数据的代码。 "发送"函数用于以下onclick事件。
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());
}
});
当String text
大于20个字节时,只接收前20个字节。如何纠正这个?
为了测试发送多个特征,我尝试了这个:
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());
}
});
但我只收到了#34; Test3"即最后一个特征。我犯了什么错误?我是BLE的新手所以请忽略任何天真
编辑:
接受回复后 ,以供日后查看的人使用。
有两种方法可以实现此目的。
1.分割数据并按所选答案的循环写入。
2.拆分数据并使用回调即onCharacterisitcWrite()
进行写入。如果在写作期间有任何错误,这将使您免于错误。
但如果您只是编写而不是等待固件的响应,则写入之间的 最重要的 会使用Thread.sleep(200)
。这将确保您的所有数据都达到。没有sleep
我总是得到最后一个数据包。如果您注意到已接受的答案,他也会在中间使用sleep
。
答案 0 :(得分:28)
BLE允许您传输最多20个字节。
如果要发送超过20个字节,则应定义数组byte[]
以包含所需的数据包数。
如果您想发送少于160个字符(160字节),以下示例正常工作。
p / s:根据需要编辑以下内容。不要完全跟着我。
实际上,当我们使用BLE时,移动端和固件端需要设置密钥(例如0x03
...)来定义双方之间的连接门。
这个想法是:
当我们继续传输数据包时,不是最后一个。门是byte[1] = 0x01
。
如果我们发送最后一个,门是byte[1] = 0x00
。
数据构造(20字节):
1 - Byte 1
- 定义Gate ID
:ex。消息门ID byte[0] = 0x03
。
2 - Byte 2
- 定义recognization
:是最后一个数据包0x00
还是继续发送数据包0x01
。
3 - Byte 3
(消除Byte 1
& Byte 2
后应为18个字节) - 在此处附加邮件内容。
在阅读下面的代码之前请先理解我的逻辑。
以下是发送包含许多数据包的消息的示例,每个数据包是一个大小为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 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;
}
}
}
答案 1 :(得分:19)
在Lollipop上,您最多可以发送512个字节。您需要使用值{512}的BluetoothGatt.requestMtu()
。此外,正如@Devunwired所提到的,您需要等到任何先前的操作完成后才能调用它。
答案 2 :(得分:9)
BLE规范不允许写操作超过20个字节是正确的。如果你不能将你的有效载荷细分为多个特征(这在逻辑上会更容易维护),那么你的分块机制就是另一种方法。
但是,当您尝试排队多个操作时,请认识到BLE堆栈 讨厌 。每次读/写都是异步的,结果来自onCharacteristicRead()
实例上的onCharacteristicWrite()
或BluetoothGattCallback
回调。您编写的代码尝试在彼此之上发送三个特征写操作,而无需等待其间的回调。您的代码需要遵循以下路径:
send(Test1)
-> Wait for onCharacteristicWrite()
-> send(Test2)
-> Wait for onCharacteristicWrite()
-> send(Test3)
-> Wait for onCharacteristicWrite()
Done!
答案 3 :(得分:9)
这里有很多误导。
BLE能够发送超过20个字节,并且可以在android中轻松完成。
您需要更改的是默认设置为23的链接MTU(其中只有20个可用于设置值)。
如果要发送的给定数据包大于当前链接MTU,则Android会提供碎片机制(这是onCharacteristicRead(...)
API中offset参数的用途)。
因此,您可以使用:requestMtu(...)
API作为来自中央的请求,使MTU更大。后者将在外围端引起回叫呼叫onMtuChanged
,这将通知他新的MTU。
完成此操作后,您可以发送更大的数据包,而无需发出Android碎片机制。
替代方法是建立自己的碎片机制,而不是发送比MTU更大的数据包。 或者依赖Android机制并使用'offset'参数来处理它。
答案 4 :(得分:6)
如果另一端的设备支持BLE Long写入,您实际上可以触发它。
您可以通过将写入类型设置为 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
来执行此操作在这种情况下,您可以发送超过20个字节。
答案 5 :(得分:5)
如果您想通过BLE发送大量数据,那么您最好的选择是使用两个特征,一个用于发送大量数据,另一个用于发送最后一个段。这样您就不需要将响应设置为WRITE_NO_RESPONSE并使用回调一直发送下一个段,直到到达最后一个段,此时您将把它写入第二个特征,让设备知道你已经完成了数据的写入,它可以将所有数据连接在一起形成一个大数据包。
答案 6 :(得分:4)
您需要申请MTU更新。这是最大传输单位。就像现在一样,BLE在一个数据包中最多接受512个字节。但是,如果不请求此MTU更新,您的设备将不会发送超过23个字节(当前)的数据包。
使用您的BluetoothGatt对象调用 requestMtu()
以下是开发人员页面的link
BluetoothGattCallback 将收到 onMtuChanged()事件,如下所示。成功更新MTU后,您可以将数据作为一个数据包发送。以下是此开发者页面的link。
我通常在连接到我想写的特征后调用requestMtu()。祝你好运。
答案 7 :(得分:2)
另一个使用Queue的解决方案,它允许无限大小的消息(由您自己管理协议以放置消息定界符)。没有睡眠,没有其他延迟:
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();
}
答案 8 :(得分:0)
这是使用chunk方法实现的示例,但是没有使用 Thread.sleep ,我发现我的应用程序发送超过20位数据更好,更有效。
数据包将在onCharacteristicWrite()
触发后发送。我刚发现外围设备(BluetoothGattServer)
发送sendResponse()
方法后会自动触发此方法。
首先,我们必须使用此功能将数据包数据转换为块:
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;
}
在我们准备好数据后,我将迭代放在这个函数上:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if(packetInteration<packetSize){
characteristicData.setValue(packets[packetInteration]);
mGatt.writeCharacteristic(characteristicData);
packetInteration++;
}
}
答案 9 :(得分:0)
我对Huy Tower的回答非常相似,但是我花了一些时间编写一些更好的代码来做到这一点。这不使用任意延迟,而是使用onCharacteristicWrite回调。我将他的解释作为对此工作原理的理解。
首先,声明两个类范围的变量:
private byte[][] byteMessageToWrite;
private int currentMessageProgress = 0;
这三个函数声明如下:
// 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;
}
最后,在OnCharacteristicWrite函数中,我具有以下内容:
@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++;
}
}
我还将注意到,我选择使用0x01和0x02,而不是像Huy建议的那样使用0x00和0x01。原因是我发现我的蓝牙设备无法成功读取任何0x00的数据包。我无法解释这一点。