Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing data to Bluetooth LE characteristic in Android

Tags:

Although similar questions have been asked but it's slightly different. I know how to pass data to a connected BLE device but I think I'm doing something wrong for which I need help. The code below contains all the methods from my class that is extending BroadcastReceiver.

  1. I scan and connect to a device specified by `PEN_ADDRESS`.
  2. In `onServicesDiscovered` method I look for a service whose `UUID` contains `abcd`.
  3. Then I loop through the characteristics of this services and look for three characteristics with specific strings in their `UUID`.
  4. The third characteristic is a writable characteristic in which I'm trying to write data by calling the method `writeCharac(mGatt,writeChar1,123);` The data `123` passed above is just a dummy data.

I debugged my code while trying writing to this characteristic but on putting breakpoints inside the writeCharac method, I found that the status value is false, indicating that the write was not successful. Am I missing something here? Please do help!

public class BackgroundReceiverFire extends BroadcastReceiver {
Context context;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mGatt;
private BluetoothLeService mBluetoothLeService;
private boolean mScanning;
private final String TAG = "READING: ";
private BluetoothDevice mDevice;
private Handler mHandler;
private static final int REQUEST_ENABLE_BT = 1;
private final String PEN_ADDRESS = "FB:23:AF:42:5C:56";
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;

    public void onReceive(Context context, Intent intent) {
        this.context = context;
        Toast.makeText(context, "Started Scanning", LENGTH_SHORT).show();
        initializeBluetooth();
        startScan();
    }

    private void initializeBluetooth() {
        mHandler = new Handler();

        // Use this check to determine whether BLE is supported on the device.  Then you can
        // selectively disable BLE-related features.
        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
        // BluetoothAdapter through BluetoothManager.
        final BluetoothManager bluetoothManager =
                (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            Toast.makeText(this.context, "No Bluetooth", LENGTH_SHORT).show();
            return;
        }
    }

    private void startScan() {
        scanLeDevice(true);
    }

    private void stopScan() {
        scanLeDevice(false);
    }

    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            //Scanning for the device
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }

    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    if (device.getAddress().matches(PEN_ADDRESS)) {
                        connectBluetooth(device);
                        Toast.makeText(context, "Device Found: " + device.getAddress(), Toast.LENGTH_LONG).show();

                    }
                }
            };

    private void connectBluetooth(BluetoothDevice insulinPen) {
        if (mGatt == null) {
            Log.d("connectToDevice", "connecting to device: " + insulinPen.toString());
            mDevice = insulinPen;
            mGatt = insulinPen.connectGatt(context, true, gattCallback);
            scanLeDevice(false);// will stop after first device detection
        }
    }

    private void enableBluetooth() {
        if (!mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.enable();
        }
        scanLeDevice(true);
    }

    private void disableBluetooth() {
        if (mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.disable();
        }
    }

    private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {


        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.i("onConnectionStateChange", "Status: " + status);
            switch (newState) {
                case BluetoothProfile.STATE_CONNECTED:
                    gatt.discoverServices();
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    Log.e("gattCallback", "STATE_DISCONNECTED");
                    Log.i("gattCallback", "reconnecting...");
                    BluetoothDevice mDevice = gatt.getDevice();
                    mGatt = null;
                    connectBluetooth(mDevice);
                    break;
                default:
                    Log.e("gattCallback", "STATE_OTHER");
                    break;
            }

        }

        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            mGatt = gatt;
            List<BluetoothGattService> services = mGatt.getServices();
            Log.i("onServicesDiscovered", services.toString());
            Iterator<BluetoothGattService> serviceIterator = services.iterator();
            while(serviceIterator.hasNext()){
                BluetoothGattService bleService = serviceIterator.next();
                if(bleService.getUuid().toString().contains("abcd")){
                    //Toast.makeText(context,"Got the service",Toast.LENGTH_SHORT);
                    BluetoothGattCharacteristic readChar1 = bleService.getCharacteristics().get(0);
                    for (BluetoothGattDescriptor descriptor : readChar1.getDescriptors()) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        mGatt.writeDescriptor(descriptor);
                        mGatt.setCharacteristicNotification(readChar1, true);
                    }
                    //mGatt.readCharacteristic(readChar1);

                    BluetoothGattCharacteristic readChar2 = bleService.getCharacteristics().get(1);
                    for (BluetoothGattDescriptor descriptor : readChar2.getDescriptors()) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        mGatt.writeDescriptor(descriptor);
                        mGatt.setCharacteristicNotification(readChar2, true);
                    }
                    //mGatt.readCharacteristic(readChar2);

                    BluetoothGattCharacteristic writeChar1 = bleService.getCharacteristics().get(2);
                    for (BluetoothGattDescriptor descriptor : writeChar1.getDescriptors()) {
                        descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        mGatt.writeDescriptor(descriptor);
                        mGatt.setCharacteristicNotification(writeChar1, true);
                    }
                    writeCharac(mGatt,writeChar1,123);
                }
            }

            //gatt.readCharacteristic(therm_char);

        }
        public void writeCharac(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int value ){
            if (mBluetoothAdapter == null || gatt == null) {
                Log.w(TAG, "BluetoothAdapter not initialized");
                return;
            }
/*
            BluetoothGattCharacteristic charac = gattService
                    .getCharacteristic(uuid);
*/
            if (charac == null) {
                Log.e(TAG, "char not found!");
            }

            int unixTime = value;
            String unixTimeString = Integer.toHexString(unixTime);
            byte[] byteArray = hexStringToByteArray(unixTimeString);
            charac.setValue(byteArray);
            boolean status = mGatt.writeCharacteristic(charac);
            if(status){
                Toast.makeText(context,"Written Successfully",Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(context,"Error writing characteristic",Toast.LENGTH_SHORT).show();
            }
        }

        public byte[] hexStringToByteArray(String s) {
            int len = s.length();
            byte[] data = new byte[len/2];

            for(int i = 0; i < len; i+=2){
                data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
            }

            return data;
        }

        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic
                                                 characteristic, int status) {
            Log.i("onCharacteristicRead", characteristic.toString());
            String characteristicValue = characteristic.getValue().toString();
            Log.d("CHARACTERISTIC VALUE: ", characteristicValue);
            gatt.disconnect();
        }

        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic
                                                    characteristic) {
            String value = characteristic.getValue().toString();
            Log.d(TAG,value);
        }

    };
like image 277
Abhinav Tripathi Avatar asked Mar 22 '16 19:03

Abhinav Tripathi


2 Answers

Although the BLE API is asynchronous in nature, the actual signal transmission is inevitably synchronous. You have to wait for the previous connect/write/read call to callback before starting any connecting/writing/reading operation.

In your code onServicesDiscovered(BluetoothGatt gatt, int status) function, you called mGatt.writeDescriptor(descriptor) twice before trying to write the characteristic. The API will refuse to start your write request as it is being busy writing the descriptor, and return false for your mGatt.writeCharacteristic(charac) call.

So just wait for the writeDescriptor to callback before calling writeCharacteristic. This nature is not well documented but you can find some source here and here.

like image 168
reTs Avatar answered Nov 15 '22 07:11

reTs


Thanks @reTs and @pooja for suggestions. The issue was due to these two lines
mGatt = null
connectBluetooth(mDevice);
in STATE_DISCONNECTED. I had figured it out but don't remember the exact reason. Posting it just in case it is helpful.

like image 40
Abhinav Tripathi Avatar answered Nov 15 '22 07:11

Abhinav Tripathi