Detailed usage process of Low Power Bluetooth Ble

summary

  • Central and peripheral roles
    There are two roles in ble, one is Central and the other is Peripheral. Bluetooth devices or mobile phones can be used as Central or Peripheral roles alone. The function of the Peripheral role is to provide various data for the Central role. The Central role can scan and receive multiple Peripheral role data (the devices in the Peripheral role broadcast, and the devices in the Central role scan for broadcast). The data is presented in the form of Service and Characteristic. Among them, the API of ble Central role is supported in Android 4.3, while the API of Peripheral role is supported in Android 5.0
  • Protocol, service, feature, descriptor
    A protocol (BluetoothGatt) consists of one or more services (bluetoothgattservices), a service consists of zero or more characteristics (bluetoothgattcharacteristics), and a feature can contain zero or more descriptors (bluetoothgattdescriptors). Each service, feature and descriptor has a UUID as the unique identifier. The identifier can be general, customized or randomly generated. The fixed format is 00000000-0000-0000-0000-000000000000000000 (8-4-4-4-12). Generally speaking, only the first 8 digits of the customized UUID change, and the following ones are basically fixed 0000-1000-8000-00805f9b34fb, Therefore, a user-defined UUID generally looks like this. The wildcard "0000????-0000-1000-8000-00805f9b34fb" represents four hexadecimal numbers. Each feature has its attribute and permission (Read | Write | Notify | Indicate). The feature can be read and written according to the attribute. In each Ble Bluetooth device, there are two default services as follows:

//Generic Access (Generic Attribute Profile general attribute specification GATT)
service: 00001801-0000-1000-8000-00805f9b34fb
characteristic: 00002a05-0000-1000-8000-00805f9b34fb

//Generic Attribute (Generic Access Profile GAP)
service: 00001800-0000-1000-8000-00805f9b34fb
characteristic: 00002a00-0000-1000-8000-00805f9b34fb
characteristic: 00002a01-0000-1000-8000-00805f9b34fb
characteristic: 00002aa6-0000-1000-8000-00805f9b34fb
  • Adapter, scanner:
    Every mobile phone supporting Bluetooth will have a Bluetooth adapter, which is managed by the Bluetooth manager and obtained from it. The adapter comes with a scanner, which can scan the surrounding Bluetooth devices.

Common classes in Ble

  • Bluetooth device: a Bluetooth device that represents a specific Bluetooth peripheral
  • Bluetooth Manager: Bluetooth manager, which is mainly used to obtain Bluetooth adapter and manage all Bluetooth related things
  • Bluetooth adapter: Bluetooth adapter. Every mobile phone that supports Bluetooth has a Bluetooth adapter. Generally speaking, there is only one, which can be obtained through Bluetooth manager
  • Bluetooth lescanner: the scanner in the Bluetooth adapter, which is used to scan BLE peripherals. It can be obtained through the Bluetooth adapter
  • Bluetooth GATT: general attribute protocol, which defines the basic rules of BLE communication, that is, through the agreed process of packaging data into services and features, it can be obtained by establishing a connection
  • BluetoothGattService: service, which describes a basic function of a BLE device. It is composed of zero or more feature groups and can be obtained and customized through BluetoothGatt (the existing contract service can be found on bluetooth.org)
  • BluetoothGattCallback: as a central callback class, it is used to callback various states and results of GATT communication
  • Bluetooth gattservercallback: as a peripheral callback class, it is used to callback various states and results of GATT communication
  • Bluetooth GATT characteristic: feature, which contains one or more groups of data. It is the smallest data unit in GATT communication.
  • Bluetooth gattdescriptor: feature descriptor, additional description of features, including but not limited to feature units, attributes, etc.
  • Bluetooth profile: a general specification for sending and receiving data according to this specification

Use process

Central device: judge whether Bluetooth is available - > turn on Bluetooth - > start scanning - > acquire scanned devices - > connect devices - > discover services - > acquire specified features - > write feature values
Peripherals: judge whether Bluetooth is available - > turn on Bluetooth - > create broadcast data - > send broadcast - > add service to broadcast - > obtain written data according to monitoring
The following figure is the use flow chart of central equipment source

Permission statement

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
//Access to scan results, android6 Dynamic permissions are required above 0 (these two permissions need to be declared in classic Bluetooth scanning)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
//perhaps
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 //When android:required is true, the app can only be forced to run on devices that support BLE. When it is false, it can run on all devices, but some methods need to be detected manually, otherwise there may be hidden bugs 
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>

Ble availability judgment

Not all devices support BLE. At first, it is necessary to determine whether the device supports BLE and whether Bluetooth is turned on.

  • Android system is only supported after 4.3, which is a prerequisite

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){
    ... ...
}
  • Determine whether Bluetooth is available

//Bluetooth Adapter is directly used when API17 is less than or equal to Getdefaultadapter() to get the Adapter
private BluetoothAdapter getAdapter(){
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = mBluetoothManager.getAdapter();
        } else {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
    return mBluetoothAdapter;
}
if(mBluetoothAdapter == null){
    //Bluetooth not available
}

//Ble not available
private boolean checkIfSupportBle(){
    return getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}

Bluetooth on

//It takes a short time to turn on the Bluetooth function, so you can't turn on the Bluetooth and immediately perform other operations. At this time, the Bluetooth is not actually turned on, and an exception occurs. Therefore, the subsequent operations should be handled in the Bluetooth status broadcast
private void enableBluetooth(){
    if (mBluetoothAdapter == null && !mBluetoothAdapter.isEnabled()) {
        //Use the system pop-up to start Bluetooth, REQUEST_ENABLE_BT is a custom Bluetooth enable request code
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

        //Or turn it on silently
        bluetoothAdapter.enable();
    }
}

//Silent shutdown
bluetoothAdapter.disable();

Bluetooth switch broadcast More Bluetooth related broadcasts

//Broadcast Action
BluetoothAdapter.ACTION_STATE_CHANGED

//Four status values    
BluetoothAdapter.STATE_ON  //Turned on
BluetoothAdapter.STATE_OFF //Closed
BluetoothAdapter.STATE_TURNING_ON //Opening
BluetoothAdapter.STATE_TURNING_OFF //Closing

//Acquisition of current status value
int curState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON); 

Scan specific usage process

  • Classic universal way

//This process lasts about 10 seconds. When the Bluetooth device is scanned, a broadcast will be sent out. As long as you register to receive the broadcast where necessary, you can obtain the scanning results. This method can scan all Bluetooth devices, including BLE, but it seems that different mobile phones have different experiences.
private void startDiscover(){ 
    mBluetoothAdapter.startDiscover();
} 

//Register this broadcast to listen to Bluetooth device ACTION_ Found to receive system messages and obtain scanning results 
private class DeviceReceiver extends BroadcastReceiver { 
  @Override 
  public void onReceive(Context context, Intent intent) { 
    String action = intent.getAction(); 
    if(BluetoothDevice.ACTION_FOUND.equals(action)){ 
      //This is the Bluetooth device obtained.
      BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 
      mDevices.add(device ); 
    } 
  } 
}
  • Ble scanning mode

//Ble Bluetooth scanning mode has changed since 5.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
    BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
    //Filter settings
    ArrayList<ScanFilter> filters = new ArrayList<>();
    ScanFilter.Builder filterBuilder = new ScanFilter.Builder();
    filters.add(filterBuilder);

    //Scan result callback settings
    ScanCallback scanCallback = new ScanCallback(){        
        public void onScanResult(int callbackType, ScanResult result){
            //scanResult.getDevice() get device
        }
    };

    //Scan parameter setting
    ScanSettings.Builder settingsBuilder = new ScanSettings.Builder();
    //This scanning mode takes up a lot of resources. It is recommended to use this mode when the application is in the foreground
    settingsBuilder.setScanMode(SCAN_MODE_LOW_LATENCY);
    // Start scanning. The third parameter is shown below. This operation is asynchronous, but it will consume a lot of resources. Generally, the scanning time is 12 seconds. It is recommended to cancel scanning after finding the required equipment
    bluetoothLeScanner.startScan(scanCallback, filter, settingsBuilder.build());
}else{
    //Scan result callback settings  
    BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback(){
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord){}
    };
    //The method has been declared obsolete 
    adapter.startLeScan(leScanCallback)
}
  • Stop scanning

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
   bluetoothLeScanner.stopScan(mScanCallback);
}else{
    bluetoothAdapter.stopLeScan(mLeScanCallback);
}

Equipment information acquisition

  • Obtain Bluetooth device according to Bluetooth address

BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
  • Bound device acquisition

private void getBoundDevices(){
    Set<BluetoothDevice> boundDevices = mBluetoothAdapter.getBondedDevices();
    for(BluetoothDevice device : boundDevices){
        //Perform other operations on the device, such as connection.
    }
}
  • Device details acquisition

private void showDetailOfDevice(){
    //Get the device name. Multiple devices can have the same name.
    String deviceName = bluetoothDevice.getName();
    //Get the physical address of the device. A device can only have one physical address. Each device has the physical address of each device. It cannot be the same.
    String deviceMacAddress =bluetoothDevice.getAddress();
    //Binding device
   bluetoothDevice.createBond();
}

connect

  • Establish connection

//The Bluetooth device for scanning callback is used to establish GATT connection. Parameter 2 controls whether to automatically connect. When it is true, the device will automatically connect when it enters the central range. When it is false, it is an active connection. It is generally recommended to use active connection because it is faster.
BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback);

//If you have connected but disconnected, you need to reconnect
bluetoothGatt.connect();
  • Acquisition of connection status

//The connection state change will be recalled in the onConnectionStateChange method of BluetoothGattCallback, and you can also actively query at any time
int state = bluetoothManager.getConnectionState(bluetoothDevice, BluetoothGatt.GATT);
  • Discovery service

//After the connection is successfully established, the discovery service is performed. onServicesDiscovered is the callback of the discovery service
bluetoothGatt.discoverServer();
  • Characteristic change

//This method is generally set after discovering the service. The purpose of setting this method is to make the hardware send data to app when the feature data changes, and app will call back to the user through onCharacteristicChanged method, and the callback data can be obtained from the parameters
bluetoothGatt.setCharacteristicNotification(characteristic, true);
  • Connection data callback (on non UI threads)

//mBluetoothGattCallback is the handler of all Bluetooth data callback, and it is also the most core part of the whole Bluetooth operation. There are many methods in it, but not all of them need to be used in development. They are listed here for partial analysis. You can rewrite which method you need. If you don't need it, you can directly remove it
private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        //When the connection state between the equipment and the center changes, the following is the judgment of connected
        if (status == BluetoothGatt.GATT_SUCCESS && newState== BluetoothProfile.STATE_CONNECTED){
            ...
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        //When a device service is found, it will call back here. The following is to traverse all the found services
        if (status == BluetoothGatt.GATT_SUCCESS) {
            //gatt.getServices() can get all the services of the peripheral
            for (BluetoothGattService service : gatt.getServices()) {
                //Every time we find a service, we traverse the features contained in the service again, service Getcharacteristics () can get all the characteristics contained in the current service
                for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
                    //You can usually put the found features into a list for subsequent operations. If you want to know which descriptors each feature contains, it is very simple to use a loop to traverse the getDescriptor() method of each feature.
                    mCharacteristics.add(characteristic);
                    //Obtain the UUID, or judge whether it is the service / feature you need according to the UUID
                    Log.i("", characteristic.getUuid().toString());//UUID of the print feature.
                }
            }
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        //Call back here after reading the feature.
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        //Call back here after writing the feature, status = = Bluetooth GATT GATT_ Success indicates that the write was successful
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        //Call back here when the characteristic (value) changes.
    }

    @Override
    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        //Call back here after reading the descriptor.
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        //Call back here after writing descriptor
    }

    @Override
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
        //I haven't used it yet.
    }

    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        //Rssi indicates the signal strength between the equipment and the center. It will be recalled here when there is a change.
    }

    @Override
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
        //I haven't used it yet.
    }
};

data transmission

  • When the characteristic value is written, onCharacteristicWrite is called back

BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
characteristic.setValue(writeString); 
//Set reply method
bluetoothGattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
  • Write descriptor

bluetoothGattDescriptor.setValue(writeContent.getBytes());
bluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
  • Read characteristic value

bluetoothGatt.readCharacteristic(characteristic);

Peripheral device implementation (connected person / server)

  • Broadcast settings

private AdvertiseSettings buildAdvertiseSettings() {
    AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder()
            //Set the broadcast mode: low power consumption, balanced, low delay, and the broadcast interval becomes shorter and shorter in turn
            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
            //Set whether you can connect. Generally, you can't connect broadcast applications on iBeacon devices
            .setConnectable(true)
            //Set the maximum broadcast time, which is LIMITED_ADVERTISING_MAX_MILLIS(180 seconds)
            .setTimeout(10*1000)
            //Set the signal strength of the broadcast_ TX_ POWER_ ULTRA_ LOW, ADVERTISE_ TX_ POWER_ LOW,
            //ADVERTISE_ TX_ POWER_ MEDIUM, ADVERTISE_ TX_ POWER_ The high signal strength increases in turn
            .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);

    return builder.build();
}
  • External broadcast data (data limit: 31 Bytes)

private AdvertiseData buildAdvertiseData() {
    AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
            //Add the manufacturer information. The first parameter is the manufacturer ID (if less than two bytes, 0 will be automatically added, for example, 0x34 here, and the actual data is 34,00)
            //Generally, no setting is required, otherwise it will not be scanned by other devices
            .addManufacturerData(0x34, new byte[]{0x56})
            //Add a service to broadcast, that is, broadcast the services owned by the device
            .addServiceData(...);
            //Whether to broadcast signal strength
            .setIncludeTxPowerLevel(true)
            //Broadcast device name
            .setIncludeDeviceName(true);

    return dataBuilder.build();
}
  • External broadcast (i.e. allow to be scanned)

private void advertise() {
 BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
    BluetoothAdapter mAdapter = bluetoothManager.getAdapter(); 
    AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            //The broadcast is successful. It is recommended to start the service here
        }
        @Override
        public void onStartFailure(int errorCode) {
             //Broadcast failed
        }
    };
    BluetoothLeAdvertiser mAdvertiser = mAdapter.getBluetoothLeAdvertiser();
    mAdvertiser.startAdvertising(buildAdvertiseSettings(), buildAdvertiseData(), mAdvertiseCallback);
}
  • Open service

//Declare the UUID of the service to be broadcast and the UUID of the feature. Be careful not to occupy the default UUID of the Bluetooth device
UUID UUID_SERVICE = UUID.fromString("00001354-0000-1000-8000-00805f9b34fb");
UUID UUID_CHARACTERISTIC = UUID.fromString("00001355-0000-1000-8000-00805f9b34fb");
UUID UUID_DESCRIPTOR = UUID.fromString("00001356-0000-1000-8000-00805f9b34fb");

//Peripheral device status and data callback, see the following for details
BluetoothGattServerCallback serverCallback = new BluetoothGattServerCallback() {...};
//GATT agreement services
BluetoothGattServer server = bluetoothManager.openGattServer(this, serverCallback);

//Create a service
BluetoothGattService service = new BluetoothGattService(UUID_SERVICE,
        BluetoothGattService.SERVICE_TYPE_PRIMARY);

//To create a feature, first, this characteristic attribute satisfies the Bluetooth gattcharacteristic PROPERTY_ Write or bluetoothgattcharacteristic PROPERTY_ WRITY_ NO_ RESPONSE,
//If the property does not contain both, the writeCharacteristic() function directly returns false and does nothing. Secondly, this characteristic permission should meet
//BluetoothGattCharacteristic.PERMISSION_WRITE, otherwise onCharacteristicWrite() callback receives GATT_WRITE_NOT_PERMITTED response
//If you need to be able to read and write, you can refer to the following writing method
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(Constants.UUID_CHARACTERISTIC,
        BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
        BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);

//Create a descriptor
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR,
        BluetoothGattDescriptor.PERMISSION_READ);

//Add descriptors to a feature. A feature can contain 0 more than one descriptor
characteristic.addDescriptor(descriptor);
//Add features to a service. A service can contain 1 to more features
service.addCharacteristic(characteristic);
server.addService(service);
  • BluetoothGattServerCallback

public abstract class BluetoothGattServerCallback {
    public BluetoothGattServerCallback() {
    }

    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
        //Connection status changed
    }

    public void onServiceAdded(int status, BluetoothGattService service) {
        //Add service
    }

    public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
        //Read feature
    }

    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        //The written data, where device is the written device, value is the written value, and responseNeeded refers to whether recovery is required. If recovery is required, call gattserver The sendresponse() method replies
    }

    public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
        //Read descriptor
    }

    public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        //Written descriptor
    }

    public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
    }

    @Override
    public void onNotificationSent(BluetoothDevice device, int status) {
    }

    @Override
    public void onMtuChanged(BluetoothDevice device, int mtu) {
    }

    @Override
    public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
    }

    @Override
    public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
    }
}
  • Broadcast data format
    Broadcast Data (or scan response Data) is composed of ad structures one by one. For other Data less than 31 bytes, it is filled with 0; Each AD Structure consists of two parts: 1 byte length information (length of Data) and the remaining Data information;
    The Data information is composed of two parts: AD Type indicates the type of the AD Structure and specific AD Data.

     

     

    For example:

02 01 06 03 03 aa fe 17 16 aa fe 00 -10 00 01 02 03 04 05 06 07 08 09 0a 0b 0e 0f 00 00 00 00

02 01 06 is an AD Structure: the length of data is 02; Data is 01 06; AD Type is 01 (Flags); AD Data is 06, indicating that General Discoverable Mode is supported and BR/EDR is not supported.
03 aa fe is an AD Structure: the length of data is 03; Data is 03 aa fe; AD Type is 03 (Service UUID of 16 bits); AD Data is aa fe, which is the Service UUID of Eddystone profile. AD Type query

be careful

  • The data broadcast by peripheral devices can be up to 31 byte s
  • Write features can be written up to 20 byte s at a time
  • The single scanning time should not be too long (10 ~ 15 seconds recommended)
  • The callback of Bluetooth gattcallback is on a non UI thread
  • Each device can use a limited number of Gatt connections, so you should turn off gattbluetoothgatt when you disconnect Close() and reconnect
  • After the Bluetooth of the peripheral device is turned off, the onConnectionStateChange of the Bluetooth gattcallback of the central device will call back the status code status = 19 and newState = 0 (disconnected)
  • After the Bluetooth of the peripheral device is turned off, the central device will connect again. The onConnectionStateChange of the Bluetooth gattcallback will call back the status code 133, and call the Bluetooth gatt of the central device after the Bluetooth of the peripheral device is turned on Connect() cannot reconnect. It is still a 133 error, so it is recommended to close gatt and free resources for the 133 error

Source address



Author: Hao Hao
Link: https://www.jianshu.com/p/3d4bfdc72384
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Keywords: Android bluetooth

Added by mol on Thu, 20 Jan 2022 23:06:41 +0200