I am NOT a java devloper. I work primarily in PHP and Asp.Net so you'll have to forgive me for this post, but i'm hitting up my favorite community of geeks for help as I'm sure many of you have written android bluetooth robot controllers before.
So after a long time of trying to create an application that can do serial BT communication i finally found a working example of code to send data to/from my arduino via a bluetooth module. The code on the arduino is really basic, it just parses the bytes sent for a few commands like "F", "B", "R", "L", 5... and then runs a motor driver to drive my robot using a motor controller.
(also note that the arduino/hardware is solid because i run this all the time with an android application "Bluetooth Serial Controller" and it works perfectly)
I set up the buttons and seem to have the messages being sent fairly well, but where i'm running into a few problems with stability:
1) The application currently allows multiple connections to open and can open a bunch of sockets that aren't closing. I'm pretty sure i can find a way to close them on application exit or something like that. I have to reboot the phone if i accidentally make this happen.
2) The bluetooth module (adafruit ez-link) that i'm using sometimes seems to "hang" meaning it just has a solid red light on and doesn't want to accept commands until i turn it off and back on again.
3) Sometimes data doesn't seem to go through. meaning i send a command and on my phoneit tells me it was sent, but on the robot it doesn't respond. may be related to #2 above.
4) THIS IS THE BIGGEST ONE. The code example i found only has one class and everything runs on the main activity. I THINK the author didn't set up the threads properly so the application doesn't make good use of threading. Being a newbie java developer i'm a little confused about how to properly fix this to use threading correctly. Right now i keep getting an error in my Log saying that frames are skipping (sometimes hundreds to thousands) from the android "coreographer".
If someone who is smarter and more experienced than me could take a look and shed some light as to what i can do to fix my application, or point me to a CLEARLY written tutorial on how to make this work i'd be forever grateful.
Here is the full source of my MainActivity:
package com.example.mcr_bt;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;
public class MainActivity extends Activity {
TextView myLabel;
EditText myTextbox;
BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;
OutputStream mmOutputStream;
InputStream mmInputStream;
Thread workerThread;
byte[] readBuffer;
int readBufferPosition;
int counter;
volatile boolean stopWorker;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button openButton = (Button)findViewById(R.id.open);
Button sendButton = (Button)findViewById(R.id.send);
Button closeButton = (Button)findViewById(R.id.close);
Button bF = (Button)findViewById(R.id.buttonF);
Button bL = (Button)findViewById(R.id.buttonL);
Button bR = (Button)findViewById(R.id.buttonR);
Button bB = (Button)findViewById(R.id.buttonB);
Button bH = (Button)findViewById(R.id.buttonH);
myLabel = (TextView)findViewById(R.id.label);
myTextbox = (EditText)findViewById(R.id.entry);
//Open Button
openButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
findBT();
openBT();
}
catch (IOException ex) { }
}
});
//Send Button
sendButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
sendData(myTextbox.getText().toString());
}
catch (IOException ex) {
showMessage("SEND FAILED");
}
}
});
bF.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
sendData("F");
}
catch (IOException ex) {
showMessage("SEND FAILED");
}
}
});
bL.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
sendData("L");
}
catch (IOException ex) {
showMessage("SEND FAILED");
}
}
});
bR.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
sendData("R");
}
catch (IOException ex) {
showMessage("SEND FAILED");
}
}
});
bB.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
sendData("B");
}
catch (IOException ex) {
showMessage("SEND FAILED");
}
}
});
bH.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
sendData("5");
}
catch (IOException ex) {
showMessage("SEND FAILED");
}
}
});
//Close button
closeButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
closeBT();
}
catch (IOException ex) {
showMessage("Close Failed");
}
}
});
}
void findBT() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null) {
myLabel.setText("No bluetooth adapter available");
}
if(!mBluetoothAdapter.isEnabled()) {
Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetooth, 0);
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if(pairedDevices.size() > 0) {
for(BluetoothDevice device : pairedDevices) {
if(device.getName().equals("Adafruit EZ-Link 05f5")) {
mmDevice = device;
break;
}
}
}
myLabel.setText("Bluetooth Device Found");
}
void openBT() throws IOException {
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); //Standard //SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
beginListenForData();
myLabel.setText("Bluetooth Opened");
}
void beginListenForData() {
final Handler handler = new Handler();
final byte delimiter = 10; //This is the ASCII code for a newline character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable() {
public void run() {
while(!Thread.currentThread().isInterrupted() && !stopWorker) {
try {
int bytesAvailable = mmInputStream.available();
if(bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for(int i=0;i<bytesAvailable;i++) {
byte b = packetBytes[i];
if(b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable() {
public void run() {
myLabel.setText(data);
}
});
}
else {
readBuffer[readBufferPosition++] = b;
}
}
}
}
catch (IOException ex) {
stopWorker = true;
}
}
}
});
workerThread.start();
}
void sendData(String msg) throws IOException {
mmOutputStream.write(msg.getBytes());
myLabel.setText("Data Sent");
}
void closeBT() throws IOException {
stopWorker = true;
mmOutputStream.close();
mmInputStream.close();
mmSocket.close();
myLabel.setText("Bluetooth Closed");
}
private void showMessage(String theMsg) {
Toast msg = Toast.makeText(getBaseContext(),
theMsg, (Toast.LENGTH_LONG)/160);
msg.show();
}
}