android BluetoothKit 藍牙開發(fā)實戰(zhàn) requestMtu的坑

前言

第一次做藍牙開發(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位的字符串)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滑潘,一起剝皮案震驚了整個濱河市垢乙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌语卤,老刑警劉巖追逮,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異粹舵,居然都是意外死亡钮孵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門眼滤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巴席,“玉大人,你說我怎么就攤上這事诅需⊙Γ” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵诱担,是天一觀的道長毡证。 經(jīng)常有香客問我,道長蔫仙,這世上最難降的妖魔是什么料睛? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮摇邦,結果婚禮上恤煞,老公的妹妹穿的比我還像新娘。我一直安慰自己施籍,他們只是感情好居扒,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丑慎,像睡著了一般喜喂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上竿裂,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天玉吁,我揣著相機與錄音,去河邊找鬼腻异。 笑死进副,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的悔常。 我是一名探鬼主播影斑,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼给赞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了矫户?” 一聲冷哼從身側響起片迅,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吏垮,沒想到半個月后障涯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡膳汪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了九秀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遗嗽。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鼓蜒,靈堂內(nèi)的尸體忽然破棺而出痹换,到底是詐尸還是另有隱情,我是刑警寧澤都弹,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布娇豫,位于F島的核電站,受9級特大地震影響畅厢,放射性物質(zhì)發(fā)生泄漏冯痢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一框杜、第九天 我趴在偏房一處隱蔽的房頂上張望浦楣。 院中可真熱鬧,春花似錦咪辱、人聲如沸振劳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽历恐。三九已至,卻和暖如春专筷,著一層夾襖步出監(jiān)牢的瞬間弱贼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工仁堪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哮洽,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓弦聂,卻偏偏與公主長得像鸟辅,于是被迫代替她去往敵國和親氛什。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354