I've been having an issue with data integrity using an RFCOMM socket over Bluetooth in Android. I don't have any issues connecting, but the data I receive is garbled and not the same as the data that is sent. The data is sent by an RS232 device over a Bluetooth adapter, which the phone connects to. There isn't a problem with the adapter as the data is properly received if I connect with a laptop.
My Bluetooth connection is handled based off of the BluetoothChat sample application found at the Android developer site (http://developer.android.com/resources/samples/BluetoothChat/index.html), with no changes. The data being sent is plain text and control characters (which are stripped out before display to the user). The specific problem I have is that some of the text is missing, some of it is repeated, etc.
The funny thing is if I connect to a computer with a terminal app and type in there, the data is transmitted fine. Additionally, if I connect to the device using the GetBlue app the data is received fine.
So I guess the issue is what does GetBlue possibly do different to handle its Bluetooth data transfer, or is there another way receive Bluetooth data over an RFCOMM socket on Android?
The fix for the solution was to create the string in the connected thread, directly after calling read() on the InputStream, and then passing the string back to the main thread for display. For whatever reason, passing the byte array between threads led to significant repetition and data loss.
Modified run() code:
public void run() {
byte[] buffer = new byte[256]; // 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 = mmInStream.read(buffer);
String readMessage = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, readMessage)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
And the handler reception:
case MESSAGE_READ:
// Read in string from message, display to mainText for user
String readMessage = (String) msg.obj;
if (msg.arg1 > 0) {
mainText.append(readMessage);
}
This error is because the object reference is passed to the UI, If you copy the byte array(buffer) to another byte array it works.
Related
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.
Based on SDK's bluetoothchat example I'm working on an app that transmits strings between an android device and arduino.
I've the folowing issues:
1- If I use this code I loose the first byte sent by arduino:
// Keep listening to the InputStream while connected
while (true) {
try {
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes,-1, buffer).sendToTarget();
But This way it works :
bytes = mmInStream.available();
if(bytes != 0) {
SystemClock.sleep(100); //pause and wait for rest of data.
bytes = mmInStream.available(); // how many bytes are ready to be read?
bytes = mmInStream.read(buffer, 0, bytes); // record how many bytes we actually read
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
}
Any explanation please?
2- Arduino sends the string "OK" when recieves a string from the device.
How to use this as an ACK(nowledgment) in my app ?
I tried this but with no success:
String ack = ""; //global variable
sendstring("test string");// send a test string to arduino
SystemClock.sleep(100); //wait for arduino response
if(ack.equals("OK")) txtv.setText(" well received"); //well done
in the handler:
if(msg.what == Bluetooth.MESSAGE_READ){
String receivedstring = new String((byte[]) msg.obj, 0, msg.arg1);
ack = receivedstring ;
I don't get ack = "OK" , and " well received" is not displayed in the text view !!
Many thanks for ur help
Hi i dont know that much about blutoothchat but i may have an anwser to your first question. If you dont already have an answer.
// Keep listening to the InputStream while connected
while (true) {
try {
bytes = mmInStream.read(buffer); // it may not work because its not reading from the first line unlike this: bytes = mmInStream.read(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes,-1, buffer).sendToTarget();
sorry for my bad english. I have to send and receive data from phone to radio using bluetooth pan interface on android. This is the code I've implemented to send, I've made it into the function DoInBackground() of AsyncTask:
try
{
ds = new DatagramSocket();
byte buf[] = "Ciao".getBytes();
DatagramPacket msg = new DatagramPacket(buf, buf.length, InetAddress.getByName(add), p);
ds.send(msg);
}
and it works perfectly. Now I have to wait 100 seconds the answer of the radio. I decide to implement it with this code:
try
{
ds.setSoTimeout(10000);
while(true)
{
try
{
ds.receive(dp);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
catch (IOException e)
{
}
but there are three questions:
1. I need to receive the packet at 192.168.11.2 and 1234 port, but when I declared datagramsocket I set it void, because if i put port and address, send code doesn't work;
2. How can I manage the packet I've received by the radio? I don't need to use address and port bytes;
3. Why have I put the timeout before the loop?
Thank you for the answers.
1) Create the datagram socket with just the port number you are after. DatagramSocket(p);
2) You have a DatagramPacket with a bytebuffer of the size of the response and the length of the response i.e:
int responseLength = 1024;
byte[] responseData = new byte[responseLength];
DatagramPacket response = new DatagramPacket(responseData, responseLength);
After receiving into this datagram packet you need to convert it into same object that it was sent as. Something like this maybe:
String s = new String(response.getData());
3) The setSoTimeout doesn't cause the message to wait for 100s, it is just saying that after 100s if nothing has been read in by the receive() then it will timeout. This only needs to be set once per connection, setting this multiple times during the loop shouldn't be done.
Hope this helps
When I try to run a test composed of an echo server and an android client with the following code, I always get the exception msg "socket is closed". This code can simply send msg to server, and receive msg from server, but if you want to do both at the same time, it just doesn't work... I am very curious why it will lead to this kind of problem, and how should I fix it if I want it to be able to first send msg to echo server
and then receive msg from echo server?
// Server IP address
InetAddress serverIp;
// try to connect Server
try {
// set up server IP address
serverIp = InetAddress.getByName("192.168.17.1");
// set up port
int serverPort=12345;
// initiate socket connection
Socket clientSocket=new Socket(serverIp,serverPort);
BufferedOutputStream out = new BufferedOutputStream(clientSocket.getOutputStream());
out.write("Send From Android1111, stitch ".getBytes());
out.flush();
//wait to receive Server's msg
BufferedReader br =new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
total.toString();*/
// Display received msg with Toast
Toast.makeText(getApplicationContext(), br.readLine(), Toast.LENGTH_SHORT ).show();
//close connection
clientSocket.close();
// out.close();
// out = null;
} catch (IOException e) {
// display exception with Toast
Toast.makeText(getApplicationContext(),e.toString(), Toast.LENGTH_SHORT).show();
}
unfortunately, it still doesn't work ...I followed your instruction and modify the code to:
// set up Server IP address
serverIp = InetAddress.getByName("192.168.2.2");
// set up Server port
int serverPort=12345;
// initiate socket connection
Socket clientSocket=new Socket(serverIp,serverPort);
// open input and output stream
OutputStream out = clientSocket.getOutputStream();
InputStream in = clientSocket.getInputStream();
//send msg
out.write("Send From Android1111, bitch ".getBytes());
// receive msg from server
byte[] buffer = new byte[in.available()];
in.read(buffer);
String rMsg = new String(buffer);
Toast.makeText(getApplicationContext(), rMsg, Toast.LENGTH_LONG ).show();
//close input and output stream
in.close();
out.close();
//關閉連線
clientSocket.close();
} catch (IOException e) {
// 出錯後顯示錯誤訊息Toast
Toast.makeText(getApplicationContext(),e.toString(), Toast.LENGTH_SHORT).show();
}
for helper's convenience, here's the python written code for server part:
# Practice Echo Server Program written in Python
import socket
# host = '' means it binds to any available interface
host = ''
port = 12345
# socket() function returns a socket object whose methods implement the various socket system calls.
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# Bind the socket to address.
s.bind((host,port))
# Listen for connections made to the socket. The backlog argument specifies
# the maximum number of queued connections and should be at least 0;
# the maximum value is system-dependent (usually 5), the minimum value is forced to 0.
s.listen(5)
# Accept a connection. The socket must be bound to an address and listening for
# connections. The return value is a pair (conn, address) where conn is a new socket
# object usable to send and receive data on the connection, and address is the address
# bound to the socket on the other end of the connection.
conn, addr = s.accept()
print 'Connected by', addr
# Receive data from the socket. The return value is a string representing the data received.
# The maximum amount of data to be received at once is specified by bufsize. See the Unix
# manual page recv(2) for the meaning of the optional argument flags; it defaults to zero.
# Note For best match with hardware and network realities, the value of bufsize should be
# a relatively small power of 2, for example, 4096.
while 1:
data = conn.recv(1024)
if not data: break
print 'received data is : ', repr(data)
conn.send(data)
conn.close()
I assume you are doing the right things in the wrong order. May be the server is too fast and when you are trying to read the response it is already received and gone.
Following the rules presented in the tutorial Reading from and Writing to a Socket
Open a socket
Open an input stream and output stream to the socket.
Read from and write to the stream according to the server's protocol.
Close the streams.
Close the socket.
Do you see the difference? First open Input and Output stream and then start sending your request.
I am sure that if you stick to this order it will work.
I had a similar issue, solved it by editing the gradle.properties file to allow for proxy connections. I added the foregoing lines as explained on https://github.com/facebook/react-native/issues/2726 by Nadeem Shaik
systemProp.http.proxyHost=proxyHost
systemProp.http.proxyPort=proxyPort
systemProp.https.proxyHost=proxyHost
systemProp.https.proxyPort=proxyPort
Your application needs INTERNET permission in your AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
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.