前言
第一次做蓝牙开发,刚开始是懵逼的,花了2-3天可以实现功能了,连接血糖仪血压仪胰岛素泵并发送接收数据,用到了BluetoothKit。能力有限,错误之处还望指出。
首先需要知道的两点,1不需要配对也可以连接成功后直接发送数据(代码中无感) 2可以同时存在多个连接
大体流程:打开蓝牙-搜索蓝牙设备-连接某个蓝牙设备-读写数据
封装
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);
}
/**
* 自动接受蓝牙设备返回的数据,建立订阅关系
*
* @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;
}
/**
* 蓝牙传输数据限制20字节,当数据过大时,可以设置,第二个参数好像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说明设置成功
}
});
}
}
调用
我去掉了自己的业务逻辑,只保留最基础的
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) {
//这里一般是要用列表展现所有设备的
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) {
//处理返回数据
read(value);
}
private void read(byte[] value) {
Log.e("bobowa","读取数据=" + Arrays.toString(value));
}
@Override
public void onNotifyFailure() {
}
@Override
public void onNotifySuccess() {
//延时处理要不写数据可能会失败
try {
Thread.sleep(200L);
}catch (InterruptedException e) {
e.printStackTrace();
}
bleManager.write(device.getAddress(),uuid,uuidwrite, getWriteData());
}
private StringdeviceName ="20200105001";
private int sendtype =1;
//根据自己的需求完成写指令的拼接
private byte[] getWriteData() {
String s1 ="551400A3";
String s2 ="";
if (sendtype ==0) {
//获取泵运行状态
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位的数组
*
* @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'会发现找不到这个方法,
项目中的源码有,sdk中没用,需要手动复制源码进自己的项目。
我设置了500,但每次只返回200字节,这个坑后续补上。
uuid
这个可以简单理解为通信的秘钥,要对应上设备的识别码才能操作,当时百度了好几个都没法用,可以用蓝牙连接设备查看
读写数据
蓝牙通信过程是使用byte数组传递的,而我们读写命令使用的hex指令(16位的字符串)