Auto connecting to a BLE device

14,040

Solution 1

For starters, I would recommend commenting out the bond code (Everything after if(device.getBondState().. in the onLeScan method) The whole bonding process was unstable on 4.3 (Nexus devices at least) and became more stable on 4.4. You should be able to discover devices, and with the BluetoothDevice the user selects you should call ConnectGatt after stopping discovery. This will attempt to connect to the Gatt server on the device. If the connection is successful, you should receive a callback on your connectionStateChange indicating that the connection was successful.

The concept behind bonding is related to pairing with the device and exchanging keys if your characteristics are encrypted. Normally you should be able to connect to the Gatt server without needing to bond, but once you are connected, if you do try to read an encrypted characteristic, it will fail.

Solution 2

I tried your code and it works. you need to follow this process:

  1. BluetoothAdapter.startLeScan(leCallback)

  2. In the onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) in leCallback, call btDevice.connectGatt(Context context, Boolean autoConnect, BluetoothGattCallback gattCallback);

  3. In the onConnectionStateChange(BluetoothGatt gatt, int status, int newState) in gattCallBack, check if newState is BluetoothProfile.STATE_CONNECTED, if yes, call gatt.discoverServices();

  4. In the onServicesDiscovered(BluetoothGatt gatt, int status) in gattCallBack, check if status is BluetoothGatt.GATT_SUCCESS, if yes, get the service by UUID like this: BluetoothGattService service = gatt.getService(YOUR_SERVICE_UUID);

  5. If the service is null, it means the service has not yet been discovered, you need to check again when the next service is discovered and the onServicesDiscovered will be called again.

  6. By the time all the services has been discovered, you should already got your service, unless the device does not support it.

  7. Now you can use your service in your Buzz method.

Solution 3

Also worth noting is that the BLE actions must all be serialized by you. Eg, if you made a read/write to a characteristic you need to wait for the callback before doing another. If not, this will result in an error.

Share:
14,040
crazyPixel
Author by

crazyPixel

Updated on June 15, 2022

Comments

  • crazyPixel
    crazyPixel about 2 years


    I am working on an application to coommunicate against a BLE device, currently I am trying
    to create a Service that starts with the application and auto connect to TI's CC2541
    keyfob.

    Problem is the gatt server seem to fail EVERY TIME....
    I have no clue whats wrong with my code since by google API's and some tutorials I saw
    It seems that all the pieces are in their place, yet still nothing works... =(

    Here is my service -

    package com.example.bluetoothgatt;
    
    import java.util.UUID;
    
    import android.app.Service;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothGatt;
    import android.bluetooth.BluetoothGattCharacteristic;
    import android.bluetooth.BluetoothGattService;
    import android.bluetooth.BluetoothManager;
    import android.bluetooth.BluetoothProfile;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.util.Log;
    
    public class BLE extends Service implements BluetoothAdapter.LeScanCallback {
    
        private final IBinder mBinder = new BluetoothLeBinder();
        private final static String TAG = "BLE";
        private static final String DEVICE_NAME = "Keyfobdemo";
        private BluetoothManager mBluetoothManager;
        public BluetoothGatt mConnectedGatt;
        private BluetoothAdapter mBluetoothAdapter;
        private BluetoothDevice mDevice;
        private String mDeviceAddress;
    
        private int mConnectionState = STATE_DISCONNECTED;
        private static final int STATE_DISCONNECTED = 0;
        private static final int STATE_CONNECTING = 1;
        private static final int STATE_CONNECTED = 2;
    
        /*******************************
         ******************************* 
         ****** Service Inherited ****** Methods **********
         *******************************/
    
        @Override
        public void onCreate() {
            super.onCreate();
            mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            Thread discoverDevices = new Thread(mStartRunnable);
            discoverDevices.setPriority(discoverDevices.MAX_PRIORITY);
            discoverDevices.start();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            close();
            return super.onUnbind(intent);
        }
    
        // Implements callback methods for GATT events that the app cares about.
        // For example, connection change and services discovered.
        private final BluetoothGattExecutor mExecutor = new BluetoothGattExecutor() {
    
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status,
                    int newState) {
                super.onConnectionStateChange(gatt, status, newState);
    
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    mConnectionState = STATE_CONNECTED;
                    mConnectedGatt = gatt;
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    mConnectionState = STATE_DISCONNECTED;
                    Log.i(TAG, "Disconnected from GATT server.");
                }
            }
    
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                super.onServicesDiscovered(gatt, status);
    
                if (status == BluetoothGatt.GATT_SUCCESS) {
                } else {
                    Log.w(TAG, "onServicesDiscovered received: " + status);
                }
            }
    
            @Override
            public void onCharacteristicRead(BluetoothGatt gatt,
                    BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
    
                if (status == BluetoothGatt.GATT_SUCCESS) {
                }
            }
    
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt,
                    BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicChanged(gatt, characteristic);
            }
        };
    
        /**
         * Return a reference for the current class
         */
        public class BluetoothLeBinder extends Binder {
            BLE getService() {
                return BLE.this;
            }
        }
    
        private Runnable mStartRunnable = new Runnable() {
            @Override
            public void run() {
                startScan();
            }
        };
    
        private void startScan() {
            if (mConnectionState == STATE_DISCONNECTED) {
                mBluetoothAdapter.startLeScan(this);
                mHandler.postDelayed(mStopRunnable, 2500);
            }
        }
    
        private Runnable mStopRunnable = new Runnable() {
            @Override
            public void run() {
                stopScan();
            }
        };
    
        private void stopScan() {
            mBluetoothAdapter.stopLeScan(this);
        }
    
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            /*
             * We are looking for SensorTag devices only, so validate the name that
             * each device reports before adding it to our collection
             */
            if (DEVICE_NAME.equals(device.getName())) {
                mDevice = device;
                mDeviceAddress = mDevice.getAddress();
                connect(mDeviceAddress);
                mConnectionState = STATE_CONNECTING;
                if(device.getBondState() == BluetoothDevice.BOND_BONDED) {
    
                } else if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
    
                } else if(device.getBondState() == BluetoothDevice.BOND_NONE) {
                    connect(device.getAddress());
                }
            }
        }
    
        /**
         * Connects to the GATT server hosted on the Bluetooth LE device.
         * 
         * @param address
         *            The device address of the destination device.
         * 
         * @return Return true if the connection is initiated successfully. The
         *         connection result is reported asynchronously through the
         *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
         *         callback.
         */
        public boolean connect(final String address) {
            if (mBluetoothAdapter == null || address == null) {
                Log.w(TAG,
                        "BluetoothAdapter not initialized or unspecified address.");
                return false;
            }
    
            // Previously connected device. Try to reconnect.
            if (mDeviceAddress != null && address.equals(mDeviceAddress)
                    && mConnectedGatt != null) {
                Log.d(TAG,
                        "Trying to use an existing BluetoothGatt for connection.");
                if (mConnectedGatt.connect()) {
                    mConnectionState = STATE_CONNECTING;
                    return true;
                } else {
                    return false;
                }
            }
    
            final BluetoothDevice device = mBluetoothAdapter
                    .getRemoteDevice(address);
            if (device == null) {
                Log.w(TAG, "Device not found.  Unable to connect.");
                return false;
            }
            // We want to directly connect to the device, so we are setting the
            // autoConnect
            // parameter to false.
            mConnectedGatt = device.connectGatt(this, false, mExecutor);
            Log.d(TAG, "Trying to create a new connection.");
            mDeviceAddress = address;
            mConnectionState = STATE_CONNECTING;
            return true;
        }
    
        /**
         * Disconnects an existing connection or cancel a pending connection. The
         * disconnection result is reported asynchronously through the
         * BluetoothGattCallback >>
         * onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)
         * callback.
         */
        public void disconnect() {
            if (mBluetoothAdapter == null || mConnectedGatt == null) {
                Log.w(TAG, "BluetoothAdapter not initialized");
                return;
            }
            mConnectedGatt.disconnect();
        }
    
        /**
         * After using a given BLE device, the app must call this method to ensure
         * resources are released properly.
         */
        public void close() {
            if (mConnectedGatt == null) {
                return;
            }
            mConnectedGatt.close();
            mConnectedGatt = null;
        }
    
        private final UUID IMMEDIATE_ALERT_UUID = UUID
                .fromString("00001802-0000-1000-8000-00805f9b34fb");
        private final UUID ALERT_LEVEL_UUID = UUID
                .fromString("00002a06-0000-1000-8000-00805f9b34fb");
    
        public void Buzz(BluetoothGatt gatt, int level) {
            BluetoothGattService alertService = gatt
                    .getService(IMMEDIATE_ALERT_UUID);
            if (alertService == null) {
                Log.d(TAG, "Immediate Alert service not found!");
                return;
            }
            BluetoothGattCharacteristic alertLevel = alertService
                    .getCharacteristic(ALERT_LEVEL_UUID);
            if (alertLevel == null) {
                Log.d(TAG, "Alert Level charateristic not found!");
                return;
            }
            alertLevel.setValue(level, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
            gatt.writeCharacteristic(alertLevel);
            Log.d(TAG, "Alert");
        }
    
        private final UUID BATTERY_SERVICE_UUID = UUID
                .fromString("0000180F-0000-1000-8000-00805f9b34fb");
        private final UUID BATTERY_LEVEL_UUID = UUID
                .fromString("00002a19-0000-1000-8000-00805f9b34fb");
    
        public int getbattery(BluetoothGatt mBluetoothGatt) {
    
            BluetoothGattService batteryService = mConnectedGatt
                    .getService(BATTERY_SERVICE_UUID);
            if (batteryService == null) {
                Log.d(TAG, "Battery service not found!");
                return 0;
            }
    
            BluetoothGattCharacteristic batteryLevel = batteryService
                    .getCharacteristic(BATTERY_LEVEL_UUID);
            if (batteryLevel == null) {
                Log.d(TAG, "Battery level not found!");
                return 0;
            }
            mBluetoothGatt.readCharacteristic(batteryLevel);
            return batteryLevel.getIntValue(
                    BluetoothGattCharacteristic.FORMAT_SINT8, 0);
        }
    
        /*
         * We have a Handler to process event results on the main thread
         */
        private static final int MSG_PROGRESS = 201;
        private static final int MSG_DISMISS = 202;
        private static final int MSG_CLEAR = 301;
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                BluetoothGattCharacteristic characteristic;
                switch (msg.what) {
                case MSG_PROGRESS:
                    break;
                case MSG_DISMISS:
                    break;
                case MSG_CLEAR:
                    break;
                }
            }
        };
    
        public void MakeBuzz() {
            Thread t = new Thread(new Runnable() {
    
                @Override
                public void run() {
                    mConnectedGatt = mDevice.connectGatt(getApplicationContext(),
                            true, mExecutor);
                    BluetoothGattService alertService = mConnectedGatt
                            .getService(IMMEDIATE_ALERT_UUID);
                    int x = getbattery(mConnectedGatt);
                    Buzz(mConnectedGatt, 2);
                }
            });
            t.start();
        }
    }
    

    This it the Application class -

    package com.example.bluetoothgatt;
    
    
    import android.app.Application;
    import android.content.Intent;
    
    public class ApplicationBleTest extends Application {
        // Application variables
        public final String SMOKE_TALK_PACKAGE_NAME = "com.smoketalk";
        private BluetoothLEService mBleService;
        private static int MODE_PRIVATE;
    
        /**
         * Application OnCreate event initiate the class parameters
         */
        public void onCreate() {
            super.onCreate();
            getApplicationContext().startService(new Intent(this, BLE.class));
        }
    }
    

    And this is the main activity (I am trying to make the keyfob alaram buzz on a button click)

    package com.example.bluetoothgatt;
    
    import com.example.bluetoothgatt.BluetoothLowEnergyService.BluetoothLeBinder;
    
    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    
    /**
     * Created by Dave Smith Double Encore, Inc. MainActivity
     */
    public class MainActivity extends Activity {
    
        BluetoothLowEnergyService mBluetoothService;
        boolean isBound = false;
        Button buzz;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Intent intent = new Intent(this, BluetoothLowEnergyService.class);
            bindService(intent, mBleServiceConnection, Context.BIND_AUTO_CREATE);
            buzz = (Button) findViewById(R.id.btn1);
            buzz.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    mBluetoothService.MakeBuzz();
                }
            });
        }
    
        private ServiceConnection mBleServiceConnection = new ServiceConnection() {
    
            public void onServiceConnected(ComponentName className, IBinder service) {
                BluetoothLeBinder binder = (BluetoothLeBinder) service;
                mBluetoothService = binder.getService();
                isBound = true;
            }
    
            public void onServiceDisconnected(ComponentName arg0) {
                isBound = false;
            }
    
        };
    }
    

    And the menifest file -

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.bluetoothgatt"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-feature
            android:name="android.hardware.bluetooth_le"
            android:required="true" />
    
        <uses-sdk
            android:minSdkVersion="18"
            android:targetSdkVersion="18" />
    
        <uses-permission android:name="android.permission.BLUETOOTH" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
        <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"/>
    
        <application
            android:name="com.example.bluetoothgatt.ApplicationBleTest"
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name=".MainActivity"
                android:label="SensorTag Weather" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <service android:name="com.example.bluetoothgatt.BLE" />
        </application>
    
    </manifest>
    

    and last one the layout for the main activity -

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin" >
    
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:padding="@dimen/activity_horizontal_margin"
            android:text="Android BLE Test"
            android:textSize="42sp" />
    
        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textView1"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="56dp"
            android:text="Buzz" />
    
    </RelativeLayout>
    

    ANY help will be appreciated since I rellay have no clue what goes wrong... =(