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

时间:2014-06-10 07:53:07

标签: java android bluetooth-lowenergy

我可以通过连接外部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());
    }
});

但我只收到了#34; Test3"即最后一个特征。我犯了什么错误?我是BLE的新手所以请忽略任何天真

编辑:

接受回复后 ,以供日后查看的人使用。

两种方法可以实现此目的。 1.分割数据并按所选答案的循环写入。 2.拆分数据并使用回调即onCharacterisitcWrite()进行写入。如果在写作期间有任何错误,这将使您免于错误。

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

10 个答案:

答案 0 :(得分:28)

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

如果要发送超过20个字节,则应定义数组byte[]以包含所需的数据包数。

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

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

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

这个想法是:

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

  • 如果我们发送最后一个,门是byte[1] = 0x00

数据构造(20字节):

1 - Byte 1 - 定义Gate ID:ex。消息门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;
            }
        }
    }

答案 1 :(得分:19)

在Lollipop上,您最多可以发送512个字节。您需要使用值{512}的BluetoothGatt.requestMtu()。此外,正如@Devunwired所提到的,您需要等到任何先前的操作完成后才能调用它。

答案 2 :(得分:9)

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

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

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

答案 3 :(得分:9)

这里有很多误导。

BLE能够发送超过20个字节,并且可以在android中轻松完成。

您需要更改的是默认设置为23的链接MTU(其中只有20个可用于设置值)。 如果要发送的给定数据包大于当前链接MTU,则Android会提供碎片机制(这是onCharacteristicRead(...) API中offset参数的用途)。

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

替代方法是建立自己的碎片机制,而不是发送比MTU更大的数据包。 或者依赖Android机制并使用'offset'参数来处理它。

答案 4 :(得分:6)

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

您可以通过将写入类型设置为 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT

来执行此操作

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

答案 5 :(得分:5)

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

答案 6 :(得分:4)

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

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

以下是开发人员页面的link

enter image description here

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

enter image description here

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

答案 7 :(得分:2)

另一个使用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();
}

答案 8 :(得分:0)

这是使用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 :(得分:0)

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