繁体   English   中英

Android:通过 BLE 发送 >20 字节的数据

[英]Android: Sending data >20 bytes by BLE

通过连接到外部 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);
}

这是我用于发送数据的代码。 "send" 函数用于以下 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 的新手,所以请忽略任何幼稚

编辑:

在接受稍后查看此内容的任何人的答案后。

两种方法可以实现这一点。 1. 拆分您的数据并按照所选答案循环书写。 2. 拆分您的数据并使用回调即onCharacterisitcWrite()写入。 如果在编写过程中有任何错误,这将使您免于出错。

最重要的是在写入之间使用Thread.sleep(200)如果您只是在写入而不是等待固件的响应。 这将确保您的所有数据都能到达。 没有sleep我总是得到最后一个包。 如果您注意到接受的答案,他也会在两者之间使用sleep

BLE 允许您最多传输 20 个字节。

如果你想发送超过 20 个字节,你应该定义数组byte[]来包含你想要的数据包数量。

如果您想发送少于 160 个字符(160 字节),以下示例工作正常。

p/s :根据需要编辑以下内容。 不要完全跟着我。

实际上,当我们使用 BLE 时,移动端和固件端需要设置 Key(例如0x03 ...)来定义双方之间的连接门。

这个想法是:

  • 当我们继续传输数据包时,不是最后一个。 门是byte[1] = 0x01

  • 如果我们发送最后一个,门是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 bytes ) - 在这里附上消息内容。

在阅读下面的代码之前,请理解我的逻辑。

下面是一个发送带有多个数据包的 Message 的示例,每个数据包是一个大小为 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个可用于设置值)。 如果要发送的给定数据包大于当前链接 MTU(这是onCharacteristicRead(...) API 中的 offset 参数的目的),Android 提供分段机制。

因此,您可以使用requestMtu(...) API 作为来自中央的请求使 MTU 更大。 后者将在外围侧引起onMtuChanged回调调用,这将通知他新的 MTU。 完成此操作后,您可以发送更大的数据包,而无需发出 Android 分片机制。

替代方案是自己构建自己的分段机制,而不是发送比 MTU 大的数据包。 或者依靠 Android 机制并使用 'offset' 参数使用它。

BLE 规范不允许写操作超过 20 个字节,这是正确的。 如果您无法将有效负载细分为多个特征(这在逻辑上更易于维护),那么您的分块机制是另一种方法。

但是,当您尝试将多个操作排队时,请意识到 BLE 堆栈讨厌 每个读/写都是异步的,其结果是通过BluetoothGattCallback实例上的onCharacteristicRead()onCharacteristicWrite()回调获得的。 您编写的代码尝试在彼此之上发送三个特征写入操作,而无需等待其间的回调。 您的代码将需要遵循更像的路径:

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

如果另一端的设备支持,您实际上可以触发 BLE 长写入。

您可以通过将写入类型设置为BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT来做到这一点

在这种情况下,您可以发送超过 20 个字节。

您需要请求 MTU 更新。 这是最大传输单位。 与现在一样,BLE 在单个数据包中最多接受 512 个字节。 但是,如果不请求此 MTU 更新,您的设备将不会发送超过 23 个字节的数据包(当前)。


使用您的 BluetoothGatt 对象调用requestMtu()

这是开发者页面的链接

在此处输入图片说明


BluetoothGattCallback将接收onMtuChanged()事件,如下所示。 MTU 更新成功后,您可以将数据作为一个数据包发送。 这是此开发人员页面的链接

在此处输入图片说明


我通常在连接到我希望写入的特征后调用 requestMtu()。 祝你好运。

如果您想通过 BLE 发送大量数据,那么最好的办法是使用两个特征,一个发送大量数据,另一个发送最后一段。 这样你就不需要将响应设置为 WRITE_NO_RESPONSE 并使用回调一直发送下一个片段,直到你到达最后一个片段,此时你将把它写入第二个特征,让设备知道您已完成写入数据,并且可以将所有数据连接在一起以形成一个大数据包。

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

我对 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 的数据包。 我无法解释这一点。

这是使用 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++;
        }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM