前言
第一次做藍牙開發(fā)燕侠,剛開始是懵逼的,花了2-3天可以實現(xiàn)功能了孵构,連接血糖儀血壓儀胰島素泵并發(fā)送接收數(shù)據(jù)屁商,用到了BluetoothKit。能力有限颈墅,錯誤之處還望指出蜡镶。
首先需要知道的兩點,1不需要配對也可以連接成功后直接發(fā)送數(shù)據(jù)(代碼中無感) 2可以同時存在多個連接
大體流程:打開藍牙-搜索藍牙設備-連接某個藍牙設備-讀寫數(shù)據(jù)
封裝
public class BleManager {
private BluetoothClientmClient;
private MyBleListenermyBleListener;
private BleConnectStatusListenermBleConnectStatusListener;
private BluetoothBondListenermBluetoothBondListener;
private BluetoothStateListenermBluetoothStateListener;
private SearchResponsemSearchResponse;
private BleConnectOptionsbleConnectOptions;
private SearchRequestsearchRequest;
private BleManager() {
}
private static class BleManagerHolder {
private static final BleManagerinstance =new BleManager();
}
public static BleManager getInstance() {
return BleManager.BleManagerHolder.instance;
}
public void initBle(Context context, MyBleListener myBleListener) {
this.myBleListener = myBleListener;
mClient = getBleManager(context);
//判斷打開
mBluetoothStateListener =new BluetoothStateListener() {
@Override
public void onBluetoothStateChanged(boolean openOrClosed) {
if (null !=myBleListener && openOrClosed) {
myBleListener.onBleOpenSuccess();
}
}
};
//搜索
mSearchResponse =new SearchResponse() {
@Override
public void onSearchStarted() {
if (myBleListener !=null)myBleListener.onSearchStarted();
}
@Override
public void onDeviceFounded(SearchResult device) {
if (myBleListener !=null)myBleListener.onDeviceFounded(device);
}
@Override
public void onSearchStopped() {
if (myBleListener !=null)myBleListener.onSearchStopped();
}
@Override
public void onSearchCanceled() {
if (myBleListener !=null)myBleListener.onSearchCanceled();
}
};
//搜索配置
searchRequest =new SearchRequest.Builder()
.searchBluetoothLeDevice(3000,3)
.searchBluetoothClassicDevice(5000)
.searchBluetoothLeDevice(2000)
.build();
//配對
mBluetoothBondListener =new BluetoothBondListener() {
@Override
public void onBondStateChanged(String mac,int bondState) {
}
};
//連接
mBleConnectStatusListener =new BleConnectStatusListener() {
@Override
public void onConnectStatusChanged(String mac,int status) {
if (status == com.inuker.bluetooth.library.Constants.STATUS_DISCONNECTED) {
//如果你同時連接多個設備可以判斷mac
if (null !=myBleListener)myBleListener.onDisConnected();
}
}
};
//連接配置
bleConnectOptions =new BleConnectOptions.Builder()
.setConnectRetry(3)
.setConnectTimeout(5000)
.setServiceDiscoverRetry(3)
.setServiceDiscoverTimeout(5000)
.build();
mClient.registerBluetoothBondListener(mBluetoothBondListener);
}
private BluetoothClient getBleManager(Context context) {
return new BluetoothClient(context);
}
/**
* 設備是否支持藍牙
*
* @return
*/
public boolean isCanUseBle() {
return null != BluetoothAdapter.getDefaultAdapter();
}
public void openBluetooth() {
if (null !=mClient) {
mClient.openBluetooth();
mClient.registerBluetoothStateListener(mBluetoothStateListener);
}
}
public void closeBluetooth() {
if (null !=mClient)mClient.closeBluetooth();
}
public boolean isBluetoothOpened() {
if (null !=mClient)return mClient.isBluetoothOpened();
return false;
}
public void search() {
if (null ==mClient)return;
if (null !=mSearchResponse)mClient.search(searchRequest,mSearchResponse);
}
public void stopSearch() {
if (null !=mClient)mClient.stopSearch();
}
public void connect(final String mac) {
if (null ==mClient)return;
mClient.connect(mac,bleConnectOptions,new BleConnectResponse() {
@Override
public void onResponse(int code, BleGattProfile data) {
if (code == com.inuker.bluetooth.library.Constants.REQUEST_SUCCESS) {
if (null !=myBleListener) {
myBleListener.onConnectSuccess(code, data,mac);
}
}else if (code == com.inuker.bluetooth.library.Constants.REQUEST_FAILED) {
if (null !=myBleListener)myBleListener.onConnectFail();
}
}
});
mClient.registerConnectStatusListener(mac,mBleConnectStatusListener);
}
public void disconnect(String mac) {
if (null !=mClient)mClient.disconnect(mac);
}
/**
* 自動接受藍牙設備返回的數(shù)據(jù)恤筛,建立訂閱關系
*
* @return
*/
public void notify(String mac, String uuid, String uuidread) {
unnotify(mac, uuid, uuidread);
if (null ==mClient)return;
mClient.notify(mac, UUID.fromString(uuid), UUID.fromString(uuidread),new BleNotifyResponse() {
@Override
public void onNotify(UUID service, UUID character,byte[] value) {
if (null !=myBleListener)myBleListener.onNotify(value);
}
@Override
public void onResponse(int code) {
if (code == com.inuker.bluetooth.library.Constants.REQUEST_SUCCESS) {
if (null !=myBleListener)myBleListener.onNotifySuccess();
}else if (code == com.inuker.bluetooth.library.Constants.REQUEST_FAILED) {
if (null !=myBleListener)myBleListener.onNotifyFailure();
}
}
});
}
public void unnotify(String mac, String uuid, String uuidread) {
if (null ==mClient)return;
mClient.unnotify(mac, UUID.fromString(uuid), UUID.fromString(uuidread),new BleUnnotifyResponse() {
@Override
public void onResponse(int code) {
}
});
}
public void write(String MAC, String uuid, String uuidwrite,final byte[] data) {
if (null ==mClient)return;
mClient.write(MAC, UUID.fromString(uuid), UUID.fromString(uuidwrite), data,new BleWriteResponse() {
@Override
public void onResponse(int code) {
if (code == com.inuker.bluetooth.library.Constants.REQUEST_SUCCESS) {
if (null !=myBleListener)myBleListener.onWriteSuccess();
}else {
if (null !=myBleListener)myBleListener.onWriteFailure();
}
}
});
}
public void destroy(String mac, String uuid, String uuidread) {
if (null !=mClient) {
mClient.stopSearch();
mClient.clearRequest(mac, com.inuker.bluetooth.library.Constants.REQUEST_READ);
mClient.clearRequest(mac, com.inuker.bluetooth.library.Constants.REQUEST_WRITE);
mClient.clearRequest(mac, com.inuker.bluetooth.library.Constants.REQUEST_NOTIFY);
mClient.refreshCache(mac);
mClient.unregisterConnectStatusListener(mac,mBleConnectStatusListener);
mClient.unregisterBluetoothBondListener(mBluetoothBondListener);
mClient.unregisterBluetoothStateListener(mBluetoothStateListener);
}
unnotify(mac, uuid, uuidread);
mBleConnectStatusListener =null;
mBluetoothBondListener =null;
mBluetoothStateListener =null;
myBleListener =null;
}
/**
* 藍牙傳輸數(shù)據(jù)限制20字節(jié)官还,當數(shù)據(jù)過大時,可以設置叹俏,第二個參數(shù)好像23-512(詳情見源碼)
*
* @return
*/
public void requestMtu(String MAC) {
if (null ==mClient)return;
mClient.requestMtu(MAC,500,new BleMtuResponse() {
@Override
public void onResponse(int code, Integer data) {
//code為1說明設置成功
}
});
}
}
調(diào)用
我去掉了自己的業(yè)務邏輯妻枕,只保留最基礎的
public class DemoActivityextends Activityimplements MyBleListener {
private BleManagerbleManager;
private SearchResultdevice;
//請使用自己的uuid
public Stringuuid ="0003cdd0-0000-1000-8000-00805f9b0131";
public Stringuuidread ="0003cdd1-0000-1000-8000-00805f9b0131";
public Stringuuidwrite ="0003cdd2-0000-1000-8000-00805f9b0131";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bleManager = BleManager.getInstance();
bleManager.initBle(this,this);
if (!bleManager.isBluetoothOpened()) {
bleManager.openBluetooth();
}else {
bleManager.search();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
bleManager.destroy(device.getAddress(),uuid,uuidread);
}
@Override
public void onSearchStarted() {
}
@Override
public void onDeviceFounded(SearchResult device) {
//這里一般是要用列表展現(xiàn)所有設備的
if (device !=null && device.getName().indexOf("P4B") != -1) {
this.device = device;
bleManager.connect(device.getAddress());
}
}
@Override
public void onSearchStopped() {
}
@Override
public void onSearchCanceled() {
}
@Override
public void onWriteSuccess() {
}
@Override
public void onWriteFailure() {
}
@Override
public void onBleOpenSuccess() {
}
@Override
public void onDisConnected() {
}
@Override
public void onStartConnect() {
}
@Override
public void onConnectFail() {
}
@Override
public void onConnectSuccess(int code, BleGattProfile data, String mac) {
bleManager.unnotify(device.getAddress(),uuid,uuidread);
bleManager.requestMtu(device.getAddress());
bleManager.notify(device.getAddress(),uuid,uuidread);
}
@Override
public void onNotify(byte[] value) {
//處理返回數(shù)據(jù)
read(value);
}
private void read(byte[] value) {
Log.e("bobowa","讀取數(shù)據(jù)=" + Arrays.toString(value));
}
@Override
public void onNotifyFailure() {
}
@Override
public void onNotifySuccess() {
//延時處理要不寫數(shù)據(jù)可能會失敗
try {
Thread.sleep(200L);
}catch (InterruptedException e) {
e.printStackTrace();
}
bleManager.write(device.getAddress(),uuid,uuidwrite, getWriteData());
}
private StringdeviceName ="20200105001";
private int sendtype =1;
//根據(jù)自己的需求完成寫指令的拼接
private byte[] getWriteData() {
String s1 ="551400A3";
String s2 ="";
if (sendtype ==0) {
//獲取泵運行狀態(tài)
s2 ="00AA";
}
if (sendtype ==1) {
//獲取大劑量
s2 ="01AA";
}
if (sendtype ==2) {
//獲取基礎率
s2 ="0200";
}
if (sendtype ==3) {
//獲取警示
s2 ="03AA";
}
if (sendtype ==4) {
//獲取排氣
s2 ="04AA";
}
String s3 = stringToHexString(deviceName) +"00";
String s4 = s1 + s2 + s3;
String crc =getCRC(s4);
Log.d("bobowa","s4=" + s4);
return hexStringToBytes(s4 + crc);
}
/**
* 字符串轉換為16進制字符串
* 設備號轉hex命令
*
* @param s
* @return
*/
public String stringToHexString(String s) {
String str ="";
for (int i =0; i < s.length(); i++) {
int ch = s.charAt(i);
String s4 = Integer.toHexString(ch);
str = str + s4;
}
return str;
}
public static String getCRC(String data) {
data = data.replace(" ","");
int len = data.length();
if (!(len %2 ==0)) {
return "0000";
}
int num = len /2;
byte[] para =new byte[num];
for (int i =0; i < num; i++) {
int value = Integer.valueOf(data.substring(i *2,2 * (i +1)),16);
para[i] = (byte) value;
}
return getCRC(para);
}
public static String getCRC(byte[] bytes) {
//CRC寄存器全為1
int CRC =0x0000ffff;
//多項式校驗值
int POLYNOMIAL =0x0000a001;
int i, j;
for (i =0; i < bytes.length; i++) {
CRC ^= ((int) bytes[i] &0x000000ff);
for (j =0; j <8; j++) {
if ((CRC &0x00000001) !=0) {
CRC >>=1;
CRC ^= POLYNOMIAL;
}else {
CRC >>=1;
}
}
}
//結果轉換為16進制
String result = Integer.toHexString(CRC).toUpperCase();
if (result.length() !=4) {
StringBuffer sb =new StringBuffer("0000");
result = sb.replace(4 - result.length(),4, result).toString();
}
//交換高低位
return result.substring(2,4) + result.substring(0,2);
}
/**
* String轉換成每2位的數(shù)組
*
* @param hexString
* @return
*/
public byte[] hexStringToBytes(String hexString) {
Log.d("bobowa","hexStringToBytes=" + hexString);
if (hexString ==null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() /2;
char[] hexChars = hexString.toCharArray();
byte[] d =new byte[length];
for (int i =0; i < length; i++) {
int pos = i *2;
d[i] = (byte) (charToByte(hexChars[pos]) <<4 | charToByte(hexChars[pos +1]));
}
return d;
}
public byte charToByte(char c) {
return (byte)"0123456789ABCDEF".indexOf(c);
}
}
requestMtu
如果你只依賴implementation 'com.inuker.bluetooth:library:1.4.0'會發(fā)現(xiàn)找不到這個方法,
項目中的源碼有粘驰,sdk中沒用投放,需要手動復制源碼進自己的項目。
我設置了500先舷,但每次只返回200字節(jié)贷祈,這個坑后續(xù)補上。
uuid
這個可以簡單理解為通信的秘鑰顶伞,要對應上設備的識別碼才能操作饵撑,當時百度了好幾個都沒法用剑梳,可以用藍牙連接設備查看
讀寫數(shù)據(jù)
藍牙通信過程是使用byte數(shù)組傳遞的,而我們讀寫命令使用的hex指令(16位的字符串)