Junaid Hafeez
Junaid Hafeez

Reputation: 1616

android to android communication over ble

I am currently working on a project which exchanges small values from android to android over bluetooth LE. I am using nexus9 for android peripheral, Moto G as central. central scan peripheral perfectly and peripheral's services are being added, now i need to write characteristics on peripheral and then peripheral will acknowledge it to central but what happening is peripheral sends certain bytes to central and not reading central characteristics, even this central working fine for ios peripheral. here is my code for android central

private void startScanning() {
        if ((btAdapter != null) && (!isScanning)) {
            if (scanner == null) {
                Log.d(TAG, "scanner was null");
                scanner = btAdapter.getBluetoothLeScanner();
            }
            if (scanCallback == null)
                setScanCallback(null);

            scanner.startScan(createScanFilters(), createScanSettings(), scanCallback);
            isScanning = true;
            Log.d(TAG, "in startScanning");
            //Toast.makeText(context, context.getString(R.string.scan_started), Toast.LENGTH_SHORT).show();
        }
    }


public void setScanCallback(final ScanCallback callback) {

    scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult scanResult) {
            super.onScanResult(callbackType, scanResult);
            Log.d(TAG, scanResult.getDevice().getName() + " ");

            deviceNames.add(scanResult.getDevice().getName());
            deviceRSSIs.add(String.valueOf(scanResult.getRssi()));
            adapter.notifyDataSetChanged();

            scanResult.getDevice().connectGatt(context, false, new BluetoothGattCallback() {


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


                    Set<BluetoothGattCharacteristic> characteristicSet;

                    switch (newState) {
                        case BluetoothProfile.STATE_DISCONNECTING:

                            break;

                        case BluetoothProfile.STATE_DISCONNECTED:
                            Log.d(TAG, gatt.getDevice().getName() + " is disconnected");
                            //gatt.close();

                            break;

                        case BluetoothProfile.STATE_CONNECTED:
                            Log.d(TAG, gatt.getDevice().getName() + " is connected");
                            gatt.discoverServices();

                            break;

                    }
                }

                @Override
                public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        Log.d(TAG, "Discovered services central");
                        // TODO: Keep this here to examine characteristics
                        // eventually we should get rid of the discoverServices step
                        boolean foundService = false;
                        try {
                            List<BluetoothGattService> serviceList = gatt.getServices();
                            for (BluetoothGattService service : serviceList) {
                                if (service.getUuid().equals(serviceUUID)) {

                                    foundService = true;
                                    HashSet<BluetoothGattCharacteristic> characteristicSet = new HashSet<>();
                                    characteristicSet.addAll(service.getCharacteristics());
                                    discoveredCharacteristics.put(gatt.getDevice().getAddress(), characteristicSet);

                                    for (BluetoothGattCharacteristic characteristic : characteristicSet) {
                                        if (characteristic.getUuid().equals(characteristicUUID)) {
                                            characteristic.setValue(dataToSend.getBytes());
                                            boolean chr =  gatt.writeCharacteristic(characteristic);
                                            characteristic = gatt.getService(serviceUUID).getCharacteristic(characteristicUUID);
                                            gatt.setCharacteristicNotification(characteristic, true);
                                            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
                                            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                            boolean desc = gatt.writeDescriptor(descriptor);
                                            //    setIndictaionSubscription(gatt, characteristic, true);
                                        }

                                    }
                                }
                            }


                        } catch (Exception e) {
                            Log.d(TAG, "Exception analyzing discovered services " + e.getLocalizedMessage());
                            e.printStackTrace();
                        }
                        if (!foundService)
                            Log.d(TAG, "Could not discover chat service!");
                    } else
                        Log.d(TAG, "Discovered services appears unsuccessful with code " + status);
                    super.onServicesDiscovered(gatt, status);
                }

                @Override
                public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                    Log.d(TAG, "onDescriptorWrite");
                }


                @Override
                public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
                    Log.d(TAG, "onCharacteristicChanged");
                    if (!characteristic.getValue().toString().isEmpty()) {
                        MainActivity.this.runOnUiThread(new Runnable() {

                            public void run() {
                                Toast.makeText(getBaseContext(), new String(characteristic.getValue()), Toast.LENGTH_LONG).show();
                                gatt.readRemoteRssi();

                            }
                        });
                    }

                    super.onCharacteristicChanged(gatt, characteristic);
                }
                @Override
                public void onCharacteristicWrite(BluetoothGatt gatt,
                                                  BluetoothGattCharacteristic characteristic, int status) {

                    Log.d(TAG, "onCharacteristicWrite");
                    Exception exception = null;
                    if (status != BluetoothGatt.GATT_SUCCESS) {

                        String msg = "Write was not successful with code " + status;
                        Log.d(TAG, msg);
                        exception = new UnknownServiceException(msg);

                    }
                }


                @Override
                public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
                    Log.i("RSSIValue", gatt.getDevice().getName() + " " + String.valueOf(rssi) + " " + String.valueOf(status));
                    super.onReadRemoteRssi(gatt, rssi, status);
                }

            });


        }

    };

}

here is my code for peripheral.

 private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            if (settingsInEffect != null) {
                Log.d(TAG, "Advertise success TxPowerLv="
                        + settingsInEffect.getTxPowerLevel()
                        + " mode=" + settingsInEffect.getMode());
            } else {
                Log.d(TAG, "Advertise success");
            }
        }

        @Override
        public void onStartFailure(int errorCode) {
            Log.d(TAG, "Advertising failed with code " + errorCode);
        }
    };


    private AdvertiseData createAdvData() {
        AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
        dataBuilder.addServiceUuid(new ParcelUuid(serviceUUID));
        dataBuilder.setIncludeTxPowerLevel(false);
        dataBuilder.setIncludeDeviceName(true);
//        builder.setManufacturerData(0x1234578, manufacturerData);
        return dataBuilder.build();
    }

    private AdvertiseSettings createAdvSettings() {
        AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
        builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
        builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
        builder.setConnectable(false);
        return builder.build();
    }

    private void stopAdvertising() {
        if (isAdvertising) {
            gattServer.close();
            advertiser.stopAdvertising(mAdvCallback);
            isAdvertising = false;
        }
    }


    public void createMessage() {

        // Chop Message!  You can only advertise 20 bytes at a time through ble
        byte[] message = ("my text need to send").getBytes();
        mDataToSend = new ArrayList<>();
        dataToSendIndex = 0;

        for(int i=0;i<Math.ceil((float)message.length/(float)20);i++) {

            byte[] slice = new byte[20];

            for(int ii=0;ii<slice.length && i*20 + ii < message.length;ii++) {
                slice[ii] = message[i*20 + ii];
            }

            mDataToSend.add(slice);

        }

        // Start sending the message
        sendMessage();
    }

    public void sendMessage() {

        if(dataToSendIndex >= mDataToSend.size()) {  // If we have incremented through the message, the message is sent
            Log.d("auto", "PeripheralActivity > sendMessage() > Message Sent");
            // Done Sending!
            return;
        }

        // Set the value of the characteristic to the next data to send
        mCharacteristic.setValue(mDataToSend.get(dataToSendIndex));
        // Notify the subscriber you have changed the value
        gattServer.notifyCharacteristicChanged(btClient, mCharacteristic, true);

        // Increment index
        dataToSendIndex ++;
    }

    BluetoothGattServerCallback gattCallback = new BluetoothGattServerCallback() {
        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            super.onConnectionStateChange(device, status, newState);
        }

//        @Override
//        public void onServiceAdded(int status, BluetoothGattService service) {
//            super.onServiceAdded(status, service);
//        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
            Log.d(TAG, characteristic.toString());
        }

        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
            btClient = device;

            gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
            createMessage();

        }

        @Override
        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
            super.onDescriptorReadRequest(device, requestId, offset, descriptor);
        }

        @Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);

            btClient = device;

            gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
            createMessage();
        }

//        @Override
//        public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
//            super.onExecuteWrite(device, requestId, execute);
//        }

        @Override
        public void onNotificationSent(BluetoothDevice device, int status) {
            super.onNotificationSent(device, status);
            sendMessage();
        }

//        @Override
//        public void onMtuChanged(BluetoothDevice device, int mtu) {
//            super.onMtuChanged(device, mtu);
//        }
    };
    private void startGattServer() {
        // Create GATT Service
        service = new BluetoothGattService(serviceUUID , BluetoothGattService.SERVICE_TYPE_PRIMARY);


        // Create Config Descriptor
        BluetoothGattDescriptor dataDescriptor = new BluetoothGattDescriptor(CLIENT_CHARACTERISTIC_CONFIG, BluetoothGattDescriptor.PERMISSION_WRITE | BluetoothGattDescriptor.PERMISSION_READ);


        // Add Descriptor
        mCharacteristic.addDescriptor(dataDescriptor);
                        BluetoothGattDescriptor.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);

        // Add Characteristic
        service.addCharacteristic(mCharacteristic);

        gattServer = manager.openGattServer(this, gattCallback);
        gattServer.addService(service);


    }

P.S: there may be a basic mistake which i couldn't able to detect, any help would be very appreciable :)

Upvotes: 1

Views: 1162

Answers (1)

einschnaehkeee
einschnaehkeee

Reputation: 1888

I know this is a pretty "old" question, but maybe this answer helps others:

When you're communicating with a GATT server, try not to stack your queries, but always read/write once and wait for the answer or else the server might ignore everything you've send to it.

So in your case it would be

boolean chr =  gatt.writeCharacteristic(characteristic);

--> wait for onCharacteristicWrite()

then do

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean desc = gatt.writeDescriptor(descriptor);
gatt.setCharacteristicNotification(characteristic, true);`

--> wait for onDescriptorWrite()

It's a bit cumbersome to do, but it will work far more reliable that sending multiple queries. Hope that helps.

Upvotes: 1

Related Questions