Recently I worked on a Bluetooth project in my company. I worked on a smart ring project just before I started out. At that time, the Bluetooth module was written by the head of the development team. What I saw was half-understood. Many things I don't know now seem to be new. Write them down.
Bluetooth 4.0(BLE) is supported above Android 4.3(API 18), and BLE is characterized by lower power consumption than traditional Bluetooth.It's a big turning point in the history of Android Bluetooth
After this development, I have only four steps to use BLE:
- Search for Bluetooth Devices
- Connect/disconnect devices
- Set up read/write/notification channels for services
- Operation Write, Read/Accept Notification
Search for Bluetooth Devices
-
First of all, get the Bluetooth rights of your mobile phone.
Add the following code to the application manifest file to declare Bluetooth rights.
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
However, students who are doing development on Android 6.0 or above need to be aware that they need to add location privileges on Android 6.0 or they will not be able to search for any devices.You also need to get permissions dynamically (dynamic acquisition is not detailed)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2. Determine if your mobile phone supports Bluetooth and if Bluetooth is turned on, or skip to turn on your Bluetooth device
BluetoothAdapter=mBluetoothAdapte=BluetoothAdapter.getDefa ultAdapter();
if (mBluetoothAdapter == null) {
"This device does not support Bluetooth transfer function!"
}
else {
"This device supports Bluetooth transmission!",
if (!mBluetoothAdapter.isEnabled())//Judge whether to open or skip to open
{
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
enableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
1). The Bluetooth Adapter represents the local Bluetooth device. Get the local Bluetooth device with getDefaultAdapter(). If the return value is empty, there is no Bluetooth device, otherwise there is a Bluetooth device.
2). Use isEnabled() method to determine if the Bluetooth device is turned on. If the return value is false, you need to call startActivityForResult(enableIntent, REQUEST_ENABLE_BT) again; method to turn on the Bluetooth device
3. Scanning equipment
Scanning and stopping mainly by getting the local Bluetooth Adapter, and returning the scanned results through Bluetooth Adapter's callback
Callback
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if (device != null){
Log.e("device.getName()",""+device.getName());//Device Name
Log.e("device.getName()",""+device.getAddress());//Device Address
};
Scan/Stop Scan
/**
* Search for Bluetooth or stop searching
* @param enable
*/
@SuppressLint("NewApi")
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);//SCAN_PERIOD=scan time
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
Connect devices and disconnect
Normally, I put three steps: connecting/disconnecting devices, setting up read/write/notification channels for services to operate read/write/accept notifications, and operating read/write/accept notifications into services so that I can operate anywhere without interface.
-
Get the Bluetooth Device in the Bluetooth Adapter and connect it with the previously scanned device address.Return results through the BluetoothGatt callback, through which all operations after this critical BluetoothGatt return results
Get Bluetooth Adapter
/**
* Initialize Bluetooth Service
* @return
*/
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter
// through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
Get Bluetooth Device and connect
/**
* Request Connection
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.e(TAG,"BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect. (Previously connected device).Try reconnecting)
if (mBluetoothDeviceAddress != null&& address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
if (mBluetoothGatt.connect()) {
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;
}
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
return true;
}
BluetoothGatt class
Bluetooth Gatt is the most we use. It's like a big channel for Android phones to communicate with BLE terminal devices. Only with this channel can we communicate with each other if all the data read and write, notification and other channels are in this channel.
BluetoothGatt class
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) { //Connection Successful
mBluetoothGatt.discoverServices(); //Find the service on the device when the connection is successful (this method is important and must be adjusted)
BleConnetInter.sendCallBack(1);
Log.e(TAG,"Ble connect suess");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //connection failed
BleConnetInter.sendCallBack(3);
Log.e(TAG,"Ble connect fail");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG,"onServicesDiscovered sucess");
displayGattServices();//Discover service callbacks, set service, read, write, notification channels here
} else {
}
Log.e(TAG, "onServicesDiscovered received: " + status);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
System.out.println("onCharacteristicRead");
if (status == BluetoothGatt.GATT_SUCCESS) {
//Read Channel Return Data
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
//Write Channel
}
@SuppressLint("LongLogTag")
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
//
byte[] data = characteristic.getValue();//Accept notification data returned by terminal
if (data != null && data.length>0){
ParaseBleData(data);//Parse data
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
System.out.println("rssi = " + rssi);
}
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
System.out.println("--------write success----- status:" + status);
};
};
Set up a read and write notification channel for the service
First understand these keywords, a BLE terminal can contain multiple services, a Service can contain multiple Characteristic, and each Characteristic can be a channel.Characteristic is very important, that is, what we call communication channel, is the key to exchange data between mobile phone and BLE terminal. Reading and setting data are related properties of Characteristic operation.
Each Service has a unique UUID, and each Characteristic has a unique UUID. We use the UUID of the BLE terminal provided by the hardware engineer to determine which Services and Characteristic we need
When the BluetoothGatt class on the device room is connected back to the notification that the connection was successful, then the method BluetoothGatt.discoverServices() for discovering the Service is dropped; (as mentioned in the BluetoothGatt example above), there are callbacks in the BluetoothGatt after discovering the Service, and the Service and Characteristic are retrieved and parsed in the callback, so we need to determine what we need based on the UUID providedTo Service, Characteristic
/**
*Analysis Services List
*/
public void displayGattServices(){
List<BluetoothGattService> listServices = mBluetoothGatt.getServices()
for (BluetoothGattService service : listServices){
if (service != null){
Log.e(TAG,""+service.getUuid());//Get the UUID of the service
if (service.getUuid().toString().equals(HANDBAND_SERVER)){//UUID comparison
ArrayList<BluetoothGattCharacteristic> mGattCharacteristics =
(ArrayList<BluetoothGattCharacteristic>) service.getCharacteristics();//Get Characteristics
for (BluetoothGattCharacteristic characteristics : mGattCharacteristics){
if (characteristics != null){
if (HANDBAND_WRITE.equals(characteristics.getUuid().toString())){//Features
// Log.e(TAG,"if characteristicsUUID:"+mGattCharacteristics.size());
writeBluetoothGattCharacteristic = characteristics;
}else if (HANDBAND_NOTIFICATION.equals(characteristics.getUuid().toString())){//Compare notification channels
notifyBluetoothGattCharacteristic = characteristics;
//setCharacteristicNotification(true);
setNotify(notifyBluetoothGattCharacteristic);//Set Notification Channel
setEnableNotify(notifyBluetoothGattCharacteristic, true);////Set Notification Channel
}
}
Log.e(TAG,"for characteristicsUUID:"+characteristics.getUuid());//service.getUuid();
}
}
}
}
}
Set Notification Channel
// Set Notifiable
public boolean setNotify(BluetoothGattCharacteristic data_char) {
if (!mBluetoothAdapter.isEnabled()) { // Bluetooth is not on
return false;
}
if (data_char == null) {
return false;
}
if (0 != (data_char.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY)) { // See if there is a notifiable property
mBluetoothGatt.setCharacteristicNotification(data_char, true);
BluetoothGattDescriptor descriptor = data_char.getDescriptor(UUID
.fromString(CLIENT_CHARACTERISTIC_CONFIG));
//
descriptor
.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}else if (0 != (data_char.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)){
mBluetoothGatt.setCharacteristicNotification(data_char, true);
BluetoothGattDescriptor descriptor = data_char.getDescriptor(UUID
.fromString(CLIENT_CHARACTERISTIC_CONFIG));
//
descriptor
.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
return true;
}
// Set Allow Notification
public boolean setEnableNotify(BluetoothGattCharacteristic data_char,
boolean enable) {
if (!mBluetoothAdapter.isEnabled()) { // Bluetooth is not on
return false;
}
if (data_char == null) {
return false;
}
mBluetoothGatt.setCharacteristicNotification(data_char, enable);
return true;
}
/**
* Set Readable
* @param characteristic
*/
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
4. Operation Read/Write/Accept Notification
1. Write operations
In Discovery and Analysis Services we get the written Characteristic and write to the BLE terminal device through Characteristic
/**
* Write operation
* @param
*/
public synchronized void wirteCharacteristic(byte[] data) {
if (mBluetoothAdapter == null || mBluetoothGatt == null
|| writeBluetoothGattCharacteristic == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
writeBluetoothGattCharacteristic.setValue(data);
Log.e("wirteCharacteristic",""+writeBluetoothGattCharacteristic.getValue());
String dataStr = "";
for(int i =0;i<data.length;i++){
dataStr =dataStr+" , "+data[i];
}
Log.e(TAG,"data:"+dataStr);
mBluetoothGatt.writeCharacteristic(writeBluetoothGattCharacteristic);
}
2. Messages that read/receive notifications are returned in the BluetoothGatt class.
By the time the basic operation of this Ble is completed, Bluetooth has said that it is also a channel for information transmission. As long as you have mastered these four steps, you can operate on it, either by scanning, connecting, writing instructions to the terminal device and accepting terminal instructions.
I also share with my family that I encountered a pit in the development process. One pit I encountered was that the instruction agreement provided by the hardware engineer was incorrect, because on the hardware side we purchased other people's finished products, the agreement they gave me was not updated, I sent a set information instruction to the terminal according to the agreement, the terminal returned successfully, and the result found by querying the instructions was also the conclusion of setting up with me.If the settings are the same, but the settings are invalid, there is no problem comparing the protocol with the instructions, because this setup involves several steps to test the steps back and forth, and ultimately failed, because the device is purchased as a finished product and can not be connected, but they have finished product APKs, through their APk settings, no problem, it is doubtful whether there is a problem with the protocol, but it is not sure, to purchaseThe finished product is also cumbersome to communicate with. Finally, list the instructions issued and returned one by one, make a document for them to compare and finally find problems, and the agreement has changed without being updated.Among them, I don't want to ask others to use the package tool at last. Some of the articles about Bluetooth package recommended by my friends have not been studied yet, so I can write them out and share them.