Jump to content

Minor issues with Bluetooth in android app

P1X3

I am working on one of my projects, and today's goal was to improve the experience.

The project is fairly simple. Android app connects to arduino through bluetooth and send/receives some data.

Previously, I had to click connect button manually, but now application attempts to connect as long as it is open.

Everything seems to work fine, except minor issues in two cases

  1. Application is started outside of range. Other device is brought closer to the phone, which would allow android app to connect, but that is not happening until thread is restarted.
  2. After being connected, if bluetooth device becomes out of range and then back in range, thread does get reconnected, but state message is never sent or textStatus is never changed.

I will try to double check the code after couple hours, which might help.

Until then, see if maybe you can spot something.

public class BluetoothThread extends Thread{	public enum State	{		DISCONNECTED("Disconnected"),		CONNECTING("Connecting"),		CONNECTED("Connected");				private final String string;		private State(String s)		{			string = s;		}				public String toString()		{			return string;		}	};		// Constants	private final String address = "00:14:01:02:30:79";	private final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");		// Bluetooth	private BluetoothAdapter bluetoothAdapter;	private BluetoothDevice bluetoothDevice;	private BluetoothSocket bluetoothSocket;	private InputStream inputStream;	private OutputStream outputStream;		// Misc	private State state = State.DISCONNECTED;	private boolean running;	private boolean keepConnected;	private LinkedBlockingQueue<byte[]> messageQueue;	private Handler handler;		public BluetoothThread(BluetoothAdapter bluetoothAdapter, Handler handler) throws IOException	{		this.bluetoothAdapter = bluetoothAdapter;		this.handler = handler;		keepConnected = true;			messageQueue = new LinkedBlockingQueue<byte[]>();		bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);		bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(MY_UUID);				inputStream = null;		outputStream = null;				running = true;	}		public void run()	{		while (running)		{			if (keepConnected && state != State.CONNECTED)			{				changeState(State.CONNECTING);				bluetoothAdapter.cancelDiscovery();									try				{					bluetoothSocket.connect();				}				catch (IOException e)				{					Log.d("BMWPi", "Failed to connect.");					try					{						bluetoothSocket.close();					}					catch (IOException e2)					{						Log.d("BMWPi", "Failed to close socket after connection failure.");					}					try {						Thread.sleep(1000);					} catch (InterruptedException e1) {						// TODO Auto-generated catch block						e1.printStackTrace();					}					continue;				}								Log.d("BMWPi", "Connected to device.");				changeState(State.CONNECTED);			}						if (state == State.CONNECTED)			{				Log.d("BMWPi", "State is " + state.toString().toLowerCase() + " and socket is " + (bluetoothSocket.isConnected() ? "connected" : "disconnected") + ".");								if (!bluetoothSocket.isConnected())				{					changeState(State.DISCONNECTED);					continue;				}																if (outputStream == null || inputStream == null)				{					try					{						inputStream = bluetoothSocket.getInputStream();						outputStream = bluetoothSocket.getOutputStream();					}					catch (IOException e)					{						Log.d("BMWPi", e.getMessage());					}				}								if (messageQueue.size() > 0)				{					byte[] message = messageQueue.peek();					if (message != null)					{						try						{							outputStream.write(message);						}						catch (IOException e)						{							Log.d("BMWPi", "Socket is not yet connected.");						}					}					messageQueue.poll();				}			}		}	}		public void cancel()	{		running = false;				try		{			bluetoothSocket.close();		}		catch (IOException e2)		{					}	}		public void Send(byte[] data)	{		messageQueue.add(data);	}		private void changeState(State state)	{		this.state = state;		Message.obtain(handler,Dashboard.MESSAGE_STATE_CHANGE, this.state).sendToTarget();	}}
public class Dashboard extends Activity{	public static final int REQUEST_ENABLE_BLUETOOTH = 1;	public static final String TOAST = "toast";	public static final int MESSAGE_STATE_CHANGE = 1;    public static final int MESSAGE_READ = 2;    public static final int MESSAGE_WRITE = 3;    public static final int MESSAGE_DEVICE_NAME = 4;    public static final int MESSAGE_TOAST = 5;	    public static byte CAR_STATE_OFF = (byte)0x00;    public static byte CAR_STATE_ACC = (byte)0x01;    public static byte CAR_STATE_IGN = (byte)0x03;    public static byte CAR_STATE_STR = (byte)0x07;		private BluetoothThread bluetoothThread = null;	private BluetoothAdapter bluetoothAdapter;	Button btnOff, btnAcc, btnIgn, btnStart, btnStatus;	TextView txtStatus;	    @[member='override2299']    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_dashboard);        		if((bluetoothAdapter = BluetoothAdapter.getDefaultAdapter())==null) 		{ 			Toast.makeText(getApplicationContext(), "Bluetooth device is not found.", Toast.LENGTH_LONG).show(); 			return; 		}        		txtStatus = (TextView) findViewById(R.id.textStatus);		        btnOff = (Button) findViewById(R.id.btnOff);        btnAcc = (Button) findViewById(R.id.btnAcc);        btnIgn = (Button) findViewById(R.id.btnIgn);        btnStart = (Button) findViewById(R.id.btnStart);                btnOff.setOnClickListener(new View.OnClickListener()        {        	@[member='override2299']			public void onClick(View v)			{				bluetoothThread.Send(new byte[] {CAR_STATE_OFF});			}		});                btnAcc.setOnClickListener(new View.OnClickListener()        {        	@[member='override2299']			public void onClick(View v)			{        		bluetoothThread.Send(new byte[] {CAR_STATE_ACC});			}		});                btnIgn.setOnClickListener(new View.OnClickListener()        {        	@[member='override2299']			public void onClick(View v)			{        		bluetoothThread.Send(new byte[] {CAR_STATE_IGN});			}		});                btnStart.setOnTouchListener(new View.OnTouchListener()		{			@[member='override2299']			public boolean onTouch(View v, MotionEvent event)			{				int action = event.getAction();				if (action == MotionEvent.ACTION_DOWN)				{					bluetoothThread.Send(new byte[] {CAR_STATE_STR});				}				else if (action == MotionEvent.ACTION_UP)				{					bluetoothThread.Send(new byte[] {CAR_STATE_IGN});				}			return false;			}		});    }        @[member='override2299']    public synchronized void onResume()    {    	super.onResume();    	    	if (bluetoothThread != null)    	{    		bluetoothThread.cancel();    		bluetoothThread = null;    	}    	    	if (!bluetoothAdapter.isEnabled()) 		{ 			Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 			startActivityForResult(enableBtIntent, REQUEST_ENABLE_BLUETOOTH); 		} 		 		if (bluetoothThread == null) 		{ 			try 			{				bluetoothThread = new BluetoothThread(bluetoothAdapter, mHandler);			} 			catch (IOException e) 			{				Toast.makeText(getApplicationContext(), "BluetoothThread initialization error", Toast.LENGTH_LONG).show();				finish();			} 		} 		 		bluetoothThread.start();    }        @[member='override2299']    public void onPause()    {    	super.onPause();    	if (bluetoothThread != null)    	{    		bluetoothThread.cancel();    		bluetoothThread = null;    	}    }        @[member='override2299']    public boolean onCreateOptionsMenu(Menu menu)    {        getMenuInflater().inflate(R.menu.dashboard, menu);        return true;    }        private final Handler mHandler = new Handler()    {    	@[member='override2299']        public void handleMessage(Message msg)    	{    		switch (msg.what)    		{    			case MESSAGE_STATE_CHANGE:    			{    				BluetoothThread.State state = (BluetoothThread.State)msg.obj;    				txtStatus.setText(state.toString());    			} break;    			case MESSAGE_WRITE:    			{    				Toast.makeText(getApplicationContext(), "Message sent out successfully.", Toast.LENGTH_SHORT).show();    			} break;    			case MESSAGE_READ:    			{    				if (msg.arg1 > 0)    				{    					byte[] tmp = (byte[]) msg.obj;    					Toast.makeText(getApplicationContext(), "Message received: " + bytesToHex(tmp, msg.arg1), Toast.LENGTH_SHORT).show();    				}    			} break;    			case MESSAGE_TOAST:    			{    				Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_LONG).show();    			} break;    		}    	}    };        final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();    public static String bytesToHex(byte[] bytes, int size) {        char[] hexChars = new char[size * 2];        int v;        for ( int j = 0; j < size; j++ ) {            v = bytes[j] & 0xFF;            hexChars[j * 2] = hexArray[v >>> 4];            hexChars[j * 2 + 1] = hexArray[v & 0x0F];        }        return new String(hexChars);    }}
 

 

Link to comment
Share on other sites

Link to post
Share on other sites

Well, I sorted out most of the issues, haven't found a way to tell when connection with device is lost.

 

First, the code below must be placed in onResume and not onCreate. It is also a good measure to the variable in onPause.

if((bluetoothAdapter = BluetoothAdapter.getDefaultAdapter())==null){	Toast.makeText(getApplicationContext(), "Bluetooth device is not found.", Toast.LENGTH_LONG).show();	return;}

Second, when thread is stopped, it is recommended to close streams before socket, separating each close call in separate try catch.

public void cancel(){	running = false;	if (inputStream != null)	{		try { inputStream.close(); } catch (Exception e) {Log.d("BMWPi", e.getMessage());}	}	if (outputStream != null)	{		try { outputStream.close(); } catch (Exception e) {Log.d("BMWPi", e.getMessage());}	}	if (bluetoothSocket != null)	{		try { bluetoothSocket.close(); } catch (Exception e) {Log.d("BMWPi", e.getMessage());}	}}

To fix the last issue, I am probably going to setup a broadcast receiver to listen for ACTION_ACL_DISCONNECTED message. However, I suspect that it will not work because android considers connection "connected" until socket is closed. So there might not be a solution...

 

 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×