Подтвердить что ты не робот

Android: отправка данных> 20 байтов BLE

Я могу отправить данные до 20 байтов, подключившись к внешнему устройству BLE. Как отправить данные размером более 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:

После принятия ответа для тех, кто рассматривает это позже.

Есть два способа два. 1. Разделите свои данные и напишите в цикле, как это сделал выбранный ответ. 2. Разделите свои данные и напишите с помощью обратного вызова, т.е. onCharacterisitcWrite(). Это избавит вас от ошибок, если они были во время написания.

Но самый важный между write использует Thread.sleep(200), если вы только пишете и не дожидаетесь ответа от прошивки. Это обеспечит достижение всех ваших данных. Без sleep я всегда получал последний пакет. Если вы заметили принятый ответ, он также использовал sleep между ними.

4b9b3361

Ответ 1

BLE позволяет передавать максимум 20 байтов.

Если вы хотите отправить более 20 байтов, вы должны определить массив byte[] будет содержать сколько пакетов вы хотите.

Следующий пример работал нормально, если вы хотите отправить менее 160 символов (160 байт).

p/s: позвольте редактировать следующее, как вы хотите. Не следуй за мной точно.

На самом деле, когда мы используем BLE, сторона для мобильных устройств и прошивка должны установить ключ (например, 0x03...) для определения шлюза соединения между обеими сторонами.

Идея заключается в следующем:

  • Когда мы продолжаем передавать пакеты, не последний. Гейт является byte[1] = 0x01.

  • Если мы отправим последний, то Gate будет byte[1] = 0x00.

Обработка данных (20 байтов):

1 - Byte 1 - Определить Gate ID: напр. byte[0] = 0x03 идентификатора шлюза сообщения byte[0] = 0x03.

2 - Byte 2 - Определить recognization: последний пакет 0x00 или продолжить отправку пакетов 0x01.

3 - Byte 3 (Должно быть 18 байт после удаления Byte 1 и Byte 2) - Приложите сюда содержание сообщения.

пожалуйста, поймите мою логику, прежде чем читать код ниже.

Ниже приведен пример отправки сообщения со многими пакетами, каждый пакет представляет собой массив размером 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;
            }
        }
    }

Ответ 2

На Lollipop вы можете отправить до 512 байт. Вам нужно использовать BluetoothGatt.requestMtu() со значением 512. Также, как упоминалось в @Devunwired, вам нужно дождаться завершения любой предыдущей операции до вызова это.

Ответ 3

Вы правы, что спецификация BLE не позволяет выполнять операции записи более 20 байтов. Если вы не можете разделить свою полезную нагрузку на несколько характеристик (что логически будет легче поддерживать), то ваш механизм блокировки - это другой подход.

Однако, поймите, что стек BLE ненавидит, когда вы пытаетесь поставить в очередь несколько операций. Каждое чтение/запись является асинхронным, результатом которого является обратный вызов onCharacteristicRead() или onCharacteristicWrite() на экземпляре BluetoothGattCallback. Код, который вы написали, пытается отправить три характерные операции записи друг над другом, не дожидаясь обратного вызова между ними. Ваш код должен будет следовать по пути больше:

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

Ответ 4

Здесь много заблуждений.

BLE способен отправлять гораздо больше 20 байтов, и это можно легко сделать в android.

Что вам нужно изменить, так это MTU по умолчанию, который по умолчанию установлен в 23 (только 20 из них могут использоваться для установки значения). Android обеспечивает механизм фрагментации, если данный пакет для отправки больше, чем текущая ссылка MTU (это назначение параметра offset в API onCharacteristicRead(...)).

Таким образом, вы можете сделать MTU больше, как запрос из центра, используя: requestMtu(...) API. Последний вызовет обратный вызов onMtuChanged на периферийной стороне, который будет информировать его о новом MTU. После этого действия вы можете отправлять большие пакеты без выдачи механизма фрагментации Android.

Альтернативы - создать собственный механизм мечения, а не отправлять пакеты, которые больше MTU. Или полагайтесь на механизм Android и работайте с ним, используя параметр "offset".

Ответ 5

Фактически вы можете запустить BLE Long write, если устройство на другом конце поддерживает его.

Вы можете сделать это, установив тип записи BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT

В этом случае вы можете отправить более 20 байтов.

Ответ 6

Если вы хотите отправлять большие наборы данных по BLE, то лучше всего использовать две характеристики: одну для отправки основной части ваших данных, а другую - для отправки последнего сегмента. Таким образом, вам не нужно устанавливать ответ на WRITE_NO_RESPONSE и использовать обратный вызов для отправки следующего сегмента до тех пор, пока вы не дойдете до последнего сегмента, после чего вы напишете его ко второму признаку, который позволит устройству узнать что вы закончили запись данных и что он может объединить все данные вместе, чтобы сформировать один большой пакет данных.

Ответ 7

Вам нужно запросить обновление MTU. Это максимальная единица передачи. Как и сейчас, BLE принимает до 512 байт в одном пакете. Однако, не запрашивая это обновление MTU, ваше устройство не отправит пакет более 23 байтов (в настоящее время).


Использование вызова объекта BluetoothGatt requestMtu()

Вот ссылка на страницу разработчика

введите описание изображения здесь


BluetoothGattCallback получит событие onMtuChanged(), как показано ниже. После успешного обновления MTU вы можете отправить данные в виде одного пакета. Вот ссылка на эту страницу разработчика.

введите описание изображения здесь


Я обычно вызываю requestMtu() после подключения к характеристике, к которой я хочу писать. Удачи.

Ответ 8

Это пример реализации с использованием метода 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

Еще одно решение с очередью, которое позволяет неограниченное количество сообщений любого размера (управляйте протоколом самостоятельно, устанавливая разделители сообщений). Без сна, без дополнительных задержек:

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