web-dev-qa-db-fra.com

Android: Envoi de données> 20 octets par BLE

Je peux envoyer des données jusqu'à 20 octets en me connectant à un périphérique BLE externe. Comment puis-je envoyer des données supérieures à 20 octets. J'ai lu qu'il fallait fragmenter les données ou fractionner la caractéristique en parties requises. Si je suppose que mes données sont de 32 octets, pourriez-vous me dire les modifications à apporter dans mon code pour que cela fonctionne? Voici les extraits requis de mon 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);
}

C'est le code que j'ai utilisé pour envoyer les données. La fonction "send" est utilisée dans l'événement onclick suivant.

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

Lorsque le String text est supérieur à 20 octets, seuls les 20 premiers octets sont reçus. Comment rectifier cela?

Pour tester l'envoi de plusieurs caractéristiques, j'ai essayé ceci:

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

Mais je n’ai reçu que "Test3", c’est-à-dire la dernière caractéristique. Quelle erreur ai-je commis? Je suis nouveau à BLE, alors ignorez toute naïveté

Modifier:

Après avoir accepté answerpour tous ceux qui visionneront cela plus tard.

Il y a deux façons d'accomplir ceci . 1. Divisez vos données et écrivez en boucle comme le fait la réponse sélectionnée . 2. Divisez vos données et écrivez en utilisant le callback, c'est-à-dire onCharacterisitcWrite(). Cela vous évitera des erreurs s'il y en avait pendant l'écriture.

Maisle plus importantentre les écritures utilise un Thread.sleep(200) si vous écrivez uniquement et n'attendez pas de réponse du microprogramme. Cela garantira que toutes vos données atteignent. Sans la sleep, je recevais toujours le dernier paquet. Si vous remarquez la réponse acceptée, il a également utilisé sleep entre les deux.

41
Ankit Aggarwal

BLE vous permet de transférer maximum 20 octets.

Si vous souhaitez envoyer plus de 20 octets, vous devez définir l'octet de tableau [] inclure le nombre de paquets que vous souhaitez. 

L'exemple a bien fonctionné si vous voulez envoyer moins de 160 caractères (160 octets).

p/s: Laissez édité suivi comme vous le souhaitez. Ne m'a pas suivi exactement.

En fait, lorsque nous utilisons BLE, le côté mobile et le côté firmware ont besoin de configurer la clé (par exemple 0x03 ...) pour définir la porte de connexion entre les deux côtés.

L'idée est:

  • Lorsque nous continuons à transférer des paquets, ce n’est pas le dernier. La porte est byte[1] = 0x01.

  • Si nous envoyons le dernier, la porte est byte[1] = 0x00.

La construction de données (20 octets):

1 - Byte 1 - Définissez le Gate ID: ex. ID de porte de message byte[0] = 0x03.

2 - Byte 2 - Définissez la recognization: est le dernier paquet 0x00 ou continuez à envoyer des paquets 0x01.

3 - Byte 3 (Doit être 18 octets après moins Byte 1 & Byte 2) - Attachez le contenu du message ici.

Devrait comprendre ma logique avant de lire le code ci-dessous s'il vous plaît.

Vous trouverez ci-dessous un exemple concernant l'envoi d'un message contenant plusieurs paquets, chaque paquet étant: octet [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

Sur Lollipop, vous pouvez envoyer jusqu'à 512 octets. Vous devez utiliser BluetoothGatt.requestMtu() avec une valeur de 512. De plus, comme l'a mentionné @Devunwired, vous devez attendre la fin de toute opération précédente avant d'appeler cette opération.

20
ThomasW

Il y a beaucoup de tromperies ici.

BLE est capable d'envoyer beaucoup plus de 20 octets, et cela peut être fait facilement dans Android.

Ce que vous devez changer est le MTU de liaison défini par défaut sur 23 (seuls 20 d'entre eux peuvent être utilisés pour définir une valeur) . Android fournit un mécanisme de fragmentation si le paquet à envoyer est plus volumineux que le MTU de lien actuel. (c’est l’objet du paramètre offset dans l’API onCharacteristicRead(...)).

Vous pouvez donc agrandir le MTU en tant que requête de la centrale en utilisant: requestMtu(...) API. Ce dernier provoquera un appel de rappel onMtuChanged du côté périphérique qui l'informera du nouveau MTU . Une fois cette action effectuée, vous pourrez envoyer des paquets plus volumineux sans utiliser le mécanisme de fragmentation Android.

Les alternatives sont de construire vous-même votre propre mécanisme de fragmentation et de ne pas envoyer de paquets plus gros que le MTU ..__ ou d'utiliser le mécanisme Android et de l'utiliser avec le paramètre 'offset'.

8
Sielar

Vous avez raison de dire que la spécification BLE ne permet pas aux opérations d'écriture de dépasser 20 octets. Si vous ne pouvez pas subdiviser votre charge utile en plusieurs caractéristiques (ce qui est logiquement plus facile à gérer), votre mécanisme de segmentation est l’autre approche.

Cependant, sachez que la pile BLEhatelorsque vous essayez de mettre en file d'attente plusieurs opérations. Chaque lecture/écriture est asynchrone, le résultat obtenu via le callback onCharacteristicRead() ou onCharacteristicWrite() sur l'instance BluetoothGattCallback. Le code que vous avez écrit tente d'envoyer trois opérations d'écriture caractéristiques les unes sur les autres, sans attendre le rappel entre les deux. Votre code devra suivre un chemin plus semblable à:

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

Vous pouvez réellement déclencher une écriture BLE Long si le périphérique à l'autre bout le supporte.

Pour ce faire, définissez le type d'écriture sur BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT

Dans ce cas, vous pouvez envoyer plus de 20 octets.

5
Zac Siegel

Si vous souhaitez envoyer de grands ensembles de données via BLE, le mieux est d’utiliser deux caractéristiques, l’une pour envoyer la majeure partie de vos données et l’autre pour envoyer le dernier segment. De cette façon, vous n'avez pas besoin de définir la réponse sur WRITE_NO_RESPONSE et d'utiliser le rappel pour envoyer le segment suivant jusqu'à la fin du dernier segment. Vous pourrez alors l'écrire dans la deuxième caractéristique, ce qui permettra au périphérique de savoir que vous avez terminé d'écrire les données et qu'il peut concaténer toutes les données pour former un grand paquet de données.

4
Zomb

Vous devez demander une mise à jour du MTU. Il s'agit de l'unité de transmission maximale. À l'heure actuelle, BLE accepte jusqu'à 512 octets dans un seul paquet. Cependant, sans demander cette mise à jour de MTU, votre périphérique n'enverra pas de paquet de plus de 23 octets (actuellement).


Avec votre objet BluetoothGatt, appelez requestMtu ()

Voici un lien vers la page du développeur

enter image description here


(BluetoothGattCallback} _ recevra l'événement onMtuChanged () comme indiqué ci-dessous. Une fois la mise à jour réussie de la MTU, vous pouvez envoyer les données en un seul paquet. Voici un lien vers cette page de développeur.

enter image description here


J'appelle généralement requestMtu () après la connexion à la caractéristique à laquelle je souhaite écrire. Bonne chance.

1
FoxDonut

Ceci est l'exemple de la mise en œuvre utilisant la méthode chunk, mais sans utiliser Thread.sleep , j'ai trouvé qu'il était préférable et efficace pour mon application d'envoyer plus de 20 bits de données.

Les paquets seront envoyés après le déclenchement de onCharacteristicWrite(). Je viens de découvrir que cette méthode sera déclenchée automatiquement après que le périphérique (BluetoothGattServer) envoie une méthode sendResponse().

premièrement, nous devons transformer les données de paquet en bloc avec cette fonction:

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

après que nos données soient prêtes, je mets donc mon itération sur cette fonction:

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

Une autre solution avec Queue qui autorise un nombre illimité de messages de toutes tailles (gérez vous-même le protocole pour mettre des délimiteurs de messages) Pas de sommeil, pas de délais supplémentaires:

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