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.