外部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());
}
});
しかし、私は「Test3」、すなわち最後の特性のみを受け取りました。どのような間違いを犯しましたか?私はBLEが初めてなので、素朴さを無視してください
Edit:
回答を受け入れた後これを後で見る人のために。
twoこれを達成する方法があります。 1.データを分割し、選択した回答のようにループで書き込みます。 2.データを分割し、コールバック、つまりonCharacterisitcWrite()
を使用して書き込みます。これにより、書き込み中にエラーが発生した場合にエラーを防ぐことができます。
しかしmost important書き込みの間でThread.sleep(200)
を使用するのは、書き込みのみで応答を待っていない場合ファームウェア。これにより、すべてのデータが確実に到達します。 sleep
がなければ、常に最後のパケットを取得していました。受け入れられた答えに気付いた場合、彼はsleep
も使用しています。
BLEでは、最大20バイトを転送できます。
20バイト以上を送信する場合は、必要なパケット数を含む配列byte[]
を定義する必要があります。
次の例は、160文字(160バイト)未満を送信する場合に正常に機能しました。
p/s:必要に応じてフォローを編集しましょう。正確にフォローしないでください。
実際、BLEを使用している場合、モバイル側とファームウェア側はキー(たとえば、0x03
...)を設定して、両側の接続ゲートを定義する必要があります。
アイデアは次のとおりです。
パケットを転送し続けるとき、最後のものではありません。ゲートはbyte[1] = 0x01
です。
最後の1つを送信すると、ゲートはbyte[1] = 0x00
です。
データ構造(20バイト):
1-Byte 1
-Gate ID
を定義します。例:メッセージゲート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;
}
}
}
Lollipopでは、最大512バイトを送信できます。 512の値で BluetoothGatt.requestMtu()
を使用する必要があります。また、@ Devunwiredが述べたように、これを呼び出す前に前の操作が完了するまで待つ必要があります。
ここには多くの誤解があります。
BLEは20バイト以上を送信でき、Androidで簡単に実行できます。
変更する必要があるのは、デフォルトで23に設定されているリンクMTUです(値の設定に使用できるのは20個のみです)。 Androidは、送信する特定のパケットが現在のリンクMTUより大きい場合に断片化メカニズムを提供します(これはonCharacteristicRead(...)
APIのオフセットパラメーターの目的です)。
中央からのリクエストとして、requestMtu(...)
APIを使用してMTUを大きくすることができます。後者は周辺機器側でコールバック呼び出しonMtuChanged
を引き起こし、新しいMTUを通知します。このアクションが完了すると、Androidフラグメンテーションメカニズムを発行せずに、より大きなパケットを送信できます。
代替策は、独自のフラグメンテーションメカニズムを構築し、MTUよりも大きいパケットを送信しないことです。または、Androidメカニズムに依存し、 'offset'パラメーターを使用して操作します。
BLE仕様では、書き込み操作が20バイトを超えることは許可されていません。ペイロードを複数の特性に分割することができない場合(論理的には維持しやすくなります)、チャンクメカニズムがもう1つのアプローチです。
ただし、複数の操作をキューに入れようとすると、BLEスタックhatesであることを認識してください。各読み取り/書き込みは非同期であり、その結果はBluetoothGattCallback
インスタンスのonCharacteristicRead()
またはonCharacteristicWrite()
コールバックを介して送信されます。記述したコードは、コールバックを間に待機することなく、3つの特徴的な書き込み操作を互いの上に送信しようとします。コードは次のようなパスに従う必要があります。
send(Test1)
-> Wait for onCharacteristicWrite()
-> send(Test2)
-> Wait for onCharacteristicWrite()
-> send(Test3)
-> Wait for onCharacteristicWrite()
Done!
実際には、もう一方のデバイスがサポートしている場合、BLE Long書き込みをトリガーできます。
これを行うには、書き込みタイプをBluetoothGattCharacteristic.WRITE_TYPE_DEFAULTに設定します
この場合、20バイト以上を送信できます。
大量のデータセットをBLEで送信する場合、最善の策は、2つの特性を使用することです。1つはデータの大部分を送信し、もう1つは最後のセグメントを送信することです。この方法では、応答をWRITE_NO_RESPONSEに設定し、コールバックを使用して、最後のセグメントに到達するまで次のセグメントを送信する必要はありません。データの書き込みが完了し、すべてのデータを連結して1つの大きなデータパケットを形成できること。
MTUの更新を要求する必要があります。これは最大伝送単位です。現在のように、BLEは単一のパケットで最大512バイトを受け入れます。ただし、このMTU更新を要求しないと、デバイスは23バイトを超えるパケットを送信しません(現在)。
BluetoothGattオブジェクトコールの使用requestMtu()
リンク は開発者のページです
BluetoothGattCallbackは、以下に示すようにonMtuChanged()イベントを受け取ります。 MTUの更新に成功すると、データを1つのパケットとして送信できます。この開発者ページへの リンク です。
通常、書き込みたいcharacteristicに接続してからrequestMtu()を呼び出します。幸運を。
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();
}
これは、チャンクメソッドを使用した実装の例ですが、Thread.sleepを使用せずに、アプリケーションが20ビット以上のデータを送信する方が効率的であることがわかりました。
パケットはafteronCharacteristicWrite()
トリガー後に送信されます。周辺機器(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++;
}
}