I'm working on an application which should be quite the same as Bluehood, an application which is on the google market .
So now I'm working on Bluetooth . The fact is, I want to transfer strings (JSON) between two devices . I've seen lots of posts on stackoverflow and some examples on the internet but it's not so clear for me .
I know that I've to use createInsecureRfcommSocketToServiceRecord for sending informations and listenUsingInsecureRfcommWithServiceRecord for receiving them , but I'm searching some simple tutorial to explain how it works and how to transfer data between two devices .
Thank in advance for your explanations...
It's hard to know if I am answering this effectively, as you say you have searched the web and I find one of the most useful tutorials at android com on Bluetooth. I have supplied parts of the code, not the full thread classes, but the bones to give you an idea of how temp sockets are used until sockets are found and made final, for the duration of the connection, and how threads manage each stage of the connection process.
listenUsingRfcommWithServiceRecord(NAME, MY_UUID); is used to create a server socket. It listens for a connection. It acts like a server. This is on the device that is acting as a server or listening for incoming connections.
This is done is a separate thread.
public AcceptThread() {
BluetoothServerSocket tmp = null;
// Create a new listening server socket
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) {
}
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Listen to the server socket if we're not connected
while (mState != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
synchronized (BluetoothConnection.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
// Either not ready or already connected. Terminate new socket.
try {
socket.close();
} catch (IOException e) {
}
break;
}
}
}
}
}
There is a separate thread to act as a client, seeking a connection. It goes looking for a connection. This is on the device that seeks the connection with the server device. (These can be interchangeable).
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
}
mmSocket = tmp;
}
public void run() {
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
}
connectionFailed();
return;
}
You then need a thread to manage the actual connection. When the client meets the server. Also in a separate thread.
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
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(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
connectionLost();
// Start the service over to restart listening mode
BluetoothConnection.this.start();
break;
}
}
}
Within this thread you also have your code to manage writing data through this connection.
There are samples supplied through android.com.
I also found this tutorial good, as a simple background into bluetooth discovery and connection, although it doesn't give you all you need to read and write data.
In terms of reading and writing the data, the following snippet is an example of a way to handle reading data and parsing it to something usable. Calling the handler from within the connection thread. In this case I am appending the data to a textView, but you can do whatever you want with it, it shows how to put it into a String. (which is what you are looking for).
private final Handler mHandler = 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);
textView1.append("\nMessage " + messageCount + ": " + readMessage);
....
Likewise there is some code to write messages - this is in the connected thread class. However, I grab this information using an OnClick event with the button to send. Grab the text from the EditText and send it to a function to parse the String to bytes.
where message is a String and mChatService is calling the write method from the Connected thread.
Converting the string to a byte array, so it can be sent.
// Get the message bytes and tell the BTManager to write
byte[] send = message.getBytes();
mChatService.write(send);
Write method from connected thread:
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
} catch (IOException e) {
}
}
It is worth noting that the states of the devices must be monitored (you can have a look a the tutorial for that).
It is also important to keep the background threads away from the UI. So that is where the skill comes in (and a handler) to transfer data to and from the UI to the socket connection.
Related
I am trying to write an app that passes the coordinates of a ball to Arduino via BT. The coordinates are being sent every 4 ms. For this test I send "123" instead of full coordinates. What am I getting now (on Arduino serial monitor) is "123123123123123..." and it refreshes only after I close the application.
What I want to achieve is "123" in every line, that shows immediately after the message is sent.
Android code BT:
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private OutputStream outStream ;
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket
// because mmSocket is final.
BluetoothSocket tmp = null;
mmDevice = device;
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it otherwise slows down the connection.
mBluetoothAdapter.cancelDiscovery();
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mmSocket.connect();
Log.i(TAG, "run: CONNECTED");
} catch (IOException connectException) {
Log.i(TAG, "run: NOT CONNECTED");
}
}
// Closes the client socket and causes the thread to finish.
public void cancel() {
try {
mmSocket.close();
if(outStream != null)
outStream.close();
finish();
} catch (IOException e) {
Log.e(TAG, "Could not close the client socket", e);
}
}
//Sending Message
public void writeData(String data){
String info = data;
try {
outStream = mmSocket.getOutputStream();
outStream.write(info.getBytes());
Log.i(TAG, "writeData: MSG SENT");
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "run: CANT SEND MSG");
}
}
public boolean isConnected(){
return mmSocket.isConnected();
}
}
In my main function I call:
if(connectThread.isConnected())
connectThread.writeData("123");
Arduino code:
String incomingByte;
void setup() {
//pinMode(53, OUTPUT);
Serial.begin(9600);
}
void loop() {
// see if there's incoming serial data:
if (Serial.available() > 0) {
// read the oldest byte in the serial buffer:
incomingByte = Serial.readString();
Serial.println(incomingByte);
delay(10);
}
}
There is no concept of messages in serial communication, unless you make it yourself.
Serial.readString() delimits your "messages" with time (1 second by default) and you are sending "messages" 4 ms apart. This obviously concatenates your "messages".
To actually send messages you need to delimit them. You can do that by sending lines.
On Android, you need to end the message with a new line character:
outStream.write(info.getBytes());
outStream.write(10); // send a new line character (ASCII code 10)
And on Arduino, you need to read, until you find a new line character:
incomingByte = Serial.readStringUntil('\n');
Serial.read(); // remove the leftover new line character from the buffer
You need to put at least \n (or maybe \r\n) after the coordinates, or the Bluetooth module just keeps buffering.
I need to implement a TCP comunication between an IoT device(custom) and an Android App.
For the Wifi device we have a Server Socket, while in Android i have an AsyncTask as a Client Socket. Both the device and the smarthone are connected to the same network.
Here is the Android Client Socket code for the initialization/socket-read and socket-write:
Variables:
static public Socket nsocket; //Network Socket
static public DataInputStream nis; //Network Input Stream
static private OutputStream nos; //Network Output Stream
AsyncTask method doInBackgroud:
#Override
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
//Init/Create Socket
SocketInit(IP, PORT);
// Socket Manager
SocketUpdate();
} catch (IOException e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: IOException");
clearCmdInStack();
MainActivity.SocketDisconnectAndNetworkTaskRestart();
result = true;
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
result = true;
} finally {
try {
SocketDisconnect();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Log.i("AsyncTask", "doInBackground: Finished");
}
return result;
}
Socket Initializzation:
public void SocketInit(String ip, int port) throws IOException {
InetAddress addr = InetAddress.getByName(ip);
SocketAddress sockaddr = new InetSocketAddress(addr, port);
nsocket = new Socket();
nsocket.setReuseAddress(false);
nsocket.setTcpNoDelay(true);
nsocket.setKeepAlive(true);
nsocket.setSoTimeout(0);
nsocket.connect(sockaddr, 0);
StartInputStream();
StartOutputStream();
}
Read from Socket:
private void SocketUpdate() throws IOException, ClassNotFoundException {
int read = 0;
// If connected Start read
if (socketSingleton.isSocketConnected()) {
// Print "Connected!" to UI
setPublishType(Publish.CONNECTED);
publishProgress();
if(mConnectingProgressDialog != null)
mConnectingProgressDialog.dismiss(); //End Connecting Progress Dialog Bar
//Set Communications Up
setCommunicationsUp(true);
Log.i("AsyncTask", "doInBackground: Socket created, streams assigned");
Log.i("AsyncTask", "doInBackground: Waiting for inital data...");
byte[] buffer = new byte[3];
do{
nis.readFully(buffer, 0, 3);
setPublishType(Publish.READ);
publishProgress(buffer);
}while(!isCancelled());
SocketDisconnect();
}
}
Streams init:
public void StartInputStream() throws IOException{
nis = new DataInputStream(nsocket.getInputStream());
}
public void StartOutputStream() throws IOException{
nos = nsocket.getOutputStream();
}
Read and Write methods:
public int Read(byte[] b, int off, int len) throws IOException{
return nis.read(b, off, len); //This is blocking
}
public void Write(byte b[]) throws IOException {
nos.write(b);
nos.flush();
}
public boolean sendDataToNetwork(final String cmd)
{
if (isSocketConnected())
{
Log.i("AsyncTask", "SendDataToNetwork: Writing message to socket");
new Thread(new Runnable()
{
public void run()
{
try
{
Write(cmd.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
Log.i("AsyncTask", "SendDataToNetwork: Message send failed. Caught an exception");
}
}
}).start();
return true;
}
Log.i("AsyncTask", "SendDataToNetwork: Cannot send message. Socket is closed");
return false;
}
The application is very simple, the android app sends a command(via sendDataToNetwork method) to the IoT device and the latter sends back an "ACK" Command string.
The problem
The problem is that while the IoT device always receives the command, the smartphone rarely gets the ACK back. Sometimes i get something like "ACKACKACKACK". By debugging the IoT device i'm sure that it successfully sends back the ACK, so the problem lies in the InputStream read() method which doesn't retrieve the string right away.
Is there a way to empty the InputStream buffer right away, so that i get an "ACK" string back from the IoT device every time i send a command?
Update
I've updated the socket config so that there are no more buffer limitations and i've replaced read() method with readFully. It greatly improved, but still make some mistakes. For istance one out of 2-3 times no ack is received and i get 2 ack the next turn. Is this perhaps the computational limit of the IoT device? Or is there still margin for a better approach?
the problem lies in the InputStream read() method which doesn't empty the buffer right away.
I don't know what 'empty the buffer' means here, but InputStream.read() is specified to return as soon as even one byte has been transferred.
Is there a way to empty the InputStream buffer right away, so that i get an "ACK" string back from the IoT device every time i send a command?
The actual problem is that you could be reading more than one ACK at a time. And there are others.
If you're trying to read exactly three bytes, you should be using DataInputStream.readFully() with a byte array of three bytes.
This will also get rid of the need for the following array copy.
You should not mess with the socket buffer sizes except to increase them. 20 and 700 are both ridiculously small values, and will not be the actual values used, as the platform can adjust the value supplied. Your claim that this improved things isn't credible.
You should not spin-loop while available() is zero. This is literally a waste of time. Your comment says you are blocked in the following read call. You aren't, although you should be. You are spinning here. Remove this.
I am trying to control/operate a motor from an android phone in "as close as possible" realtime using the Android SPP Bluetooth socket interface. The motor ought to run in a so called 'dead man' operation mode. So the motor will only turn if a button on the android APP is touched and ought to stop immediately if the touch is released.
I implemented this by continuously sending 'keep turning' telegrams of 20 Bytes about every 20ms to keep the motor turning and to have the motor stop immediately as soon as no more telegrams are received or if a STOP telegram is received.
This seem to work acceptable well on some phone but others continue sending 'keep turning' telegrams even after the MotionEvent.ACTION_UP event has been processed and no more data are being send.
I assume that this is caused by some internal buffers that cache the transmit data and continue sending until the buffer is empty.
Simple questions:
Is there a way to purge the BT stream transmit buffer to stop all data transfer immediately?
Or can I get the fill level of the transmit buffer in which case I would not put anything more than about 2 telegrams into it?
Or is there a way to specify the buffer size when opening the stream?
Searching the net, I was not able to find anything that talks about BT stream buffer size of buffer management.
And Yes, I have implemented read and write functions as threads and I do not have any problems in reading all telegrams, and I do not need to deliver telegrams in real time but I should be able to stop sending 'keep turning' telegrams within about 50 to 100ms.
Any hints are very welcome.
I am sorry that I did not add the code, I thought it may not be necessary as it is straight forward as:
#Override
public boolean onTouch(final View v,MotionEvent event) {
int eventAction = event.getAction();
switch (eventAction) {
case MotionEvent.ACTION_DOWN:
if (v == btnUp || v == btnDown) {
// Start a thread that sends the goUP or DOWN command every 10 ms until
// btnUp released
tvCounter.setText("----");
action_touched = true;
new Thread(new Runnable() {
#Override
public void run() {
int counter = 1;
// Disable heart beat
ServiceRequest.send(EnRequest.REQ_SET_HEARTBEAT,0);
// Send GoUp command plus a wrapping counter byte every nn ms
// until the button is released
while (action_touched) {
try {
setDeadmanMove(v==btnUp,counter);
Thread.sleep(20);
++counter;
}
catch (InterruptedException ex) {
action_touched = false;
}
catch (Exception ex) {
action_touched = false;
}
}
// Send a STOP command
setDeadmanStop();
// Enable heart beat again
ServiceRequest.send(EnRequest.REQ_SET_HEARTBEAT,1);
// We are done
}
}).start();
}
break;
case MotionEvent.ACTION_UP:
// Stop Thread
action_touched = false;
break;
}
return true;
}
The snipped below is part of the communication class that manages the Bluetooth serial communication.
public void btWrite(DeviceRecord message) {
if (runBTreceiver) {
if (message.isValidRecord()) {
try {
lock.lock();
++lockCounter;
mmBufferedOut.write(message.getFullRecord());
mmBufferedOut.flush();
}
catch (IOException e) {
if (GlobalData.isDebugger) Log.i(TAG, "Failed sending " + message + " " + e.getMessage());
ServiceResponse.send(EnEvent.EVT_BT_RECEIVER_ERROR, "Error data send: " + e.getMessage());
resetConnection();
runBTreceiver=false;
}
finally {
--lockCounter;
lock.unlock();
}
}
}
}
The code snipped that allocates and opens the Bluetooth connection
try {
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = myBluetoothAdapter.getRemoteDevice(myBluetoothMacId);
if (device != null)
{
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
myBluetoothSocket = device.createRfcommSocketToServiceRecord(GlobalData.MY_UUID);
}
catch (IOException e) {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,
String.format(GlobalData.rString(R.string.srv_failcrt),BTERROR_CREATE,e.getMessage()));
}
// Establish the connection. This will block until it connects or
// timeout?
try {
if (! myBluetoothSocket.isConnected()) {
myBluetoothSocket.connect();
}
}
catch (IOException e) {
try {
Log.e("","trying fallback...");
myBluetoothSocket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
myBluetoothSocket.connect();
}
catch (IOException e2) {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,e2.getMessage());
}
}
}
else {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,
String.format(GlobalData.rString(R.string.srv_failcrt),BTERROR_DEVICE,"getRemoteDevice failed"));
}
}
catch (Exception e) {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL, e.getMessage());
return;
}
InputStream tmpIn = null;
OutputStream tmpOut = null;
mmSocket = socket;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
}
catch (IOException e) {
ServiceResponse.send(EnEvent.EVT_ERROR, GlobalData.rString(R.string.srv_failcst) + e.getMessage());
resetConnection();
runBTreceiver=false;
}
mmInStream = tmpIn;
// mmOutStream = tmpOut;
mmBufferedOut = new BufferedOutputStream(tmpOut,80);
// Initial request
btWrite(new DeviceRecord(0, 4));
I have never discovered any problems sending and receiving data via this code. All records are sent and received properly. Only problem was that I am unable to purge the transmit buffer at the moment the operate button was released.
To overcome this problem, I have changed the protocol in such a way, that only a single 'keep turning' telegram is send at a time, the next telegram will be send after a response from the other end (sort of handshaking), the program then continue to run this ping/pong until the button is released.
This method works quite well as the transmit buffer will never hold more than one telegram at a time.
the mentioned problem is solved though but I still have no clue of whether it would be possible to purge a transmit buffer
I'm trying to establish a bluetooth communication between an android phone/tablet (4.0.3), and a bluetooth device, which is an earring reader (Destron Fearring DTR3E, in case you want to know, which I don't suppose you do).
I paired the phone with the reader (the reader has the pairing passcode on a tag) from the bluetooth settings, bluetooth is on of course, and now I'm trying to listen to reads from the device, by means of BluetoothServerSocket. The problem is that the accept call never returns, so obviously I am doing something wrong. The communication is done using RFCOMM.
Code:
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
// Use a temporary object that is later assigned to mmServerSocket,
// because mmServerSocket is final
BluetoothServerSocket tmp = null;
try {
// MY_UUID is the app's UUID string, also used by the client code
String uuid = "00001101-0000-1000-8000-00805F9B34FB";
tmp = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("pdfParserServer", UUID.fromString(uuid));
} catch (Exception e) {
e.printStackTrace();
}
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
// Do work to manage the connection (in a separate thread)
try {
mmServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) { }
}
}
Is there something I am missing?
Thank you!
The only reason that could cause the code never to come back from accept is that, the device "Destron Fearring DTR3E" you are trying to connect to, has actually a bluetoothserver socket and not a bluetooth client, hence, the device might be waiting for you to actually connect to it, in stead of you creating a bluetoothserver socket and waiting for it to connect to your android device, you should read the specs on the device and make sure that actually is you the one that has to open a connection on "Destron Fearring DTR3E" socket...
Hope this helps...
Regards!
I'm facing the following problem:
I am connecting two devices via Bluetooth socket, one tablet android and a bluetooth device like reader barcode, up to now it's ok, the problem is, when a read the barcode by the bluetooth device and I send it to tablet, the bar code sometimes it's sent in two parts, for example, if I read a barcode with content "212154521212", the tablet receive "2121" and after "54521212", Anyone know tell me what should I do to avoid this?
Thanks in advanced.
My code that read the data from bluetooth device:
[code]
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private BluetoothSocket socket;
public ConnectedThread(BluetoothSocket socket) {
this.socket = socket;
InputStream tmpIn = null;
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
new LogDeErrosRodesTablet(e);
Log.e(TAG, e.getMessage());
Log.e(TAG, "Erro no construtor da classe ConnectedThread.");
}
mmInStream = tmpIn;
}
public void run() {
// continua lendo o inputstream até ocorrer um erro
while (true) {
int read = 0;
byte[] buffer = new byte[128];
do {
try {
read = mmInStream.read(buffer);
Log.e(TAG, "read: " + read);
final String data = new String(buffer, 0, read);
Log.e(TAG, "data: " + data);
//TODO
//send data only (bar code) only after read all
Bundle bundle = new Bundle();
bundle.putString(TelaInserirPedido.CODIGO_BARRAS, data);
Message message = new Message();
message.what = TelaInserirPedido.MSG_COD_BARRAS;
message.setData(bundle);
//Send a message with data
handler.sendMessage(message);
} catch(Exception ex) {
read = -1;
return;
}
Log.e(TAG, "inside while.");
} while (read > 0);
Log.e(TAG, "outside of while.");
}
}
public void cancel () {
try {
socket.close ();
} catch ( IOException e) { }
}
}
[/code]
This isn't a Bluetooth error. The Bluetooth device is sending all of the data to your application, but you are reading the stream before all of the data have been received. You could check for the amount of bytes available() on the stream before reading, if you know the exact length of the data; you could concatenate the results of all of the reads until you reach a known end point. Or you could put in an arbitrary time delay and hope the transmission completed in that time.
You would create the Bundle and Message after the while loop that collects the input string, because you don't know the entire string until that loop finishes. (Unless you are expecting multiple strings in one connection, in which case you need more complex code to handle partial numbers).
Use OutputStream.flush() to force send the all data.