Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android how to read multiple BLE characteristics with a PriorityQueue

A bit stuck here, might need your help. I want to read several BLE characteristics at once, some people suggest using PriorityQueue for that. I already know all the uuids, etc. just need a way to read several at once. Could anyone explain how exactly should it look like? Or maybe there is yet another easier solution?

Thanks in advance, here is my code:

public static final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    PriorityQueue<BluetoothGattCharacteristic> queue = new PriorityQueue<BluetoothGattCharacteristic>();

    // When connection state changes
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            Log.v(TAG, "Connected!");
            gatt.discoverServices();
        }
        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            Log.v(TAG, "Disconnected...");

        }
    }

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

        List<BluetoothGattService> services = gatt.getServices();
        BluetoothGattService rightService = null;

        for (int i = 0; i < services.size(); i++) {
            if (services.get(i).getCharacteristics().size() > 8) {
                rightService = services.get(i);
            }
        }

        List<UUID> uuidsList;

        UUID TRANSMISSION_POWER = rightService.getCharacteristics().get(4).getUuid();
        UUID BROADCASTING_INTERVAL = rightService.getCharacteristics().get(6).getUuid();
        UUID BEACON_NAME = rightService.getCharacteristics().get(8).getUuid();
        UUID CONNECTION_MODE = rightService.getCharacteristics().get(9).getUuid();
        //UUID SOFT_REBOOT = rightService.getCharacteristics().get(10).getUuid();

        uuidsList = new ArrayList<UUID>();

        uuidsList.add(TRANSMISSION_POWER);
        uuidsList.add(BROADCASTING_INTERVAL);
        uuidsList.add(BEACON_NAME);
        uuidsList.add(CONNECTION_MODE);
        //uuidsList.add(SOFT_REBOOT);

        queue.add(rightService.getCharacteristic(uuidsList.get(0)));
        queue.add(rightService.getCharacteristic(uuidsList.get(1)));
        queue.add(rightService.getCharacteristic(uuidsList.get(2)));

    }

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

        Log.v(TAG, "CHARACTERISTIC VALUE___: " + characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0));
        onServicesDiscovered(gatt, 0);

    }

};

UPDATE:

even after putting them on different threads it still only reacts to one gatt.readCharacteristic(...). like following:

// Gatt Callback
public static final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    // When connection state changes
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            Log.v(TAG, "Connected!");
            gatt.discoverServices();
        }
        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            Log.v(TAG, "Disconnected...");

        }
    }

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


        List<BluetoothGattService> services = gatt.getServices();

        /*
        DISPLAY ALL SERVICES AND CHARACTERISTICS

        for (int i = 0; i < services.size(); i++) {
            Log.v(TAG, "SERVICE____: " + services.get(i).getUuid());

            for (int k = 0; k < services.get(i).getCharacteristics().size(); k++) {
                Log.v(TAG, "CHARACTERISTIC____: " + services.get(i).getCharacteristics().get(k).getUuid());
            }

        }
        */

        BluetoothGattService rightService = null;

        for (int i = 0; i < services.size(); i++) {
            if (services.get(i).getCharacteristics().size() > 8) {
                rightService = services.get(i);
            }
        }

        List<UUID> uuidsList;

        UUID TRANSMISSION_POWER = rightService.getCharacteristics().get(4).getUuid();
        UUID BROADCASTING_INTERVAL = rightService.getCharacteristics().get(6).getUuid();
        UUID BEACON_NAME = rightService.getCharacteristics().get(8).getUuid();
        UUID CONNECTION_MODE = rightService.getCharacteristics().get(9).getUuid();
        //UUID SOFT_REBOOT = rightService.getCharacteristics().get(10).getUuid();

        uuidsList = new ArrayList<UUID>();

        uuidsList.add(TRANSMISSION_POWER);
        uuidsList.add(BROADCASTING_INTERVAL);
        uuidsList.add(BEACON_NAME);
        uuidsList.add(CONNECTION_MODE);
        //uuidsList.add(SOFT_REBOOT);


        class powerThread extends Thread{

            UUID uuid;
            BluetoothGatt gatt;
            BluetoothGattService service;
            public powerThread(UUID uuid, BluetoothGatt gatt, BluetoothGattService service) {
                this.gatt = gatt;
                this.service = service;
                this.uuid = uuid;
            }
            @Override
            public void run() {
                gatt.readCharacteristic(service.getCharacteristic(uuid));
            }
        }
        powerThread pt = new powerThread(TRANSMISSION_POWER, gatt, rightService);
        pt.run();


        class intervalThread extends Thread{

            UUID uuid;
            BluetoothGatt gatt;
            BluetoothGattService service;
            public intervalThread(UUID uuid, BluetoothGatt gatt, BluetoothGattService service) {
                this.gatt = gatt;
                this.service = service;
                this.uuid = uuid;
            }
            @Override
            public void run() {
                gatt.readCharacteristic(service.getCharacteristic(uuid));
            }
        }
        intervalThread it = new intervalThread(BROADCASTING_INTERVAL, gatt, rightService);
        it.run();


    }

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

        Log.v(TAG, "CHARACTERISTIC VALUE___: " + characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0));

    }

};
like image 558
Robert K. Avatar asked Nov 17 '15 07:11

Robert K.


2 Answers

To anyone who might encounter the same problem, here is an easy solution using a List<> of characteristics.

public static final BluetoothGattCallback readGattCallback = new BluetoothGattCallback() {

    List<BluetoothGattCharacteristic> chars = new ArrayList<>();

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

        if (newState == BluetoothProfile.STATE_CONNECTED) {
            Log.v(TAG, "Connected!");
            broadcastingInterval = 999;
            transmissionPower = 999;
            gatt.discoverServices();
        }
        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            Log.v(TAG, "Disconnected...");

        }
    }

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

        List<BluetoothGattService> services = gatt.getServices();
        BluetoothGattService rightService = null;

        for (int i = 0; i < services.size(); i++) {
            if (services.get(i).getCharacteristics().size() > 8) {
                rightService = services.get(i);
            }
        }

        chars.add(rightService.getCharacteristics().get(4));
        chars.add(rightService.getCharacteristics().get(6));

        requestCharacteristics(gatt);

    }

    public void requestCharacteristics(BluetoothGatt gatt) {
        gatt.readCharacteristic(chars.get(chars.size()-1));
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {

            if (characteristic.getUuid().toString().substring(7, 8).equals("5")) {
                transmissionPower = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
                Log.v(TAG, "tPOWER READ");

            } else if (characteristic.getUuid().toString().substring(7,8).equals("7")) {
                broadcastingInterval = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
                Log.v(TAG, "INTERVAL READ");
            }

            chars.remove(chars.get(chars.size() - 1));

            if (chars.size() > 0) {
                requestCharacteristics(gatt);
            } else {
                gatt.disconnect();
            }
        }
    }

};
  1. Create a list of characteristics
  2. In onServicesDiscovered populate the list with characteristics you want to read/write
  3. Create a new method called requestCharacteristics(gatt) and pass the gatt object to it. Call this method from onServicesDiscovered after adding characteristics to the list.
  4. In requestCharacteristics() method call gatt.readCharacteristic(chars.get(chars.size()-1));
  5. In onCharacteristicRead check if size of your list is not zero, then read your characteristic, remove the last item of your list and call requestCharacteristic() again.
  6. That's all
like image 167
Robert K. Avatar answered Oct 02 '22 18:10

Robert K.


Thanks a lot for your post, Robert K. I wanted my app to read all the characteristic values by itself and show the characteristic value in the ExpandableList instead of the UUID. Robert K's code worked for me but I have experienced an offset of the values that were being displayed. So I made some slight changes and want to share it: 1.

  1. This is how I modified onServicesDiscovered and OnCharacteristicRead:

        public List<BluetoothGattCharacteristic> chars = new ArrayList<>();
    
    public void requestCharacteristics(BluetoothGatt gatt) {
            gatt.readCharacteristic(chars.get(chars.size() - 1));
    }
    
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
    
            List<BluetoothGattService> services = gatt.getServices();
            for(BluetoothGattService service:services){
                chars.addAll(service.getCharacteristics());
            }
            requestCharacteristics(gatt);
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }
    
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            chars.remove(chars.get(chars.size() - 1));
            if (chars.size() > 0) {
                requestCharacteristics(gatt);
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            } else {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            }
        }
    }
    
  2. the introduced List of characteristics is emptied after each disconnection.

  3. I introduced a list to save the characteristic values and an integer showing which index I am at (these need to be emptied / set to zero after each disconnection). I modified the displayData method, which is called after each characteristicRead:

public

public List<String> mCharValues = new ArrayList<>();
public int idx_charValues = 0;
private void displayData(String data) {
    if (data != null) { mCharValues.add(0, data); }
    else { mCharValues.add(0, "no Data"); }
}
  1. I want the characteristic values to be displayed on the ExpandableList as soon as services are discovered. The method displayGattServices fills the ExpandableList with the services' and characteristics' names and UUIDs.

Instead of filling the UUID, I fill the corresponding element of mCharValues

currentCharaData.put(LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString));
currentCharaData.put(LIST_UUID, mCharValues.get(idx_charValues));
idx_charValues = idx_charValues+1;
gattCharacteristicGroupData.add(currentCharaData);
  1. Finally, I commented out the content of the ExpandableListView.OnChildClickListener. Apart from what I introduced here I left the rest of the code pretty much the same.
like image 37
Roia Avatar answered Oct 02 '22 19:10

Roia