I'm building an android application that keeps tracks of the Bluetooth connection on a device and triggers an alarm when they get out of range.
The Android documentation asks for a UUID in order to establish a connection.
An 'uuid' is a Universally Unique Identifier (UUID) standardized 128-bit format for a string ID used to uniquely identify information. It's used to uniquely identify your application's Bluetooth service.
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
I am not installing an app on both devices, so I don't get to set my own UUID, I want to use android's instead... but I can't find this in the docs anywhere.
Maybe I'm not approaching the problem correctly. Any help will be appreciated.
Thanks in advance
You can get the UUID from the BluetoothDevice
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice. This code below show how to do it and handle the case that the UUID from the device is not found and trying a default UUID.
// Default UUID
private UUID DEFAULT_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
try {
// Use the UUID of the device that discovered // TODO Maybe need extra device object
if (mmDevice != null)
{
Log.i(TAG, "Device Name: " + mmDevice.getName());
Log.i(TAG, "Device UUID: " + mmDevice.getUuids()[0].getUuid());
tmp = device.createRfcommSocketToServiceRecord(mmDevice.getUuids()[0].getUuid());
}
else Log.d(TAG, "Device is null.");
}
catch (NullPointerException e)
{
Log.d(TAG, " UUID from device is null, Using Default UUID, Device name: " + device.getName());
try {
tmp = device.createRfcommSocketToServiceRecord(DEFAULT_UUID);
} catch (IOException e1) {
e1.printStackTrace();
}
}
catch (IOException e) { }
Related
I tried to create a simple android application to connect to my ELM327 device to get some car diagnostic data. But I wasn't able to set up the bluetooth connection b/t my android phone and my ELM327 device.
My code is very simple as below:
public class Bluetooth {
protected BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
private ConnectThread mConnectThread = null;
private AcceptThread mAcceptThread = null;
private WorkerThread mWorkerThread = null;
private BluetoothDevice mOBDDevice = null;
private BluetoothSocket mSocket = null;
private String uuid;
Bluetooth() {
mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices;
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())
return;
pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
for (BluetoothDevice device : pairedDevices) {
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
//TODO: check whether this is OBD and whether it is connected
//by sending a command and check response
if (deviceName.contains("OBD")) {
mOBDDevice = device;
uuid = device.getUuids()[0].toString();
break;
}
}
}
mBluetoothAdapter.cancelDiscovery();
}
/**
* Start the chat service. Specifically start AcceptThread to begin a session
* in listening (server) mode. Called by the Activity onResume()
*/
public synchronized void connect()
{
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
mSocket = mOBDDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and return.
try {
mSocket.close();
} catch (IOException closeException) {
Log.e(TAG, "Could not close the client socket", closeException);
}
return;
}
}
}
In the mainactivity, I will first new a Bluetooth class then call bluetooth.connect():
mBluetooth = new Bluetooth();
mBluetooth.connect();
When I debug the program, I was able to get my ELM327 bluetooth device by querying all the bonded devices with a name of "OBD". I also was able to get the device's uuid and create a socket using createRfcommSocketToServiceRecord. But in the connect function, mSocket.connect() always fail with a return value of -1 and get a IOexception.
My questions are:
When my android application connect to the ELM327 device, my android phone is the bluetooth client and my ELM327 device is the bluetooth server, is this understanding correct?
Is there a server program running on my ELM327 device listening and accept incoming connection? Is this defined behavior of ELM327 protocol?
Any idea why mSocket.connect()has failed? Any idea on how to look into this issue? Or any obvious error in my program? Thanks.
problem solved. see source codes below:
public synchronized void connect() throws IOException {
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
mSocket = mOBDDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mSocket.connect();
} catch (IOException e1) {
Log.e(TAG, "There was an error while establishing Bluetooth connection. Falling back..", e1);
Class<?> clazz = mSocket.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[]{Integer.TYPE};
try {
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[]{Integer.valueOf(1)};
mFallbackSocket = (BluetoothSocket) m.invoke(mSocket.getRemoteDevice(), params);
mFallbackSocket.connect();
mSocket.close();
mSocket = mFallbackSocket;
} catch (Exception e2) {
Log.e(TAG, "Couldn't fallback while establishing Bluetooth connection.", e2);
mSocket.close();
//throw new IOException();
}
}
inputStream = mSocket.getInputStream();
outputStream = mSocket.getOutputStream();
}
I don't know much about Android, although I know about OBD2 and the lot.
It depends on the type of your adapter. If you have a WiFi adapter, you can consider the adapter being the server and you the client. You connect to a socket and then read from it. In the case of a Bluetooth adapter, it's different. If you connect via rfcomm, it's a serial protocol and neither is the server nor the client. If you connect via BTLE, the OBD2 dongle is the Peripheral and you are the Central.
On WiFi adapters, yes. This behavior is not part of ELM327 though. ELM327 only specifies the serial commands. How you transfer these is not part of the spec, since it happens on the layer above (WiFi, rfcomm, BTLE, USB, etc.).
Are you sure that rfcomm works via the socket interface? It's a serial interface, so I would have expected file-like operations.
I have a couple problems regarding creating a bluetooth server and client for a multiplayer game I'm making.
The first problem I have is searching for devices. Google provides following code:
// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
But I can't find where mArrayAdapter is defined or how it should look like.
Second question I have is, when I have a server running on my device, do I have to connect to it also, or does only the other phone have to connect and my server acts as a client and a server?
Third question I have is how to connect to the server. Again, google provides this code:
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket);
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
How do I create and use this class, and what variable should I pass for BluetoothDevice device in the constructor?
Thanks for your help :)
You can see from mArrayAdapter.add(device.getName() + "\n" + device.getAddress()); that a String is added. So it will be defined as:
ArrayAdapter<String> mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.bluetooth_device_name);
Where did you get your code from? The complete code you can find in DeviceListActivity.java of the BluetoothChat example app which comes with the SDk for many versions again and again.
For your second question: One app on a device makes the device visible. The other app on the other device scanns for neighbouring devices and tries to connect. Then both devices have to agree.
I advise you to do the BluetoothChat app from tne examples first. It works out of the box.
The Bluetooth tutorials i read all mentioned that i need to have the same UUID on both sides (Server and Client) to establish a connection between two devices. But what if i dont know the UUID of my Client and if i dont care?
Background information: I have over 1000 microcontrollers with bluetooth. Each microcontroller has a fix and unchangeable UUID. Smartphones should be able to send string messages to that micrcontrollers (single connection, one smartphone is controlling one microcontroller). It should not matter which Smartphone is controlling which microcontroller. So in fact i really dont care about the UUID of the Client.
So my Smartphone is the Server and is opening a listening thread for incoming Bluetooth connections but i have to put in a UUID here:
tempBluetoothServerSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
But when i have thousand different UUID's and i really dont care about the UUID what should i put in there? Also the BluetoothSocket:
tempBluetoothSocket = this.bluetoothDevice.createRfcommSocketToServiceRecord(MY_UUID);
How to know which UUID?
So the core question is: How can i connect to any microcontroller?
I've been using this:
// Unique UUID for this application
private static final UUID UUID_ANDROID_DEVICE =
UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
private static final UUID UUID_OTHER_DEVICE =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
And it's uses:
public AcceptThread(boolean isAndroid) {
BluetoothServerSocket tmp = null;
// Create a new listening server socket
try {
if(isAndroid)
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_ANDROID_DEVICE);
else
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_OTHER_DEVICE);
} catch (IOException e) { }
mmServerSocket = tmp;
}
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
if(BluetoothService.this.isAndroid)
tmp = device.createRfcommSocketToServiceRecord(UUID_ANDROID_DEVICE);
else
tmp = device.createRfcommSocketToServiceRecord(UUID_OTHER_DEVICE);
} catch (IOException e) { }
mmSocket = tmp;
}
Which allows my devices to connect to any bluetooth device I've tested with. For the sake of testing, it has only been varying bluetooth barcode scanners. Although I believe this is a generic RFCOMM UUID.
It hasn't failed me yet.
I'm struggling with getting consistent bluetooth connections in a star topology. I have one master phone which is a Samsung Galaxy S4 running API 10. All of the phones that connect to the bluetoothserver socket on the S4 are LG Dynamic Tracfones also running API 10.
Over the past few days, I have seen a LOT of conflicting information on the web about what type of connection to use.
This is my current set up:
MASTER CODE
public void acceptConnection() {
.... (enable bt adapter) ...
// initializes a Bluetooth server socket
bluetoothServerSocket = bc.createBluetoothServerSocket();
//connection made to Master, discovery no longer needed
bluetoothAdapter.cancelDiscovery();
BluetoothSocket bluetoothSocket;
// loops until the thread is interrupted or an exception occurs
while (!isInterrupted()) {
try {
// attempts to accept the slave application's connection
bluetoothSocket = bluetoothServerSocket.accept();
} catch (IOException e) {
// prints out the exception's stack trace
e.printStackTrace();
Log.v("Default Thread", "Connection to slave failed.");
// breaks out of the while loop
return;
}
try {
... (enumerate all input and output streams, and all bt sockets) ...
} catch (IOException e) {
// prints out the exception's stack trace
e.printStackTrace();
}
}
This is the method that is called when creating a blueToothServerSocket, and this is where half of my confusion is. How should I listen on the adapter? Currently, I'm doing it insecurely.
public BluetoothServerSocket createBluetoothServerSocket() {
// gets the name of the application
String name = "PVCED";
// gets a common UUID for both the master and slave applications
UUID uuid = UUID.fromString("23ea856c-49da-11e4-9e35-164230d1df67");
// initializes an empty Bluetooth server socket
serverSocket = null;
try {
// creates a Bluetooth socket using a common UUID
serverSocket = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(name, uuid);
} catch (IOException e) {
// prints out the exception's stack trace
e.printStackTrace();
}
return serverSocket;
}
SLAVE CODE
And this is where the other half of my confusion is, how should I create a socket? Currently I'm doing it insecurely.
private BluetoothSocket createBluetoothSocket(Set<BluetoothDevice> pairedDevices) {
// gets a common UUID for both the master and slave applications
UUID uuid = UUID.fromString("23ea856c-49da-11e4-9e35-164230d1df67");
// initialises an empty Bluetooth socket
BluetoothSocket bluetoothSocket = null;
// checks to see if there are any paired devices
if (pairedDevices.size() > 0) {
// loops through each paired device
for (BluetoothDevice device : pairedDevices) {
// checks to see if the name of the paired device is MASTER
if (device.getName().equals("MASTER")) {
try {
master = device;
// creates a Bluetooth socket using a common UUID
//bluetoothSocket = master.createRfcommSocketToServiceRecord(uuid);
//Method m = master.getClass().getMethod("createRfcommSocketToServiceRecord", new Class[] {int.class});
//bluetoothSocket = (BluetoothSocket) m.invoke(master, 1);
bluetoothSocket = master.createInsecureRfcommSocketToServiceRecord(uuid);
} catch(Exception e){
Log.v("Connect Exception", e.getMessage());
}
}
}
}
//check if we paired succesfully to a master, if not, prompt user to do so.
if (master == null){
... (tell user to pair with master via toast) ...
}
return bluetoothSocket;
}
My logcat is often filled with errors such as "Bad File Descriptor", "Unable to start Service Discovery", or "Service Discovery has failed."
What is the best connection scheme to use for my scenario? If you guys need more details on how I'm enabling/disabling bt adapters, or closing bt connections, I can supply more code.
I've written an android application getting data from external sensors using Bluetooth. It's working fine on Sony Ericsson XPERIA but not on a HTC Hero (it finds external devices but it can't get any data from them) I'm wondering why. After some research on the net, I still haven't found any clue.
Anyone had similar bluetooth issues on HTC?
you can make it like this:
private final String PBAP_UUID = "0000112f-0000-1000-8000-00805f9b34fb"; //standard pbap uuid
mSocket = mDevice.createInsecureRfcommSocketToServiceRecord(ParcelUuid.fromString(PBAP_UUID).getUuid())
mSocket.connect();
Just do it.
If I remember correctly the HTC phones had or [have] an issue at a certain API level (maybe 2.1 and below?). The resolution is reflection.
Reference
Disconnect a bluetooth socket in Android
Service discovery failed exception using Bluetooth on Android
Solution
Instead of using
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
use
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
tmp = (BluetoothSocket) m.invoke(device, 1);
to get your BluetoothSocket on certain HTC phones using a certain API level.
Solution Expanded
I recently had an application where I had to account for this, and I did not like using this on non-HTC phones, so I had a conditional to check for HTC, if true then use reflection, otherwise dont.
public BTConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the given BluetoothDevice
if (isAnHTCDevice())
{
try
{
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
tmp = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
}
catch (Exception e)
{
Log.e(BCTAG, "Error at HTC/createRfcommSocket: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_LOG_MESSAGE, "Exception creating htc socket: " + e));
}
}
else
{
try
{
UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (Exception e)
{
Log.e(BCTAG, "Error at createRfcommSocketToServiceRecord: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_LOG_MESSAGE, "Exception creating socket: " + e));
}
}
mmSocket = tmp;
}
isAnHTCDevice():
public boolean isAnHTCDevice()
{
String manufacturer = android.os.Build.MANUFACTURER;
if (manufacturer.toLowerCase().contains("htc"))
return true;
else
return false;
}
you can make it like this:
private final String PBAP_UUID = "0000112f-0000-1000-8000-00805f9b34fb"; //standard pbap uuid
mSocket = mDevice.createInsecureRfcommSocketToServiceRecord(ParcelUuid.fromString(PBAP_UUID).getUuid());
mSocket.connect();
Just do it.