前言:
藍(lán)牙是一種短距離的無線通信技術(shù),可以實現(xiàn)固定設(shè)備啊楚、移動設(shè)備之間的數(shù)據(jù)交換参袱。一般將藍(lán)牙分為兩大類电谣,藍(lán)牙3.0規(guī)范之前的版本稱為傳統(tǒng)藍(lán)牙,藍(lán)牙4.0規(guī)范之后的版本稱為低功耗藍(lán)牙抹蚀,也就是常說的BLE(Bluetooth Low Energy)剿牺。
BLE分主模式和從模式(外圍模式),Android從API 18(4.0)開始支持BLE功能的主模式环壤,但是從API 21(5.0)開始才提供了對外圍設(shè)備相關(guān)的API支持晒来。此文就簡單分享一下BLE外圍模式(peripheral)的開發(fā);
外圍模式的工作流程:從機開啟藍(lán)牙 ——>從機初始化參數(shù)并發(fā)送帶有service和characteristic的廣播——>主機開啟藍(lán)牙掃描BLE設(shè)備-->主機和從機通過約定好的service下的characteristic進(jìn)行通訊郑现;
BLE權(quán)限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
上代碼:
簡單粗暴點湃崩,直接上代碼:
1:創(chuàng)建一個BlePeripheralCallback回調(diào)類:
package com.roy.www.ble_peripheral_api;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.le.AdvertiseSettings;
import com.roy.www.ble_peripheral_api.protocol.BleCommPack;
/**
* Created by Roy.lee
* On 2021/6/27
* Email: 631934797@qq.com
* Description:
*/
public interface BlePeripheralCallback {
/**
* Connection status callback
*
* @param device
* @param status
* @param newState
*/
void onConnectionStateChange(BluetoothDevice device, int status, int newState);
/**
* Advertise success callback
*
* @param settingsInEffect
*/
void onStartAbSuccess(AdvertiseSettings settingsInEffect);
/**
* Advertise failure callback
*
* @param errorCode
*/
void onStartAbFailure(int errorCode);
/**
* Receive new data callback
*
* @param device
* @param requestId
* @param characteristic
*/
void onReceiveNewBytes(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, byte[] reqeustBytes);
/**
* Send data status callback
* @param status
* @param bytes
*/
void onWriteBytesAndStatus(boolean status, byte[] bytes);
}
2:封裝一個BlePeripheralHelper工具類:
package com.roy.www.ble_peripheral_api;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.ParcelUuid;
import android.os.SystemClock;
import android.util.Log;
import com.roy.www.ble_peripheral_api.protocol.BleCommPack;
import com.roy.www.ble_peripheral_api.protocol.BleCommand;
import com.roy.www.ble_peripheral_api.utils.CRC16;
import com.roy.www.ble_peripheral_api.utils.HexDump;
import java.util.Arrays;
import java.util.UUID;
/**
* Created by Roy.lee
* On 2021/6/27
* Email: 631934797@qq.com
* Description:
*/
public class BlePeripheralHelper {
private static final String TAG = "@@@ ===> " + BlePeripheralHelper.class.getSimpleName();
// 這里的參數(shù)可自行定義
private static String BLE_NAME = "SmartBox";
private static final UUID UUID_SERVER = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
private static final UUID UUID_CHARREAD = UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
private static final UUID UUID_CHARWRITE = UUID.fromString("0000fff2-0000-1000-8000-00805f9b34fb");
private static final UUID UUID_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private BluetoothGattCharacteristic mCharacteristicRead;
//BluetoothHelper
private static BlePeripheralHelper mBlePeripheralHelper;
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private static Context mContext;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private BluetoothGattServer mBluetoothGattServer;
private BlePeripheralCallback mBlePeripheralCallback;
private BlePeripheralHelper() {
initPeripheral(mContext);
}
/**
* 獲取BleController實例對象
* @return
*/
public synchronized static BlePeripheralHelper getInstance(Context context) {
mContext = context;
if (null == mBlePeripheralHelper) {
mBlePeripheralHelper = new BlePeripheralHelper();
}
return mBlePeripheralHelper;
}
/**
* 初始化BLE相關(guān)參數(shù)
*
* @param context
*/
private void initPeripheral(Context context) {
// 初始化mBluetoothManager
mBluetoothManager = getBleManager(mContext);
// 初始化mBluetoothAdapter
mBluetoothAdapter = getBluetoothAdapter();
if (isBleEnabled()){
mBluetoothLeAdvertiser = getBluetoothLeAdvertiser();
}else {
if (setBleEnabled(true)){
SystemClock.sleep(1000);
mBluetoothLeAdvertiser = getBluetoothLeAdvertiser();
}
}
if (mBluetoothLeAdvertiser == null){
Log.e(TAG, "== The device not support peripheral ==");
}
// 注冊BLE的狀態(tài)變化廣播
context.registerReceiver(mBluetoothReceiver, makeGattUpdateIntentFilter());
}
/**
* 初始化BLE廣播
*/
public void initGATTServer() {
initGATTServer(null);
}
/**
* 初始化BLE廣播并設(shè)置BEL_NAME
* @param bleName
*/
public void initGATTServer(String bleName) {
if (bleName != null && !bleName.isEmpty())
BLE_NAME = bleName;
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setConnectable(true) //是否被連接
.setTimeout(0) //超時時間
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) //廣播模式
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) //發(fā)射功率
.build();
AdvertiseData advertiseData = new AdvertiseData.Builder()
.setIncludeDeviceName(true) //是否在廣播中攜帶設(shè)備的名稱
.setIncludeTxPowerLevel(true) //是否在廣播中攜帶信號強度
.build();
AdvertiseData scanResponseData = new AdvertiseData.Builder()
.addServiceUuid(new ParcelUuid(UUID_SERVER))
.setIncludeTxPowerLevel(true)
.build();
//設(shè)置BLE設(shè)備的名稱
mBluetoothAdapter.setName(BLE_NAME);
/**
* 開啟廣播的結(jié)果callback
*/
AdvertiseCallback mAdCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.d(TAG, "BLE advertisement added successfully");
// TODO 初始化服務(wù)
initServices();
if (mBlePeripheralCallback != null)
mBlePeripheralCallback.onStartAbSuccess(settingsInEffect);
}
@Override
public void onStartFailure(int errorCode) {
Log.e(TAG, "Failed to add BLE advertisement, reason: " + errorCode);
if (mBlePeripheralCallback != null)
mBlePeripheralCallback.onStartAbFailure(errorCode);
}
};
//開啟廣播
if (mBluetoothLeAdvertiser != null)
mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponseData, mAdCallback);
}
/**
* 初始化廣播服務(wù)參數(shù)
*/
private void initServices() {
mBluetoothGattServer = getBluetoothGattServer(mBluetoothGattServerCallback);
BluetoothGattService service = new BluetoothGattService(UUID_SERVER, BluetoothGattService.SERVICE_TYPE_PRIMARY);
//add a read characteristic.
mCharacteristicRead = new BluetoothGattCharacteristic(UUID_CHARREAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ);
//add a descriptor
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR, BluetoothGattCharacteristic.PERMISSION_WRITE);
mCharacteristicRead.addDescriptor(descriptor);
service.addCharacteristic(mCharacteristicRead);
//add a write characteristic.
BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHARWRITE,
BluetoothGattCharacteristic.PROPERTY_WRITE |
BluetoothGattCharacteristic.PROPERTY_READ |
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_WRITE);
service.addCharacteristic(characteristicWrite);
mBluetoothGattServer.addService(service);
Log.e(TAG, "2. initServices ok");
}
/**
* 獲取BluetoothLeAdvertiser
*
*/
private BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
return mBluetoothAdapter == null ? null : mBluetoothAdapter.getBluetoothLeAdvertiser();
}
/**
* 獲取BluetoothGattServer
*
*/
private BluetoothGattServer getBluetoothGattServer(BluetoothGattServerCallback bluetoothGattServerCallback){
return mBluetoothManager == null ? null : mBluetoothManager.openGattServer(mContext, bluetoothGattServerCallback);
}
/**
* 獲取BluetoothManager
*
* @param context
* @return
*/
private BluetoothManager getBleManager(Context context) {
return context == null ? null : (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
}
/**
* 獲取BluetoothAdapter
*
* @return
*/
private BluetoothAdapter getBluetoothAdapter(){
return mBluetoothManager == null ? null : mBluetoothManager.getAdapter();
}
/**
*
* 開啟/關(guān)閉BLE
*
* @param enabled
* @return
*/
public boolean setBleEnabled(boolean enabled){
if (enabled){
return mBluetoothAdapter == null ? false : mBluetoothAdapter.enable();
}else {
return mBluetoothAdapter == null ? false : mBluetoothAdapter.disable();
}
}
/**
* 獲取BLE狀態(tài)
* @return
*/
public boolean isBleEnabled(){
return mBluetoothAdapter == null ? false : mBluetoothAdapter.isEnabled();
}
// ========================================================BluetoothGattServerCallback=========================================
/**
* 服務(wù)事件的回調(diào)
*/
private BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() {
/**
* 1.連接狀態(tài)發(fā)生變化時
* @param device
* @param status
* @param newState
*/
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
Log.w(TAG,"onConnectionStateChange [ status : " + status + " | newState : " + newState + "]");
Log.e(TAG, String.format("1.onConnectionStateChange:device name = %s, address = %s", device.getName(), device.getAddress()));
Log.e(TAG, String.format("1.onConnectionStateChange:status = %s, newState =%s ", status, newState));
super.onConnectionStateChange(device, status, newState);
if (mBlePeripheralCallback != null)
mBlePeripheralCallback.onConnectionStateChange(device, status, newState);
}
@Override
public void onServiceAdded(int status, BluetoothGattService service) {
super.onServiceAdded(status, service);
Log.e(TAG, String.format("onServiceAdded:status = %s", status));
}
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
Log.e(TAG, String.format("onCharacteristicReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
Log.e(TAG, String.format("onCharacteristicReadRequest:requestId = %s, offset = %s", requestId, offset));
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
// super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
}
/**
* 3. onCharacteristicWriteRequest,接收具體的字節(jié)
* @param device
* @param requestId
* @param characteristic
* @param preparedWrite
* @param responseNeeded
* @param offset
* @param requestBytes
*/
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {
Log.e(TAG, String.format("3.onCharacteristicWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
Log.e(TAG, String.format("3.onCharacteristicWriteRequest:requestId = %s, preparedWrite=%s, responseNeeded=%s, offset=%s, value=%s", requestId, preparedWrite, responseNeeded, offset, HexDump.byteTo16String(requestBytes)));
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, requestBytes);
// TODO 4.處理響應(yīng)內(nèi)容
onResponseToClient( device, requestId, characteristic,requestBytes);
}
/**
* 2.描述被寫入時,在這里執(zhí)行 bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS... 收接箫,觸發(fā) onCharacteristicWriteRequest
* @param device
* @param requestId
* @param descriptor
* @param preparedWrite
* @param responseNeeded
* @param offset
* @param value
*/
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
Log.e(TAG, String.format("2.onDescriptorWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
Log.e(TAG, String.format("2.onDescriptorWriteRequest:requestId = %s, preparedWrite = %s, responseNeeded = %s, offset = %s, value = %s,", requestId, preparedWrite, responseNeeded, offset, HexDump.byteTo16String(value)));
// now tell the connected device that this was all successfull
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
/**
* 5.特征被讀取攒读。當(dāng)回復(fù)響應(yīng)成功后,客戶端會讀取然后觸發(fā)本方法
* @param device
* @param requestId
* @param offset
* @param descriptor
*/
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
Log.e(TAG, String.format("onDescriptorReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
Log.e(TAG, String.format("onDescriptorReadRequest:requestId = %s", requestId));
// super.onDescriptorReadRequest(device, requestId, offset, descriptor);
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
}
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
Log.e(TAG, String.format("5.onNotificationSent:device name = %s, address = %s", device.getName(), device.getAddress()));
Log.e(TAG, String.format("5.onNotificationSent:status = %s", status));
}
@Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
Log.e(TAG, String.format("onMtuChanged:mtu = %s", mtu));
}
@Override
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
super.onExecuteWrite(device, requestId, execute);
Log.e(TAG, String.format("onExecuteWrite:requestId = %s", requestId));
}
};
/**
* 4.處理響應(yīng)內(nèi)容
*
* @param reqeustBytes
* @param device
* @param requestId
* @param characteristic
*/
private void onResponseToClient(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, byte[] reqeustBytes) {
Log.e(TAG, String.format("4.onResponseToClient:device name = %s, address = %s", device.getName(), device.getAddress()));
Log.e(TAG, String.format("4.onResponseToClient:requestId = %s", requestId));
if (mBlePeripheralCallback != null){
// 數(shù)據(jù)回調(diào)
mBlePeripheralCallback.onReceiveNewBytes(device, requestId, characteristic, reqeustBytes);
}
}
/**
* 發(fā)送數(shù)據(jù)
* @param device
* @param data
* @return
*/
public boolean transfer(BluetoothDevice device, byte[] data){
if (notify(device,mCharacteristicRead,data)){
if (mBlePeripheralCallback != null)
mBlePeripheralCallback.onWriteBytesAndStatus(true, data);
return true;
}else {
if (mBlePeripheralCallback != null)
mBlePeripheralCallback.onWriteBytesAndStatus(false, data);
return false;
}
}
/**
* 發(fā)送通知給主機
*
* @param device :發(fā)送的目標(biāo)設(shè)備
* @param characteristic :用來通知的characteristic
* @param data :通知的內(nèi)容
*/
private boolean notify(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] data) {
if (device != null && characteristic != null && data != null) {
//設(shè)置寫操作的類型 WRITE_TYPE_DEFAULT的情況選 底層會自動分包 不用人為分包
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
//把要設(shè)置的數(shù)據(jù)裝進(jìn)characteristic
characteristic.setValue(data);
//發(fā)送出去
return mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);
} else {
return false;
}
}
/**
*
* @return
*/
public BlePeripheralCallback getBlePeripheralCallback() {
return mBlePeripheralCallback;
}
public void setBlePeripheralCallback(BlePeripheralCallback blePeripheralCallback) {
this.mBlePeripheralCallback = blePeripheralCallback;
}
//========================================================BluetoothReceiver======================================================
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
return intentFilter;
}
// 監(jiān)聽BLE狀態(tài)變化
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,"=========藍(lán)牙接收處理廣播========"+intent.getAction());
BluetoothDevice device;
switch (intent.getAction()){
case BluetoothAdapter.ACTION_STATE_CHANGED:
int bleState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
switch (bleState) {
case BluetoothAdapter.STATE_TURNING_OFF:
Log.i(TAG, "...正在關(guān)閉藍(lán)牙...");
break;
case BluetoothAdapter.STATE_OFF:
Log.i(TAG, "...藍(lán)牙已關(guān)閉列牺!");
break;
case BluetoothAdapter.STATE_TURNING_ON:
Log.i(TAG, "...正在開啟藍(lán)牙...");
break;
case BluetoothAdapter.STATE_ON:
Log.i(TAG, "...藍(lán)牙已開啟...");
if (mBlePeripheralHelper != null)
initPeripheral(mContext);
initGATTServer();
break;
}
break;
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
switch (intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1)) {
case BluetoothA2dp.STATE_CONNECTING:
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i(TAG,"device: " + device.getName() +" connecting");
break;
case BluetoothA2dp.STATE_CONNECTED:
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i(TAG,"device: " + device.getName() +" connected");
break;
case BluetoothA2dp.STATE_DISCONNECTING:
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i(TAG,"device: " + device.getName() +" disconnecting");
break;
case BluetoothA2dp.STATE_DISCONNECTED:
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i(TAG,"device: " + device.getName() +" disconnected");
break;
default:
break;
}
break;
case BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED:
int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
switch (state) {
case BluetoothA2dp.STATE_PLAYING:
Log.i(TAG,"state: playing.");
break;
case BluetoothA2dp.STATE_NOT_PLAYING:
Log.i(TAG,"state: not playing");
break;
default:
Log.i(TAG,"state: unkown");
break;
}
break;
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (bondState){
case BluetoothDevice.BOND_BONDED: //配對成功
Log.i(TAG,"Device:"+device.getName()+" bonded.");
//取消搜索整陌,連接藍(lán)牙設(shè)備
break;
case BluetoothDevice.BOND_BONDING:
Log.i(TAG,"Device:"+device.getName()+" bonding.");
break;
case BluetoothDevice.BOND_NONE:
Log.i(TAG,"Device:"+device.getName()+" not bonded.");
break;
default:
break;
}
break;
default:
break;
}
}
};
}
3:使用方式:
BlePeripheralHelper mBlePeripheralHelper = BlePeripheralHelper.getInstance(this);
if (mBlePeripheralHelper.isEnabled()){
// 設(shè)置回調(diào)
mBlePeripheralHelper.setBlePeripheralCallback(new BlePeripheralCallback() {
//連接狀態(tài)回調(diào)
@Override
public void onConnectionStateChange(BluetoothDevice bluetoothDevice, int i, int i1) {
}
//開啟廣播成功回調(diào)
@Override
public void onStartAbSuccess(AdvertiseSettings advertiseSettings) {
}
//開啟廣失敗的功回調(diào)
@Override
public void onStartAbFailure(int i) {
}
//收到BLE數(shù)據(jù)回調(diào)
@Override
public void onReceiveNewBytes(BluetoothDevice bluetoothDevice, int i, BluetoothGattCharacteristic bluetoothGattCharacteristic, byte[] bytes) {
}
//發(fā)送BLE數(shù)據(jù)回調(diào)
@Override
public void onWriteBytesAndStatus(boolean b, byte[] bytes) {
}
});
// 初始化廣播
mBlePeripheralHelper.initGATTServer();
}