Reputation: 628
I'm working on an Android app, which uses bluetooth connection to transfer data between my android smartphone and a non-android bluetooth module, using SPP profile. I've used Bluetooth Chat Example from Android Developer site as reference.
I've successfully made two devices connect to each other and sent simple strings from the smart phone to the bluetooth module. But I've got some error in reading data sent back from the module. I've used the following code, which is exactly the same as in Bluetooth Chat Example, to read data from InputStream
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
String str = new String(buffer);
Log.i(TAG, "mmInStream - " + str);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
When my bluetooth module send a simple string to the phone, that string is not received correctly. It is splited into several pieces in random ways. For example, if I send "1234567890abcdef1234567890abcdef0123456789" for three times to the phone, the Logcat on Eclipse will log these:
mmInstream - 12345678910abcdef��������(continuing null)
mmInstream - 1��������(continuing null)
mmInstream - 2345678910abcdef0123456789��������(continuing null)
for the first time. In the second and the third time data is transmitted, it is received in a difference pieces:
mmInstream - 1234567891�������(continuing null)
mmInstream - 0abcdef012�������(continuing null)
mmInstream - 3456789���������(continuing null)
mmInstream - 1234567891����������������(continuing null)
mmInstream - 0abcdef0123456789������������(continuing null)
I don't know why this happen and how to solve this problem. If data is received in a arbitrary way like this, I can't get necessary data to process. How can I get it in one pieces?
Any help would be appreciated.
Many Thanks.
Upvotes: 17
Views: 24724
Reputation: 1
I looked for solving of this problem. And found it: It's a bad idea to send buffer which could be reload. So i did this: ( i send not the buffer but cloned RXbuffer)
Now I have 0 errors for 1000 blocks (for 32 bytes each) instead 20% errors before;
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
byte[] bufferRX = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (mState == STATE_CONNECTED) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
bufferRX = buffer.clone();
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, bufferRX)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
Upvotes: 0
Reputation: 33
I have used last two codes, they work well, though when connection is lost user interface does not get notified and so state does not change to STATE_NONE. In my case I want app to try to reconnect last device whenever connection is lost!! after trying many ways I finally solved the problem this way:
"buffer" then is sent to User Interface, and tmp_msg is reinitialized to ""(empty string). here is the entire code: (no modification is to be done with BluetoothChat activity).
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
String tmp_msg ="";
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
String readMessage = new String(buffer, 0,bytes);
if (readMessage.contains(".")){
tmp_msg+=readMessage;
byte[] buffer1 = tmp_msg.getBytes();
int bytes1=buffer1.length;
tmp_msg="";
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ,bytes1,-1,buffer1).sendToTarget();
}else{
tmp_msg+=readMessage;
}
} catch (IOException e) {
// Log.e(TAG, "disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
}
}
}
Upvotes: 1
Reputation: 87
My way: I build a textView in MainActivity with its id being txtReceive.
@Override
public void run() {
byte[] buffer=new byte[1024];
int bytes;
while(true) {
try {
bytes = inputStream.read(buffer);
final String strReceived = new String(buffer, 0, bytes);
runOnUiThread(new Runnable() {
@Override
public void run() {
txtReceive.append(strReceived);
}
});
} catch (Exception e) {
Log.d(TAG, "loi ");
runOnUiThread(new Runnable() {
@Override
public void run() {
txtReceive.setText("Error");
}
});
}
}
}
Upvotes: -1
Reputation: 1
I think there are many good answers. My contribution is to process each digit one at a time as it comes into the Bluetooth input stream as follows. The value of this is to ensure that every set of data is one value in length (as a String), which can be processed very easily by putting each new value (contained in the readMessage String in the main Activity Handler) into a List. That way, you can then use the List(String) (the editor won't let me use <>'s) object to process the data back into real numbers by extracting via
Integer.valueOf(List(String) object passed in here)
// Keep listening to the InputStream while connected while (true) {
try {
byte[] buffer = new byte[1]; // make this one byte to pass one digit at a time through Bluetooth Handler
String readMessage;
int bytes;
if (mmInStream.available()>2) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
readMessage = new String(buffer, 0, bytes);
}catch (IOException e) {
Log.e(TAG, "disconnected", e);
break;
}
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(HomeBlueRemote.MESSAGE_READ, bytes, -1, readMessage)
.sendToTarget();
}
Upvotes: 0
Reputation: 9
I found it!
You have to reset the buffer:
buffer = new byte [1024];
before this:
bytes = mmInStream.read(buffer);
At least it worked for me, and no sleep command is needed.
Upvotes: 0
Reputation: 2389
Try this:
public void run() {
byte[] buffer;
ArrayList<Integer> arr_byte = new ArrayList<Integer>();
while (true) {
try {
int data = mmInStream.read();
if(mmInStream.available()>0) {
arr_byte.add(data);
} else {
arr_byte.add(data);
buffer = new byte[arr_byte.size()];
for(int i = 0 ; i < arr_byte.size() ; i++) {
buffer[i] = arr_byte.get(i).byteValue();
}
Log.e("INPUT",new String(buffer));
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
arr_byte = new ArrayList<Integer>();
}
} catch (IOException e) {
break;
}
}
}
I had this problem too. Now is working good. Without � Characters.
Upvotes: -1
Reputation: 129
I have a this problem and I've solved the problem of this characters � in this way
public void run() {
int bytes; // bytes returned from read()
int availableBytes = 0;
// Keep listening to the InputStream until an exception occurs
while (needRun) {
try {
availableBytes = mmInStream.available();
if(availableBytes > 0){
byte[] buffer = new byte[availableBytes]; // buffer store for the stream
// Read from the InputStream
bytes = mmInStream.read(buffer);
Log.d("mmInStream.read(buffer);", new String(buffer));
if( bytes > 0 ){
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
}
}
} catch (IOException e) {
Log.d("Error reading", e.getMessage());
e.printStackTrace();
break;
}
}
}
Upvotes: 3
Reputation: 31
Radu's fix works fantastic!. I myself have been working on this issue for quite some time now using the Bluetooth Chat sample code. Below is what i'm using to capture and display temperature reading from a remote sensor:
// Keep listening to the InputStream while connected
while (true) {
try {
byte[] buffer = new byte[128];
String readMessage;
int bytes;
if (mmInStream.available()>2) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
readMessage = new String(buffer, 0, bytes);
}catch (IOException e) {
Log.e(TAG, "disconnected", e);
break;
}
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(HomeBlueRemote.MESSAGE_READ, bytes, -1, readMessage)
.sendToTarget();
}
else {
SystemClock.sleep(100);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
As u see, I modified the (buffer.available() > 0) to > 2. This is because the my micro-controller is sending 2 bytes for the temperature. . Prior to this fix, inputstream for the byte count would vary, sometimes only capturing 1 byte which messed up the temperature display reading in the Android app. Again, out all the suggestions on the web, Radu has the best workaround for the android inputstream bug.
Upvotes: 1
Reputation: 2074
Two things I noticed with your code:
First of all sending further down to your app a reference to the buffer in which you read is not always a good solution: What if in the meantime the buffer gets overridden? See this bug on stackoverflow for example You can bypass this by making a copy of the data (for example use buffer.clone()) which you have read from Bluetooth, or if you don't like using too much memory you can make your read buffer a circular one.
You should be able to recompile your data even if it is received in separate packets (but packets are received in a short time span). You can make start/stop flags for instance. Ofc it still depends on the type of object you send over Bluetooth...
And now a possible solution if the 2 previous warnings are of no use is this:
Instead of an infinite loop which calls .read - a blocking call - you can do something like this:
while(true) {
if mmInStream.getAvailable()>0 {
-your read code here-
}
else SystemClock.sleep(100);
}
It's a hack and it might still sometimes read only some part of the message - but it will be very rare!
Pls vote up/correct if useful!
Upvotes: 15