Implementing BlockingQueue Buffer used in Bluetooth Communication for Android - android

I'm really stumped with this and I've trying to debug for the passed three days. Hopefully someone will be able to tell me what I am doing wrong.
I am implementing a BlockingQueue (FIFO) buffer to receive information being streamed from my PC over bluetooth. I am sending prerecorded electrocardiogram signal over a Hyperterminal link using RealTerm.
I have tested the buffer as I start up the app by adding values and then removing them, and it seems to work as it should.
The problem comes in when I try to store in the buffer while I'm receiving data from the bluetooth connection. I don't know if I am adding faster than the BlockingQueue can cope, but when I stop the data transmission and check my buffer, the whole buffer contains the last value that was added. The size of the buffer is correct, but the content isn't.
Here is my buffer:
public class IncomingBuffer {
private static final String TAG = "IncomingBuffer";
private BlockingQueue<byte[]> inBuffer;
public IncomingBuffer() {
inBuffer = new LinkedBlockingQueue<byte[]>();
Log.i(TAG, "Initialized");
}
public int getSize() {
int size;
size = inBuffer.size();
return size;
}
// Inserts the specified element into this queue, if possible. Returns True
// if successful.
public boolean insert(byte[] element) {
Log.i(TAG, "Inserting " + element[0]);
boolean success = inBuffer.offer(element);
return success;
}
// Retrieves and removes the head of this queue, or null if this queue is
// empty.
public byte[] retrieve() {
Log.i(TAG, "Retrieving");
return inBuffer.remove();
}
// Retrieves, but does not remove, the head of this queue, returning null if
// this queue is empty.
public byte[] peek() {
Log.i(TAG, "Peeking");
return inBuffer.peek();
}
}
The portion of my BluetoothCommunication class which receives the information and sends it to the buffer is the following:
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
ringBuffer = new IncomingBuffer();
byte[] buffer = new byte[1024];
Log.i(TAG, "Declared buffer byte");
int bytes;
byte[] retrieve;
int size;
Log.i(TAG, "Declared int bytes");
//Setting up desired data format 8
write(helloworld);
Log.i(TAG, "Call write(initialize)");
// Keep listening to the InputStream while connected
while (true) {
try {
Log.i(TAG, "Trying to get message");
// Read from the InputStream
bytes = mmInStream.read(buffer);
//THIS IS WHERE THE BYTE ARRAY IS ADDED TO THE IncomingBuffer
RingBuffer.insert(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
Log.i(TAG, "Sent to target" +ringBuffer.getSize());
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothCommService.this.start();
break;
}
}
}
So an example of my problem would be:
Send values over bluetooth connection (8 bit values from 1 to 20). In the insert method of the IncomingBuffer class, the log message confirms the proper value is sent. When values are retrieved from buffer, it contains twenty byte arrays which all contain the last number inserted (20).
Any clue as to why the buffer would work in other circumstances but not during the bluetooth communication?

I figured out what my problem was.
When I was using the variable buffer to read from mmInStream and then pass that to the ringBuffer, I pass the same byte array variable every time i go through the while loop. From what I can understand that simply assigns a specific memory location where the byte array is calculated and that is why at the end all of the elements in my ringBuffer are the last value that was assigned to 'buffer' from the mmInStream.
What i did to change that is make a separate variable that i clone the 'buffer' byte array to. Before I pass 'buffer' to 'RingBuffer', i do the following:
byte[] newBuf;
newBuf = buffer.clone();
ringBuffer.store(newBuf);
This takes care of my problem.

Related

Not receiving full packet when sending data from STM32 to Android via Bluetooth

I am sending accelerometer data from STM32 microcontroller to a HC-06 Bluetooth module, and I have developed an Android app to receive the data.
On STM32, I make a 3 byte packet (uartBuffer) which has the following structure: [0] counter, [1] xdata, [2] ydata.
I then send this data via UART to the HC-06 module.
HAL_UART_Transmit(&uartHand, uartBuffer, 3, 10); // {uartHandle, data, dataSize, timeout}
On Android, I have no problem connecting with the device, etc. However, when I receive the packet, it doesn't arrive together. It does always arrive eventually (in the correct order as well), but I might get a packet with only the 1st byte, then a packet with the last 2 bytes, and sometimes other combinations, as can be seen in the logcat picture below. The value which always increments by one (i.e. 20, 21, 22, etc..) is the 1st byte of the packet as sent by STM32.
I have tested this with different sampling rates between up to 100Hz, and with bigger packets (up to 5 bytes).. I never lose any bytes, but the packet arrangement is further divided at higher sampling rates and data sizes.
Here is the ConnectedThread class which does the reading of data in the run method.
`
private class ConnectedThread extends Thread {
private final BluetoothSocket mmBTSocket;
private final InputStream mInStream;
private final OutputStream mOutStream;
public ConnectedThread(BluetoothSocket mSocket) {
Log.d(TAG, "ConnectedThread: Starting");
mmBTSocket = mSocket;
InputStream mTempIn = null;
OutputStream mTempOut = null;
try {
mTempIn = mmBTSocket.getInputStream();
mTempOut = mmBTSocket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "Failed to get I/O Stream", e);
}
mInStream = mTempIn;
mOutStream = mTempOut;
}
public void run(){
byte[] mBuffer = new byte[3]
int mBytes = 0; // bytes returned from read
String mIncomingMessage = new String();
mIncomingStringBuilder = new StringBuilder();
while (true) {
try {
mIncomingStringBuilder.setLength(0);
mBytes = mInStream.read(mBuffer, 0, 3);
for (int i=0; i<mBytes; i++) {
mIncomingMessage = String.valueOf(mBuffer[i]);
mIncomingStringBuilder.append(mIncomingMessage + ",");
}
Log.d(TAG, "run: ConnectedThread - InputStream: " + mIncomingStringBuilder);
Intent incomingMessageIntent = new Intent("incomingMessage");
incomingMessageIntent.putExtra("theMessage", mIncomingStringBuilder.toString());
LocalBroadcastManager.getInstance(mContext).sendBroadcast(incomingMessageIntent);
} catch (IOException e) {
Log.e(TAG, "error reading from inputStream, e);
break;
}
}
}
`
So my questions are:
1) Why am I not receiving the full packets in the same structure as they are sent from the STM32? A commentor here mentions here that:
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.
Is this true? Can Bluetooth really not preserve the full packet structure? Can anyone verify this?
2) If this is true, then what is the best method to construct a package which is easy for Android to detect the start of and end? I assume I need a sync/start byte. How do I ensure this sync byte doesn't occur in my accelerometer data? Should I use 2 consecutive sync bytes? Is a checksum at the end of the packet enough to detect if the packet has errors?
3) Finally, is it good practice to only use connectedThread on Android to receive the byte, convert to string and send to main activity so that its free to receive the next as quickly as possible? Then in main activity, I would reconstruct the packet? Or would this be too much work for main activity too?
Thanks in advance for your help.

1024 buffer in Bluetooth chat chunks my information ;-(

I use some of the Bluetooth chat samplecode for sending a SMALL (177 byte to 3617 byte) "settings-file" "securly" between apps.
when it is under 1024 bit everything works fine: (so the 177 works PERFECT)
sendingdevice press "send button" and the reciver gets it (with a dialog if they want it..) (and I save the "string" to a "settings"file on that device)
but if the file is over 1024 it gets chunkt/cut off.. (example: 2000byte)
so the file gets corrupted (data-loss but some info remains..)
Probably I need to "split" my file in 1024 bits and send the bits and in the receiver-end, I need to "add them all up"..
but I don't know the "standard best practices" for this, do you have any suggestions?
I have tryed to "only higher" the 1024 byte to 65536byte, but that don't work..
(or maby I do this wrong..)
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// 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();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
}
}
}
sedan write:
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
mmOutStream.flush();
// Share the sent message back to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
and when i "click on send settings":
String message = view.getText().toString();
String settingInAString = getSettingInALargeString();
sendMessage(settingInAString);
and in "sendMessage":
if (message.length() > 0) {
// Get the message bytes and tell the BluetoothChatService to write
byte[] send = message.getBytes();
mChatService.write(send); //SO convert to byte and then send the byte..
// Reset out string buffer to zero and clear the edit text field
mOutStringBuffer.setLength(0);
mOutEditText.setText(mOutStringBuffer);
}
and:
/**
* Write to the ConnectedThread in an unsynchronized manner
* #param out The bytes to write
* #see ConnectedThread#write(byte[])
*/
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}
but I think you know what Im lookning for ...
(or can I some how change the "BluetoothChat" so it can sent and recive a large Sring, and not "byte:s"? :-) )
Best REGARDS to you all :-)
EDIT:
on the reader side I have this:
on the "reader end" I have:
....
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
//only a byte redebuffer, hmm can I change this? or do i use a whileloop?
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
recivedStringCheckFirst(readMessage);
//simple-check if the data is a "data-setting-file"
String [] allSettingsInALargeArray1 = doSplitOnLargeString(readMessage);
int titleArrayLength1 = getLengthOffTheUpCommingTitleArrayFromNew(allSettingsInALargeArray1); //this do a split and looks if it is 1,2,3..20 settings..)
mConversationArrayAdapter.add(titleArrayLength1 + " datasettings recived from " + mConnectedDeviceName + " SAVE THIS?");
//this type this text to the "chatwindow"
break;
Here is the splitting-chunk-problem now..
if i send under ~ 1024 I receive the correct amount of settings ant i can save this fine :-)
If i sent larger then 1024 I get first for exampel "6 settings from.." and then a new message that I recived "1 settings from.." message :-(
just for your info:
protected void recivedStringCheckFirst(String readMessage) {
String eventuellSettings = readMessage;
if (isThisASettingFile(eventuellSettings)){
//of ok..
System.out.println("incommingISAsetting :-) ");
inkommenSettings = eventuellSettings;
showDialog(); //dialog for save settings?
}
if (!isThisASettingFile(eventuellSettings)){
//not a settingsfile!
Toast.makeText(this, "try again..", Toast.LENGTH_LONG).show();
}
}
so i think it is:
case MESSAGE_READ:
is not only called if a complete file is received,
it is also called if a small chunks is received.
So I probably should place the "readFile-chunk" in a separate buffer
(i.e. mNewBufForFile += readFileChunk)
And then check the mNewBufForFile has a complete packet in it (how?). If it is done: I "save" the file message and then clear all buffer.
but how can i "split this from "Message_read", and do I "add a stopping bit" so i can check when i recive all the data? or can i do this better?
You can send as many bytes as you want. They come in in chunks smaller than the size of buffer (1024). Indeed the original code will mix all up caused by using one buffer. Change
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
to
while (true) {
try {
byte[] buffer = new byte[1024];
// Read from the InputStream
int nbytes = mmInStream.read(buffer);
Log.i(TAG, "read nbytes: " + nbytes);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, nbytes, -1, buffer)
.sendToTarget();
The data still comes in in chuncks but now you get all displayed in the rigth sequence.
As the chunck sizes -during some tests- are smaller than 1024 it makes no sense to have a bigger buffer. If you want to transfer a real file you should concatenate all together. This is a normal action using sockets.

How to read and write data through Java Nio client in Android

I am doing Client server communication in java successfully but now i need to write client in Android rather the java.
client: public class ExampleClient2 {
public static void main(String[] args) throws IOException,
InterruptedException {
int port = 1114;
SocketChannel channel = SocketChannel.open();
// we open this channel in non blocking mode
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("192.168.1.88", port));
if(!channel.isConnected())
{
while (!channel.finishConnect()) {
System.out.println("still connecting");
}
}
System.out.println("connected...");
while (true) {
// see if any message has been received
ByteBuffer bufferA = ByteBuffer.allocate(60);
int count = 0;
String message = "";
while ((count = channel.read(bufferA)) > 0) {
// flip the buffer to start reading
bufferA.flip();
message += Charset.defaultCharset().decode(bufferA);
}
if (message.length() > 0) {
System.out.println("message " + message);
if(message.contains("stop"))
{
System.out.println("Has stop messages");
// break;
}
else
{
// write some data into the channel
CharBuffer buffer = CharBuffer.wrap("Hello Server stop from client2 from 88");
while (buffer.hasRemaining()) {
channel.write(Charset.defaultCharset().encode(buffer));
}
}
message = "";
}
}
}
}
this code is running successfully in java but in android it consuming lots of memory and not running reliably, due to its while (true) loop its like polling , plz let me know some solution that without polling i can read and write the data.
Thanks.
You need to compact() the buffer after calling decode() (or get(), or write(), anything that takes data out of the buffer).
Youu shouldn't allocate a new buffer every time around that while loop, and you should break out of it if read() returned -1. I don't actually see a need for the while loop at all.

Android Bluetooth InputStream real time read

I am working on an Android application that receives a real time data by Bluetooth and plots it on the screen.
The data is a gyro sensor position information. I am sending it from a custom Freescale Kinetis K10 microcontroller board (designed and tested by myself). For the Bluetooth communication I am using HC-05 Bluetooth module.
The format of the data is as follows:
byte_1: position identification byte, always equals to -128
byte_2: position of axis 1
byte_3: position of axis 2
byte_4: position of axis 3
I am sending these 4 bytes continuously one after another, in that particular order. I am sending this packet of 4 bytes every 5 ms and sending the packet takes about 4.7 ms (9600 baud rate).
The data output from the microcontroller is perfect in terms of accuracy and timing (checked with a logic analyzer).
The problem is that when it is being received from the phone, some of the bytes seem to get lost. Here is the part of the code, where I am reading the InputStream:
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e("Printer Service", "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
#Override
public void run() {
Log.i("BluetoothService", "BEGIN mConnectedThread");
byte[] buffer = new byte[4];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
int position = 0;
if(buffer[0] == -128) {
if(bytes >= 2) {
sendArray.errorTilt = buffer[1];
}
if(bytes >= 3) {
sendArray.errorRoll = buffer[2];
}
if(bytes == 4) {
sendArray.errorPan = buffer[3];
}
}
else if(buffer[1] == -128) {
position = 1;
if(bytes >= 3) {
sendArray.errorTilt = buffer[2];
}
if(bytes == 4) {
sendArray.errorRoll = buffer[3];
}
if(bytes >= 2) {
sendArray.errorPan = buffer[0];
}
}
else if(buffer[2] == -128 && bytes >= 3) {
position = 2;
sendArray.errorRoll = buffer[0];
sendArray.errorPan = buffer[1];
if(bytes == 4) {
sendArray.errorTilt = buffer[3];
}
}
else if(buffer[3] == -128 && bytes == 4) {
position = 3;
sendArray.errorTilt = buffer[0];
sendArray.errorRoll = buffer[1];
sendArray.errorPan = buffer[2];
}
if(position <= bytes && bytes > 1) {
sendArray.errorUpdate = true;
}
} catch (Exception e) {
e.printStackTrace();
connectionLost();
BluetoothService.this.stop();
break;
}
}
}
public void write(int oneByte) {
try {
mmOutStream.write(oneByte);
} catch (IOException e) {
Log.e("BluetoothService", "Exception during write", e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e("BluetoothService", "close() of connect socket failed", e);
}
}
}
sendArray is a singleton that keeps many different variables.
errorTilt, errorRoll and errorPan are the current values of the axis, which are being updated from the receiving buffer.
"position" keeps the position of the position identification byte. It is used for a check if any variables have been updated.
Many times just one byte is received in the input buffer and since I don't know which axis is that supposed to be, since I don't have information about it's relative position to the position byte, this particular byte is useless and gets lost.
I've tested the accuracy of receiving by the following method. I made the MCU output a triangular wave on one of the axis, instead of the axis data. On the phone the lines of the triangular wave are not straight as they are supposed to be, but randomly curved and containing artifacts.
To plot the data I am using GraphView and I am updating the graph on equal intervals from a separate thread.
I've tried using longer receiving buffer (with a modified receiving algorithm), but this doesn't help as only a few bytes are being received at a time.
I've tried implementing InputStream.available() but it was always giving 127 bytes available, which didn't seem to be true.
I've read many threads about similar problems and I spent the last 5 days working on it, but I couldn't find a good solution.
To summarize, I need to achieve accurate, real time (or close to real time) receiving of all the bytes.
Thread with a similar problem:
How to do good real-time data streaming using Java Android SDK
Thank you.
UPDATE:
I've tried sending the information just for one of the axis, so it is simple and clear, without the need of position bytes. I was sending it again every 5 ms, but this time it was more time between the consecutive bytes, since it's just one byte in the packet.
I used InputStream.read() this time, which doesn't require a buffer. However, the incoming data was corrupted again, because random bytes couldn't be received.
I've seen different project using this method successfully, I don't know why it's not working with me. I thought it might be a problem with the HC-05 Bluetooth module I'm using, but I tried a different one - HC-06, and the situation is the same. I haven't tried a different phone, but my phone (Samsung Galaxy S3, Android 4.1.2) seems to be working OK.
UPDATE2: I've tried again testing the original code with InputStream.available() before reading from the stream.
When the condition is available()>0, there are no major changes, maybe it works slightly worse.
When the condition is available()>1, it never reads. I guess that is because of the unreliable available() method, as it says in the documentation.
you have incorrect processing of data, if you want to get data from microcontroller board. You have to use bytesAvaliable because android bluetooth Socket is pretty slow over microcontroller boards with bluetooth. But "bytesAvaliable way" has nuance - As socket is slow receiver, bytesAvaliable can catch more then 1 packet from board in one time so you gotta devide readed data by yourself, Check my code out below! My code is getting 38 bytes packets from inertial sensor so you gotta only change count of bytes! 0xAA is the first byte of every next packet so if you find 0xAA byte and have 38 bytes you get packet and nullify iterator. But anyway I'm sure that you still can sometimes lose data because it's not high frequency data transfering way
public void run() {
byte[] bytes = new byte[38];
int iterator = 0;
while (true) {
try {
int bytesAvailable = mmInStream.available();
if (bytesAvailable > 0) {
byte[] curBuf = new byte[bytesAvailable];
mmInStream.read(curBuf);
for (byte b : curBuf) {
if (b == (byte) 0xAA && iterator == 38) {
mHandler.obtainMessage(MainActivity.DATA_READ, bytes.length, -1, bytes).sendToTarget();
iterator = 0;
bytes[iterator] = b;
} else {
bytes[iterator] = b;
}
iterator++;
}
}
} catch (IOException ex) {
Log.e(TAG, "disconnected", ex);
connectionLost();
break;
}
}
}

How to clear the bluetooth InputStream buffer

In the bluetoothChat example app, the sent and received data is added into a ArrayAdapter called mConversationArrayAdapter. There, each character is added into the array.
In my case, I have a String instead of an array because I don't need to send and receive several data, I only need to send one string, and receive one string each time.
The problem that I'm getting is that if I first receive a string like hello world, and then I receive a shorter one, the first is overwrited by the second, instead of deleting the first and writing the new.
So, if i first receive hello world, and then I supposse that I have to receive bye, what I really receive is byelo world.
So, how can I clear the buffer each time a receive what I want?
Code Snipets
Send data:
byte[] send1 = message_full1.getBytes();
GlobalVar.mTransmission.write(send1);
Write call:
public void write(byte[] out) {
/**Create temporary object*/
ConnectedThread r;
/**Synchronize a copy of the ConnectedThread*/
synchronized (this) {
if (GlobalVar.mState != GlobalVar.STATE_CONNECTED) return;
r = GlobalVar.mConnectedThread;
}
/**Perform the write unsynchronized*/
r.write(out);
}
Write Thread:
public void write(byte[] buffer) {
try {
GlobalVar.mmOutStream.write(buffer);
/**Share the sent message back to the UI Activity*/
GlobalVar.mHandler.obtainMessage(GlobalVar.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
} catch (IOException e) {}
}
Finally, read Thread:
public void run() {
byte[] buffer = new byte[12]; // buffer store for the stream
int bytes; // bytes returned from read()
/**Keep listening to the InputStream until an exception occurs*/
while (true) {
try {
/**Read from the InputStream*/
bytes = GlobalVar.mmInStream.read(buffer);
/**Send the obtained bytes to the UI activity*/
GlobalVar.mHandler.obtainMessage(GlobalVar.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
GlobalVar.mTransmission.connectionLost();
/**Start the service over to restart listening mode*/
//GlobalVar.mTransmission.start();
break;
}
}
}
try this
bytes = inputStream.read(buffer);
buffer[bytes] = '\0';

Categories

Resources