How to read all bytes together via Bluetooth?

2019-02-08 20:31发布

问题:

I have an application that uses the bluetooth to receive some data (bytes) from other device. everything is going well, but I have a small issue on receiving the bytes all together. After receiving the bytes I show them on a Toast just to test them. When the other device sends 10 bytes together (for example: "ABCDEFGHIJ"), the program will take the first byte "A" only and show it on a Toast, then go to the second iteration and read the other 9 bytes and show "BCDEFGHIJ" on the Toast. Here is my code:

byte[] buffer = new byte[1024]; // Read 1K character at a time.
int bytes = 0; // Number of bytes.

while(true)
{
    try
    {
        // Read from the InputStream.
        bytes = bInStream.read(buffer);

        // Send the obtained bytes to the MainActivity.
        mainActivityHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
    }
    catch(IOException e)
    {
        connectionLost();
        break;
    }
}

In the MainActivity, I have:

// The Handler that gets information back from the BluetoothManager.
private final Handler handler = new Handler()
{
    @Override
    public void handleMessage(Message msg)
    {
        switch(msg.what)
        {
            case MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;

                // construct a string from the valid bytes in the buffer.
                String readMessage = new String(readBuf, 0, msg.arg1);
                Toast.makeText(MainActivity.this, readMessage, Toast.LENGTH_SHORT).show();
                break;

            // ...
        }
    }
};

How can I receive all the bytes together?!

回答1:

Mmm most likely the culprit is in the way you're sending the messages. Your receive has no problems, it will receive as many bytes (up to your 1024) as it is written to.

If you have no control over the way the messages are sent you can probably read one byte at a time and then send a handler message you when you hit a predefined terminator. Ex: "ABCDEFGHIJ#" where # is the terminator.

String msg = "";
byte ch;
while((ch=mInStream.read())!='#') {
    bytes++;
    msg+=ch;
}


回答2:

The bluetooth connection is stream based, not packet based. There is no guarantee or attempt to preserve packetization. So any number of writes can result in any number of reads, just the stream of bytes are guaranteed to be correct. If you need to detect packets, you need to provide your own packet structure to wrap your data. For example, add a length field before each data packet so you can reconstruct on the receiving side.



回答3:

The accepted answer from @broody is correct. But if the data itself contains '#', it might be difficult to fetch the data. So the best approach according to me is appending '\n' followed by '\r' (or any other characters which are unlikely to feature as data Refer ASCII Table) in the Device which sends data to your Android app. It just acts as Line feed and marks end of data.

Eg: ABCDEFGH\n\r

Then your code can be something like this :

byte[] buffer = new byte[1024];
 while (true) {

         // Read from the InputStream
          buffer[bytes] = (byte) mmInStream.read();                 
         // Send the obtained bytes to the UI Activity
 if ((buffer[bytes] == '\n')||(buffer[bytes]=='\r'))
 {
   mHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
   bytes=0;
 }
 else
 bytes++;
}

Hope it helps Regards



回答4:

To read all bytes together you need to separate the data by "\n" or "\r" or "\r\n" in your code..

For example: If you want to send data from Arduino to Android app via Bluetooth:

(Arduino code):

    int count =0;
    int sensorPin = 0;

    void setup()
    {
      Serial.begin(9600);
    }

    void loop()
    {
    int val= analogRead(sensorPin);
      if(val<threshold){ 
         a++; 
      }
      else{
        delay(2);
        count = count + 1;
        Serial.print(count);
        Serial.print("\n");
          }

And now, to read the sent data (value of variable 'count'), here is the code of Android Studio:

 private class ConnectedThread extends Thread {
        private final InputStream mmInStream;

        public ConnectedThread(BluetoothSocket socket) {
            InputStream tmpIn = null;

            // Get the input streams, using temp objects because
            // member streams are final
            try {
                tmpIn = socket.getInputStream(); // opens the input stream in order to retrieve InputStream objects
            } catch (IOException e) {
            }

            mmInStream = tmpIn;
        }

     public void run() {
        int bytes; // bytes returned from read()
        int availableBytes = 0;

        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                availableBytes = mmInStream.available();
                if(availableBytes>0){
                    byte[] buffer = new byte[availableBytes];  // buffer store for the stream
                    // Read from InputStream
                    bytes = mmInStream.read(buffer); // Get number of bytes and message in "buffer"
                    if (bytes>0){
                        h.obtainMessage(RECEIVE_MESSAGE, bytes, -1, buffer).sendToTarget();     // Send to message queue Handler
                    }
                   }
                } catch (IOException e) {
                break;
                        }
        }
    }

And this:(in onCreate() method):

mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
// The Handler that gets information back
 h = new Handler() { // Handler-->used to communicate b/w UI & BG Thread
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                    case RECEIVE_MESSAGE:// if receive message
                        byte[] readBuf = (byte[]) msg.obj;
                       String strIncom = new String(readBuf, 0, msg.arg1); // create string from bytes array

                        sb.append(strIncom);                                                // append string
                        int endOfLineIndex = sb.indexOf("\n");                            // determine the end-of-line
                        if (endOfLineIndex > 0) {                                            // if end-of-line,
                            String sbprint = sb.substring(0, endOfLineIndex);               // extract string
                            sb.delete(0, sb.length());// and clear
                            Toast.makeText(ledControl.this,sbprint,Toast.LENGTH_SHORT).show();
                            footSteps.setText(sbprint);            // update TextView
                        }
                        break;
                }}};


回答5:

None of the above answers worked for me . Thread.sleep(1000) gave me the solution



回答6:

A way to handle longer-than-buffer messages (can be dangerous because can take up your memory if never cleared) is to handle them in chunks:

String inString = "";
byte[] buffer = new byte[1024];  // buffer store for the stream
int bytes; // bytes returned from read()

while (true) {
    try {
        bytes = mmInStream.read(buffer);
        String chunk = new String(buffer, 0, bytes);
        if(chunk.contains(";"))
        {
            inString += chunk.substring(0,chunk.indexOf(';'));
            Message msg = new Message();
            msg.obj  = inString;
            handler.sendMessage(msg);
            inString =  chunk.substring(chunk.indexOf(';'));
        }
        else
        {
            inString += chunk;
        }
    } catch (IOException e) {
        break;
    }
}