Preface:

If you are interested in BLE Bluetooth, please join our discussion group:

QQ: 494309361 (Android Bluetooth development team)

With the advent of the Internet of Things era, more and more smart hardware devices have become popular, such as smart wristbands, heart rate monitors, and various smart furniture and toy products. Android 4.3(API 18) provides platform support and apis for BLE’s core functions, which apps can use to discover devices, query services, and read and write features. Compared with traditional Bluetooth, BLE is more notable for its low power consumption. This article mainly explains Android low power Bluetooth API use and bluetooth scan, connect, send data, receive data and a series of operations, and mainly introduces the BleLib Bluetooth library encapsulated by myself, which is very suitable for Bluetooth beginners, only need a line of code injection OK, and the usage is extremely simple. The following section is devoted to the use of the BleLib library.

directory

  • A detailed explanation of the native API

  • Advantages of the BleLib library

  • How do I use the library

  • Detailed analysis of the BleLib library

Without further ado, let’s look at the renderings in the Demo:

A detailed explanation of native API

In the BLE protocol, there are two roles, Periphery and Central; The periphery is the data provider, the central is the data user/processor, a central can connect multiple periphery at the same time, but a periphery can only connect one central at a time. BluetoothGatt and BluetoothGattCallback classes, which inherit from the BluetoothProfile, BluetoothGatt as a central place to use and process data. BluetoothGatt allows you to connect to devices, discoverServices and return the corresponding properties back to the BluetoothGattCallback, BluetoothGattCallback returns the state of the central and the data provided by the surrounding.

1. Bluetooth development process:

The main purpose of our Bluetooth operation is to take out the central BluetoothGatt object and carry out all the following operations, as follows:

BluetoothManager = (BluetoothManager) getSystemService(context.bluetooth_service); 2. Get BluetoothAdapt btAdapter = bluetoothManager. GetAdapter (); 3. Start scanning: btAdapter startLeScan (BluetoothAdapter. LeScanCallback); BluetoothDevice public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {BluetoothDevice public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {... . } 5. BluetoothDevice BluetoothGatt: gatt = device.connectgatt (this, true, gattCallback);Copy the code

Now you finally have the central BluetoothGatt, which has a number of BluetoothGatt methods that you could use to interact with the surrounding BluetoothGattServer through the BluetoothGattCallback.

2. General understanding of the main categories:
  • BluetoothProfile: a general specification by which data is sent and received.

  • BluetoothManager: Access the BluetoothAdapter through the BluetoothManager

    For example: BluetoothManager, BluetoothManager = (BluetoothManager) getSystemService(context.bluetooth_service);Copy the code
  • BluetoothAdapter: An Android system has only one BluetoothAdapter, which is obtained from the BluetoothManager

    BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
    Copy the code
  • BluetoothGattDescriptor: can be regarded as descriptors, description of Characteristic, including range, units of measurement, etc.

  • BluetoothGattService: a collection of services, Characteristic.

  • BluetoothGattCallback: Result returned from some operations on a device that has been connected. Here must remind, has been connected to the device can be returned, did not return carefully to see whether connected to the device.

    Private BluetoothGattCallback GattCallback = new BluetoothGattCallback() {BluetoothGattCallback = new BluetoothGattCallback() { Public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState){}; public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){ }; }; BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); BluetoothGatt gatt = device.connectGatt(this, false, mGattCallback);Copy the code
3. The main correspondence of bluetooth interaction for the 9 methods mentioned above:

(1) Notification corresponds to onCharacteristicChanged;

gatt.setCharacteristicNotification(characteristic, true);
Copy the code

This method is usually set after service discovery. The purpose of setting this method is to enable the hardware to send data to the app when data changes, and then the app calls back to the user through onCharacteristicChanged method, and the data can be retrieved from the parameters.

(2) readCharacteristic corresponding to onCharacteristicRead;

gatt.readCharacteristic(characteristic);
Copy the code

(3) writeCharacteristic corresponds to onCharacteristicWrite;

gatt.wirteCharacteristic(mCurrentcharacteristic);
Copy the code

(4) Connect bluetooth or disconnect Bluetooth corresponding to onConnectionStateChange;

bluetoothDevice.connectGatt(this, false, mGattCallback); Or gatt. Disconnect (); (remember gatt.close();)Copy the code

(5) readDescriptor corresponds to onDescriptorRead;

gatt.readDescriptor(descriptor);
Copy the code

(6) writeDescriptor corresponds to onDescriptorWrite;

gatt.writeDescriptor(descriptor);
Copy the code

ReadRemoteRssi corresponds to onReadRemoteRssi;

gatt.readRemoteRssi();
Copy the code

(8) executeReliableWrite corresponds to onReliableWriteCompleted;

 gatt.executeReliableWrite();
Copy the code

(9) discoverServices corresponds to onServicesDiscovered

gatt.discoverServices();
Copy the code
Permissions for Enabling Bluetooth:
 <uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
Copy the code

If android. Hardware. Bluetooth_le set to false, can be installed in an unsupported devices, determine whether to support bluetooth 4.0 with the following code is ok, such as:

if (! GetPackageManager ().hasSystemFeature(packagemanager.feature_bluetooth_le)) {toast.maketext (this, "Device does not support Bluetooth 4.0", Toast.LENGTH_SHORT).show(); finish(); }Copy the code

Bluetooth startup and shutdown operations:

1. Enable the Bluetooth dialog box by default

if (mBluetoothAdapter == null || ! mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }Copy the code

2, the background bluetooth, do not do any prompts, this can also be customized to open the Bluetooth dialog box

mBluetoothAdapter.enable();
Copy the code

3. Turn off bluetooth in the background

mBluetoothAdapter.disable();
Copy the code

Second, the advantages of BleLib library

  • Simplest implant (almost a line of code)

Private void initBle() {mBle = ble.options ().setLogbleExceptions (true)// Sets whether to print Bluetooth logs. In order to facilitate debugging). SetThrowBleException (true)/whether/set bluetooth exception thrown. SetAutoConnect (true) / / Settings are automatically connected. SetConnectFailedRetryCount (3) .setConnectTimeout(10 x 1000)// Set the connection timeout period (default 10 x 1000 ms). SetScanPeriod (default 12 x 1000 ms)// Set the scan period (default 10 x 1000 ms). SetUuid_service (UUID. FromString (" 0000FEe9-0000-1000-8000-00805f9b34Fb "))// UUID of the primary service .setuuid_write_cha (UUID. FromString (" d44BC439-ABFD-45A2-b575-925416129600 "))// UUID. Create (getApplicationContext());  }Copy the code
  • Simplifies the amount of code to the greatest extent

Comparison hurts, so let’s take a look at the comparison between the native API calling Bluetooth flow and this library:

For example, scanning equipment
Native API writing:
Private void scanLeDevice(final Boolean enable) {if (enable) {// Stop scanning mHandler.postDelayed(new Runnable() { @Override public void run() { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); }... } then get the scan result in the mLeScanCallback callback:  // Device scan callback. private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { ... }}); }Copy the code
Scanning in BleLib:
mBle.startScan(scanCallback); Callback result:  BleScanCallback<BleDevice> scanCallback = new BleScanCallback<BleDevice>() { @Override public void onLeScan(final BleDevice device, int rssi, byte[] scanRecord) { ... }}};Copy the code
  • Provides unique OTA upgrade interface (i.e. bluetooth hardware update interface)
This is absolutely not available in other Bluetooth libraries, see the following API library usage stepsCopy the code

How to use the library

First add the dependency to buidl.gradle (see README file in Demo for latest version) :

compile 'cn.com.superLei:blelibrary:2.5.2-beta'
Copy the code
1. Initialize Bluetooth (see DEMO for operations such as dynamically granting Bluetooth operation rights, enabling Bluetooth, and judging whether the device supports Bluetooth)
Private void initBle() {mBle = ble.options ().setLogbleExceptions (true)// Sets whether to print Bluetooth logs. SetThrowBleException (true)// Sets whether a Bluetooth exception is thrown. SetAutoConnect (true)// Sets whether to connect automatically SetConnectFailedRetryCount (3) / / set connection retries. SetConnectTimeout (10 * 1000) / / set the connection timeout value (the default 10 * 1000 ms). SetScanPeriod (12 * SetUuid_service (UUID. FromString (" 0000FEe9-0000-1000-8000-00805f9b34Fb "))// UUID of the primary service .setuuid_write_cha (UUID. FromString (" d44BC439-ABFD-45A2-b575-925416129600 "))// UUID. Create (getApplicationContext());  }Copy the code
2. Scan peripheral devices
mBle.startScan(scanCallback); BleScanCallback<BleDevice> scanCallback = new BleScanCallback<BleDevice>() {@override public void onLeScan(final)  BleDevice device, int rssi, byte[] scanRecord) { ... // Obtain the Bluetooth device object and operate according to its own needs (the same device has been filtered in the library)}};Copy the code
3. Start the connection
mBle.connect(device, connectCallback); Private BleConnCallback<BleDevice> connectCallback = new BleConnCallback<BleDevice>() {@override public void OnConnectionChanged (BleDevice device) {if (device.isConnected()) {setNotify(device); } Log.e(TAG, "onConnectionChanged: " + device.isConnected()); } @Override public void onConnectException(BleDevice device, int errorCode) { super.onConnectException(device, errorCode); Toast.maketext (bleactivity.this, "connection exception, exception status code :" + errorCode, toast.length_short).show(); }};Copy the code

The connection exception status code can be found in the project’s wiki

4. Set notification and callback
Private void setNotify(BleDevice device) {/* */ mble.startNotify (device, new BleNotiftCallback<BleDevice>() {@override public void onChanged(BleDevice device, BluetoothGattCharacteristic characteristic) { Log.e(TAG, "onChanged: "+Arrays. ToString (characteristic.getValue())); } @override public void onReady(BleDevice device) {log. e(TAG, "onReady: ready to send or read data "); } @Override public void onServicesDiscovered(BluetoothGatt gatt) { Log.e(TAG, "onServicesDiscovered is success "); } @Override public void onNotifySuccess(BluetoothGatt gatt) { Log.e(TAG, "onNotifySuccess is success "); }}); }Copy the code

When receiving onChanged (BluetoothGattCharacteristic characteristic) callback, then a bluetooth device data changed, notify the program to make changes. There are also a lot of callbacks, but if you don’t understand them, you can refer to the native API above for a detailed explanation.

5. Read the remote Rssi
mBle.readRssi(mBle.getConnetedDevices().get(0), new BleReadRssiCallback<BleDevice>() { @Override public void onReadRssiSuccess(int rssi) { super.onReadRssiSuccess(rssi); Log.e(TAG, "onReadRssiSuccess: " + rssi); Toast.makeText(BleActivity.this, "onReadRssiSuccess:"+ rssi, Toast.LENGTH_SHORT).show(); }});Copy the code
6. Actively read data
public void read(BleDevice device) { boolean result = mBle.read(device, new BleReadCallback<BleDevice>() { @Override public void onReadSuccess(BluetoothGattCharacteristic characteristic) { super.onReadSuccess(characteristic); byte[] data = characteristic.getValue(); Log.w(TAG, "onReadSuccess: " + Arrays.toString(data)); }}); if (! Result) {log. d(TAG, "failed to read data!") ); }Copy the code
7. Write data
boolean result = mBle.write(device, changeLevelInner(), new BleWriteCallback<BleDevice>() { @Override public void onWriteSuccess(BluetoothGattCharacteristic characteristic) { Toast.maketext (bleactivity.this, "Data sent successfully ", toast.length_short).show(); }}); if (! Result) {log. e(TAG, "changeLevelInner: "+" failed to send data! ); }Copy the code
8. Sending large packets (such as files)
Byte []data = toByteArray(getAssets().open(" whitechristmas.bin ")); WriteEntity (write.getConnetedDevices ().get(0), data, 20, 50, new BleWriteEntityCallback<BleDevice>() { @Override public void onWriteSuccess() { L.e("writeEntity", "onWriteSuccess");  } @Override public void onWriteFailed() { L.e("writeEntity", "onWriteFailed"); }}); } catch (IOException e) { e.printStackTrace(); }Copy the code
9. Set the BLE4.2 (MTU)
If (build.version.sdk_int >= build.version_codes.lollipop){// The second argument here is not specific. For example, you can set 500, but if the device does not support 500 bytes the maximum number will be returned mBle.setMTU(mBle.getConnetedDevices().get(0).getBleAddress(), 250, new BleMtuCallback<BleDevice>() { @Override public void onMtuChanged(BleDevice device, int mtu, int status) { super.onMtuChanged(device, mtu, status); ToastUtil. ShowToast (" maximum support MTU: "+ MTU); }}); }else {ToastUtil. ShowToast (" Device does not support MTU"); }Copy the code
10. The OTA upgrade
File file = new file (...) file file = new file (...) ; OtaManager mOtaManager = new OtaManager(bleactivity. this); boolean result = mOtaManager.startOtaUpdate(file, (BleDevice) mBle.getConnetedDevices().get(0), mBle); Log.e("OTA upgrade result :", result + "");Copy the code

4. Detailed analysis of BleLib library encapsulation

BleLib library API structure diagram (below is the structure of 1.x library, API name part is a little different from the current) :

1. Let’s take a look at the structure of the library and what each class does. The diagram below:

Ble.

This class provides almost all the methods you need, including Bluetooth scan, connect, disconnect, bluetooth current connection status, and more. It manages all the interfaces and methods for Bluetooth operation.

BleDevice:

This class describes and records bluetooth properties and status, such as Bluetooth name, Bluetooth MAC address, Bluetooth alias (that is, the modified name), and Bluetooth connection status.

BleStatus:

This class is bluetooth status class, which defines constant values of Bluetooth scanning, connection, notification enable, send, receive and other states (refer to this class for status codes of abnormal connection).

BluetoothLeService:

This class is the most important core Bluetooth processing class. It is mainly the implementation class of various methods used in Bluetooth operation and the realization of the core functions of the whole Bluetooth. Ble is the management class that provides all bluetooth operation interfaces externally.

Some details should be paid attention to. For example, most devices will repeatedly scan the same Bluetooth device, which must be filtered. For application development, product filtering must be carried out, such as filtering through broadcast packet of the device, or filtering by device name, as follows (Note: To filter according to the broadcast package provided by our products, the following picture is of our products) :

  /**
 * Verify the product broadcast parameters
 * @param data Parameter data
 * @return Whether the match
 */
public static boolean matchProduct(byte[] data) {
    if (data == null || data.length <= 0) {
        return false;
    }
    int i = 0;
    do {
        // Read packet size
        int len = data[i++] & 0xff;
        if (len > 0) {
            // Read packet data
            byte[] d = new byte[len];
            int j = 0;
            do {
                d[j++] = data[i++];
            } while (j < len);
            // Authentication Type and Length
            if (d.length > BROADCAST_SPECIFIC_PRODUCT.length && (d[0] & 0xFF) == BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA) {
                // Matching product parameters
                boolean passed = true;
                for (int x = 0; x < BROADCAST_SPECIFIC_PRODUCT.length; x++) {
                    passed = passed && d[x + 1] == BROADCAST_SPECIFIC_PRODUCT[x];
                }
                //Match successful
                if (passed) {
                    return true;
                }
            }
        }

    } while (i < data.length);
    return false;
}
Copy the code

Top priorities:Attached BleLib library GitHub address

QQ: 494309361 (Android Bluetooth development team)