9
votes

I am developing an Android Application that connects to a BLE Device and reads the Gatt Services and Gatt Characteristics. I used the BluetoothLeGatt sample project from the Android Development Site as my reference.

So far, I am able to programmatically connect to a device (I took note of my Device's Address to be able to do this) and filter out the specific Gatt Service I want to read and that Services' specific Characteristics by taking note of the UUID of both the Service and the Characteristics. The sample provided by Google also updates whenever there's a message sent from my BLE Device to my Android Application. Overall, I have no problems at this end.

However, upon reading up further on GATT, I found that it is possible to connect to multiple BLE devices (all slaves OR servers - being the ones that send the data) using a single Android Application (as master OR client - as the one who receives said data). So what I tried to do was to have 2 BLE Devices (different Address), took note of their Address, and then my application tries to connect to them once the application sees that those 2 addresses are up and running.

In code, I call this function when I see my 2 BLE Devices:

private void connectToDevice(){

    mDeviceName = deviceList.get(currentIndex).getName();
    mDeviceAddress = deviceList.get(currentIndex).getAddress();

    Log.e(TAG, "connecting to device name = " + mDeviceName);

    mBluetoothLeService.connect(mDeviceAddress);
}

Where currentIndex is initially set to zero. Then once I get a successful connection, I do:

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
            Log.e(TAG, "connected");
            mConnected = true;

            if(currentIndex < deviceList.size()-1) currentIndex ++;
            connectToDevice();

        } 
    }
};

Where I check if I still have devices to connect to in my deviceList, if so, increment my counter and then connect until I exhaust everything in my list.

However, I seem to have no success at all using this method.

Kindly note that switching connection (round robin) between my devices isn't an option. This will be an issue when I have a lot of devices and it's important to get their messages real time without delays. This said, I have to have a live connection to my devices.

Has anyone tried to connect to multiple BLE Devices in Android? I'm not sure on how to proceed on this.

3
Yes you can and i have done this using a simple loop. What i do is, First I scan for the devices, once they are found I put it in an arraylist and call this connect method. To successfully establish the connection you will also need a lock & wait mechanism since you want to try to establish the second connection after the first one.Gautam
@Gautam would you mind posting your code that made it work? I'd appreciate any leads on this topic.Razgriz

3 Answers

4
votes

Indeed it is possible to connect to more than one peripheral from your Android device. However, it will make your code much more complex since you will need to manage each connection and responses.

For each connection you would have to implement a BluetoothGatt with it's callbacks. I tested it many months ago with a dummy test and as I said, it worked well and I was able to connect to different peripherals. However, if you chain many commands there seem to be some overlapping issues described in this thread.

1
votes

As asked here is the relevant code : (Here the ArrayList contains the founded peripheral devices)

for(int i=0;i< Utility.selectedDeviceList.size();i++) {
            Log.d(Utility.TAG,"state"+ Utility.selectedDeviceList.get(i).getmConnectionState());
            if (Utility.selectedDeviceList.get(i).getmConnectionState() != Utility.CONNECTED) {
                Log.d(Utility.TAG,"Connecting LeSerive::" + Utility.selectedDeviceList.get(i).getAddress());
                Utility.mBluetoothLeService.connect(i, Utility.selectedDeviceList.get(i).getAddress());
            }

        }

This for loop is a part of runnable interface which is called inside a handler having a looper.

public void run() {
    Looper.prepare();
    Looper mLooper = Looper.myLooper();
    Log.d(Utility.TAG,"BLE Thread Started::");
    mHandler = new Handler(mLooper) {

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case Utility.BLE_SYNC:
                    Log.d(Utility.TAG,"BLE Sync Connecting::");
                    mHandler.post(SynState);
                    break;
       }
    };
    Looper.loop();
}

I used this approach because their is lot of communication between peripherals to send and receive the data from them.

This is the connect method which inside a Service :

public boolean connect(int tag,final String address) {
    if (mBluetoothAdapter == null || address == null) {
        Log.w(Utility.TAG, "BluetoothAdapter not initialized or unspecified address.");
        return false;
    }

    Utility.selectedDeviceList.get(tag).setmConnectionState(Utility.CONNECTING);

    if( Utility.selectedDeviceList.get(tag).getmBluetoothGatt()==null){
        Log.w(Utility.TAG, "new connect :: "+ Utility.selectedDeviceList.get(tag).getAddress());

        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(Utility.TAG, "Device not found.  Unable to connect.");
            return false;
        }
        try {
            Utility.selectedDeviceList.get(tag).setmBluetoothGatt(device.connectGatt(this, false, mGattCallback));
        }
        catch (Exception e)
        {
            e.printStackTrace();
            Log.d(Utility.TAG,"ConnectGatt exception caught");
        }
    }

    return true;
}

This is the mGattCallBack :

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        Log.d(Utility.TAG, "onServicesDiscovered");

    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {


    }
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
        Log.d(Utility.TAG,">>onCharacteristicWrite");

    }
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {

    }
};

Hope it clears few things for you

0
votes

It is possible to connect to multiple devices at a time. in my experience it works pretty stable and the number of devices you can connect to (stable) depends on your hardware. I found out that best practise (for me) was to create one separate service for the scanning stuff and one service for each Bluetoothconnection. it's important not to use bound services because the termination of a connection is not stible when binding to it. With this pattern you can control your connection easily. To transport data out of your service you can use a broadcastreceiver, for example if you want to display the data in your main activity. Termination of the connection is pretty important so stop the service and in onDestroy call

mConnectedGatt.disconnect();
ble_device=null;

For the Scanning part I've used a List of Strings where I saved all the mac Adresses I want to find. When i found one device I deleted it from the list and if the list is empty it stopped the scanner service. To transmit my found device I used a broadcastreceiver and sent it to my main Activity. There I transmitted it to the right service. Hope this helps