it-swarm.com.de

Android: Senden von Daten> 20 Bytes per BLE

Ich kann Daten mit bis zu 20 Bytes senden, indem ich mich an ein externes BLE-Gerät anschließe. Wie versende ich Daten mit mehr als 20 Bytes? Ich habe gelesen, dass wir die Daten entweder fragmentieren oder in erforderliche Teile aufteilen müssen. Wenn ich davon ausgehe, dass meine Daten 32 Byte umfassen, können Sie mir dann die Änderungen mitteilen, die ich in meinem Code vornehmen muss, damit das funktioniert? Nachfolgend die erforderlichen Ausschnitte aus meinem 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);
}

Dies ist der Code, den ich zum Senden der Daten verwendet habe. Die Funktion "Senden" wird im folgenden Onclick-Ereignis verwendet.

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());
        }
    });

Wenn der String text größer als 20 Bytes ist, werden nur die ersten 20 Bytes empfangen. Wie kann ich das korrigieren?

Um das Senden mehrerer Merkmale zu testen, habe ich Folgendes versucht:

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());
    }
});

Ich habe aber nur "Test3" erhalten, also das letzte Merkmal. Welchen Fehler habe ich begangen? Ich bin neu bei BLE, also ignoriere bitte jegliche Naivität

Edit:

Nach dem Annehmen der Antwortfür alle, die dies später sehen.

Es gibt zwei Möglichkeiten, dies zu erreichen . 1. Teilen Sie Ihre Daten auf und schreiben Sie in einer Schleife, wie es bei der ausgewählten Antwort der Fall ist. 2. Teilen Sie Ihre Daten auf und schreiben Sie sie mit Hilfe des Rückrufs, d. H. onCharacterisitcWrite(). Dadurch werden Sie vor Fehlern geschützt, wenn beim Schreiben Fehler aufgetreten sind.

Aberam wichtigstenzwischen den Schreibvorgängen verwenden eine Thread.sleep(200), wenn Sie nur schreiben und nicht auf eine Antwort der Firmware warten. Dadurch wird sichergestellt, dass alle Ihre Daten erreicht werden. Ohne sleep bekam ich immer das letzte Paket. Wenn Sie die akzeptierte Antwort bemerken, hat er auch sleep dazwischen verwendet.

41
Ankit Aggarwal

BLE erlauben die Übertragung maximal 20 Bytes.

Wenn Sie mehr 20 Bytes senden möchten, müssen Sie das Array-Byte [] mit der Anzahl der gewünschten Pakete definieren. 

Das Beispiel funktionierte gut, wenn Sie <160 Zeichen (160 Byte) senden möchten.

p/s: Lass edit folgen, wie du möchtest. Mir ist nicht genau gefolgt.

Wenn wir BLE verwenden, müssen die mobile Seite und die Firmware-Seite tatsächlich den Schlüssel einrichten (z. B. 0x03 ...), um das Verbindungsgatter zwischen beiden Seiten zu definieren.

Die Idee ist:

  • Wenn wir weiterhin Pakete weiter übertragen, ist dies nicht das letzte. Das Gate ist byte[1] = 0x01.

  • Wenn wir den letzten senden, lautet das Gatter byte[1] = 0x00.

Die Datenstruktur (20 Bytes):

1 - Byte 1 - Definieren Sie den Gate ID: ex. Nachrichtentor-ID byte[0] = 0x03.

2 - Byte 2 - Definieren Sie die recognization: Ist das letzte Paket 0x00 oder senden Sie die Pakete weiter 0x01.

3 - Byte 3 (Sollte nach minus Byte 1 & Byte 2 18 Bytes sein) - Fügen Sie hier den Inhalt der Nachricht ein.

Sollte meine Logik verstehen, bevor Sie den Code unten lesen.

Im Folgenden finden Sie ein Beispiel für die Send Message mit vielen Paketen, jedes Paket: 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;
            }
        }
    }
24
Huy Tower

Auf Lollipop können Sie bis zu 512 Bytes senden. Sie müssen BluetoothGatt.requestMtu() mit einem Wert von 512 verwenden. Wie @Devunwired erwähnt, müssen Sie warten, bis alle vorherigen Vorgänge abgeschlossen sind, bevor Sie dies aufrufen.

20
ThomasW

Es gibt viele Irrtümer hier.

BLE kann viel mehr als 20 Bytes senden, und dies ist in Android problemlos möglich.

Was Sie ändern müssen, ist die Link-MTU, die standardmäßig auf 23 festgelegt ist (nur 20 von ihnen können verwendet werden, um einen Wert festzulegen) . Android bietet einen Fragmentierungsmechanismus, wenn das zu sendende Paket größer ist als die aktuelle Link-MTU (Dies ist der Zweck des Versatzparameters in der onCharacteristicRead(...)-API).

So können Sie die MTU auf Anforderung von der Zentrale mithilfe der folgenden Funktion vergrößern: requestMtu(...) API. Letzteres führt zu einem Callback-Aufruf onMtuChanged an der Peripherie, der ihn über die neue MTU informiert .. Nach dieser Aktion können Sie größere Pakete senden, ohne den Android-Fragmentierungsmechanismus ausgeben zu müssen.

Die Alternativen sind, sich einen eigenen Fragmentierungsmechanismus zu bauen und keine Pakete zu senden, die größer sind als die MTU . Oder Sie setzen auf den Android-Mechanismus und arbeiten mit dem Parameter 'offset'.

8
Sielar

Sie sind richtig, dass die BLE-Spezifikation nicht zulässt, dass Schreibvorgänge 20 Byte überschreiten. Wenn Sie Ihre Nutzlast nicht auf mehrere Merkmale aufteilen können (was logischerweise einfacher zu warten ist), ist Ihr Chunking-Mechanismus der andere Ansatz.

Beachten Sie jedoch, dass der BLE-Stackhasst, wenn Sie versuchen, mehrere Operationen in die Warteschlange zu stellen. Jedes Lesen/Schreiben ist asynchron, wobei das Ergebnis über die Funktion onCharacteristicRead() oder onCharacteristicWrite() callback der Instanz BluetoothGattCallback erhalten wird. Der von Ihnen geschriebene Code versucht, drei charakteristische Schreiboperationen übereinander zu senden, ohne auf den Rückruf dazwischen zu warten. Ihr Code muss einem Pfad folgen, der wie folgt aussieht:

send(Test1)
  -> Wait for onCharacteristicWrite()
  -> send(Test2)
    -> Wait for onCharacteristicWrite()
    -> send(Test3)
      -> Wait for onCharacteristicWrite()
Done!
8
Devunwired

Sie können tatsächlich einen BLE-Long-Write auslösen, wenn das Gerät am anderen Ende dies unterstützt.

Sie können dies tun, indem Sie den Schreibtyp auf BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT setzen

In diesem Fall können Sie mehr als 20 Bytes senden.

5
Zac Siegel

Wenn Sie große Datenmengen über BLE senden möchten, verwenden Sie am besten zwei Merkmale: eine zum Senden des Großteils Ihrer Daten und die andere zum Senden des letzten Segments. Auf diese Weise müssen Sie die Antwort nicht auf WRITE_NO_RESPONSE setzen und das nächste Segment mit dem Rückruf bis zum letzten Segment senden. Dann schreiben Sie das in das zweite Merkmal, das das Gerät informiert dass Sie mit dem Schreiben der Daten fertig sind und dass sie alle Daten zu einem großen Datenpaket zusammenfassen können.

4
Zomb

Sie müssen ein MTU-Update anfordern. Dies ist die maximale Übertragungseinheit. Derzeit akzeptiert BLE bis zu 512 Byte in einem einzelnen Paket. Ohne dieses MTU-Update anzufordern, sendet Ihr Gerät jedoch kein Paket mit mehr als 23 Byte (derzeit).


Verwenden Ihres BluetoothGatt-Objektaufrufs requestMtu ()

Hier ist ein Link zur Entwicklerseite

 enter image description here


BluetoothGattCallback empfängt das Ereignis onMtuChanged () wie unten gezeigt. Bei einem erfolgreichen MTU-Update können Sie die Daten als ein Paket senden. Hier ist ein Link zu dieser Entwicklerseite.

 enter image description here


Normalerweise rufe ich requestMtu () auf, nachdem ich mich mit dem Merkmal verbunden habe, in das ich schreiben möchte. Viel Glück.

1
FoxDonut

Dies ist das Beispiel der Implementierung mit der chunk-Methode, aber ohne Thread.sleep habe ich festgestellt, dass es für meine Anwendung besser und effizienter ist, mehr als 20 Bit-Daten zu senden.

Die Pakete werden nach onCharacteristicWrite() ausgelöst. Ich habe gerade herausgefunden, dass diese Methode automatisch ausgelöst wird, nachdem (BluetoothGattServer) des Peripheriegeräts eine sendResponse() -Methode sendet.

zunächst müssen wir die Paketdaten mit dieser Funktion in Chunk umwandeln:

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

nachdem unsere Daten fertig sind, habe ich meine Funktion auf diese Funktion gestellt:

@Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if(packetInteration<packetSize){
        characteristicData.setValue(packets[packetInteration]);
        mGatt.writeCharacteristic(characteristicData);
            packetInteration++;
        }
    }
0
Doni

Eine weitere Lösung mit Queue, die unbegrenzt Nachrichten beliebiger Größe zulässt (Verwalten Sie das Protokoll selbst, um Nachrichtentrennzeichen zu setzen). Kein Schlaf, keine zusätzlichen Verzögerungen:

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();
}
0
MartinLoren