android BluetoothKit 蓝牙开发实战 requestMtu的坑

前言

第一次做蓝牙开发,刚开始是懵逼的,花了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位的字符串)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343