Android藍(lán)牙通信過程詳解

藍(lán)牙無線技術(shù)是一種全球通用的短距離無線技術(shù),具有耗電量低致燥、成本低登疗、安全性、穩(wěn)定性、易用性等優(yōu)點(diǎn)辐益,尤其在物聯(lián)網(wǎng)設(shè)備上的占有率非常高断傲,因此我們有必要對(duì)藍(lán)牙做深入的了解。本文從藍(lán)牙和Android手機(jī)的一次通信過程為引智政,講解Android藍(lán)牙通信上的一些問題和原理认罩,以及調(diào)用方法。如有描述不當(dāng)?shù)牡胤竭€請(qǐng)指出续捂。

例子

假設(shè)Android手機(jī)A要給藍(lán)牙設(shè)備B發(fā)送一條“hello”的消息垦垂,然后B會(huì)給A回一條“world”。我們應(yīng)該怎么做呢牙瓢?

過程

過程可以大致分為三步:

  • 尋找設(shè)備
  • 連接
  • 通信

尋找設(shè)備

A要怎么找到B呢劫拗,一般是由設(shè)備B按照一定的周期廣播數(shù)據(jù)包,然后A和B指定好協(xié)議矾克,看廣播數(shù)據(jù)里有沒有協(xié)議約定的數(shù)據(jù)页慷,有的話則說明找到了B。
廣播的數(shù)據(jù)包分為四種

Android設(shè)備通過系統(tǒng)提供的API開始接受廣播數(shù)據(jù),

//要先停止上一次的scan控妻,不然無法啟動(dòng)新的scan
bluetoothAdapter.getBluetoothLeScanner().stopScan(bleCallback);
bluetoothAdapter.getBluetoothLeScanner().startScan(getFilters(), getSettings(), bleCallback);

然后在callback里獲取廣播數(shù)據(jù)

  private ScanCallback bleCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, final ScanResult result) {
           
        }
}

ScanResult就是當(dāng)前接收到的廣播里數(shù)據(jù)欲逃,在5.0以及5.0以上的api,會(huì)幫我們解析廣播的數(shù)據(jù)饼暑,但是5.0以下的話,只是會(huì)把整個(gè)廣播數(shù)據(jù)傳回來洗做。下面大致講解下廣播協(xié)議數(shù)據(jù)弓叛。

廣播協(xié)議分析:

image.png

官方文檔:
https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
廣播的數(shù)據(jù),按 len 1字節(jié)诚纸,TYPE1字節(jié), data (len -1)字節(jié)的順序組依次組織撰筷,key的含義在上面的表格中已經(jīng)給出。 下面舉個(gè)栗子
假設(shè)下面是設(shè)備B的廣播數(shù)據(jù)

02 01 06 05 03 F6 FE F5 FE 0E 09 5B 52 4F 41 44 42 49 54 5D 00 00 00 00 0D FF AA 00 66 EE 06 66 1F FF FF 25 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

解析數(shù)據(jù)

02 01 06           //長(zhǎng)度2畦徘,type 1 表示設(shè)備功能毕籽, 6表示110 -> 普通發(fā)現(xiàn)模式,且不支持BR/EDR
05 03 F6 FE F5 FE  //長(zhǎng)度5井辆,type 03关筒,表示16位uuid列表, F6FE和F5FE兩組uuid
0E 09 5B 52 4F 41 44 42 49 54 5D 00 00 00 00  長(zhǎng)度 0E杯缺,即14蒸播, type 9,即設(shè)備名稱,后面試試字符串轉(zhuǎn)16進(jìn)制
0D FF AA 00 66 EE 06 66 1F FF FF 25 04 00 , FF表示自定義數(shù)據(jù)袍榆,即藍(lán)牙設(shè)備的自定義的數(shù)據(jù)

另外胀屿,設(shè)備的廣播頻率是可以自己定義的,從幾ms到幾百ms不等包雀,廣播的頻率決定了手機(jī)發(fā)現(xiàn)設(shè)備的快慢宿崭,
有個(gè)軟件叫"nRF Connect",可以很方便的觀看藍(lán)牙設(shè)備的廣播速度。下載地址
軟件截圖:

221526440023_.pic.jpg

上圖的Adv. Interval就是廣播的間隔才写,上文講到的所有東西都可以在這個(gè)軟件上觀察到葡兑。

連接過程

藍(lán)牙分為5種工作狀態(tài),

  • 準(zhǔn)備(standby):就緒狀態(tài)琅摩,準(zhǔn)備轉(zhuǎn)變?yōu)槠渌麪顟B(tài)
  • 廣播(advertising):向外發(fā)送數(shù)據(jù)的狀態(tài)
  • 監(jiān)聽掃描(Scanning): 掃描狀態(tài)的時(shí)候铁孵,在接受到ADV_IND包是,會(huì)發(fā)送SCAN_REQ包房资,可以獲得更多的信息蜕劝。
  • 發(fā)起連接(Initiating):發(fā)起連接狀態(tài),在ADV_IND或者ADV_DIRECT_IND之后轰异,會(huì)發(fā)送CONNECT_REQ包岖沛,從而建立連接。
  • 已連接(Connected):根據(jù)連接時(shí)約定的參數(shù)搭独,發(fā)送CONNECT_EVENT婴削,保持連接不斷開。

具體工作流程如下:

  1. 可被連接的設(shè)備(Advertiser)牙肝,按照一定的周期廣播ADV_IND或者ADV_DIRECT_IND包(可參考“藍(lán)牙協(xié)議分析(5)_BLE廣播通信相關(guān)的技術(shù)分析”)唉俗。
  2. 主動(dòng)連接的設(shè)備(Initiator),在收到廣播包之后配椭,會(huì)回應(yīng)一個(gè)CONNECT_REQ請(qǐng)求虫溜,該請(qǐng)求攜帶了可決定后續(xù)“通信時(shí)序”的參數(shù),例如雙方在哪一個(gè)時(shí)間點(diǎn)股缸、哪一個(gè)Physical Channel收發(fā)數(shù)據(jù)衡楞,等等。
  3. Initiator在發(fā)出CONNECT_REQ數(shù)據(jù)包之后敦姻,自動(dòng)轉(zhuǎn)變?yōu)镃onnection狀態(tài)瘾境,成為Master角色(注意:這是“自動(dòng)”的,不需要等待另一方的回應(yīng))镰惦。同樣迷守,Advertiser在收到CONNECT_REQ請(qǐng)求之后,也自動(dòng)轉(zhuǎn)變?yōu)镃onnection狀態(tài)陨献,成為Slave角色盒犹。
  4. 此后,雙方按照CONNECT_REQ參數(shù)所給出的約定,定時(shí)到切換到某一個(gè)Physical Channel上急膀,按照Master->Slave然后Slave->Master的順序沮协,收發(fā)數(shù)據(jù),直至連接斷開卓嫂。

在Android手機(jī)中慷暂,連接的代碼非常簡(jiǎn)單,只有一個(gè)api可以調(diào)用晨雳,如下:

private BluetoothGatt connectGattCompat(BluetoothGattCallback bluetoothGattCallback, BluetoothDevice device, boolean autoConnect) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            return device.connectGatt(context, autoConnect, bluetoothGattCallback, TRANSPORT_LE);
        } else {
            return device.connectGatt(context, autoConnect, bluetoothGattCallback);
        }
    }

之后在bluetoothGattCallback的onConnectionChange的回調(diào)里行瑞,就可以知道連接的結(jié)果。

通信

說道通信餐禁,就要講到跳頻血久。跳頻的原理是

藍(lán)牙這邊在通信的時(shí)候,會(huì)約定一個(gè)隨機(jī)的頻率(也不是完全隨機(jī)帮非,有一個(gè)范圍)氧吐。雙方在這個(gè)頻率上通信,這樣通信更穩(wěn)定末盔。所以Ble在掃描和連接兩個(gè)步驟中筑舅,可能耗時(shí)比較長(zhǎng),但是開始通信之后陨舱,速度和穩(wěn)定性都會(huì)加快不少翠拣。
而跳頻的參數(shù),是在連接命令里發(fā)過去的游盲。

藍(lán)牙設(shè)備误墓,都會(huì)注冊(cè)service和characteristic,并且用uuid標(biāo)識(shí)益缎。

  • service 可以理解為一個(gè)服務(wù)优烧,在BLE從機(jī)中有多個(gè)服務(wù),電量信息链峭,系統(tǒng)服務(wù)信息等,每一個(gè)service中包含了多個(gè)characteristic特征值又沾,每一個(gè)具體的characteristic特征值才是BLE通信的主題弊仪。

  • characteristic特征值:BLE主機(jī)從機(jī)通信均是通過characteristic進(jìn)行,可以將其理解為一個(gè)標(biāo)簽杖刷,通過該標(biāo)簽可以讀取或?qū)懭胂嚓P(guān)信息励饵。

大家可以理解為service就是java里的class,characteristic是class里的public方法滑燃。所以我們應(yīng)該先找到class役听,再調(diào)用其方法。

再往下想,寫和讀肯定是兩個(gè)方法典予,所以也會(huì)是兩個(gè)characteristic甜滨。
service和characteristic的uuid都是通信之前雙方約定好的。android端在上文說的discoverService之后瘤袖,查看有沒有對(duì)應(yīng)的uuid衣摩,如果有,就可以發(fā)起通信了捂敌。

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            listener.onServicesDiscovered(status);
        }
         @Override
         public void onServicesDiscovered(final int status) {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
                service = getGatt().getService(ServerUUID);
                if (null != service) {
                    BluetoothGattCharacteristic read_characteristic = service.getCharacteristic(readDataUUID);
                    if (null != read_characteristic) {
                        int properties = read_characteristic.getProperties();
                        if ((properties | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                            getGatt().setCharacteristicNotification(read_characteristic, true);
                            }
                      }
                   }
             }
        }

setCharacteristicNotification 成功后艾扮,就可以在onCharacteristicChanged方法收到回調(diào)了。
藍(lán)牙通信的過程占婉,就是往writeCharacteristic里寫數(shù)據(jù)泡嘴,然后監(jiān)聽readCharacteristic的返回。這兩個(gè)操作保持有序進(jìn)行逆济,就可以通信了酌予。

過程總結(jié)

針對(duì)上文說的例子, 畫個(gè)圖來總結(jié)一下


藍(lán)牙通信完整流程.png

藍(lán)牙采坑總結(jié)

scan過程

  • 據(jù)測(cè)試纹腌,Android5.0以下的手機(jī)霎终,是不支持128位的uuid的,只支持16位的升薯。如果過濾的uuid填入128位的話莱褒,是搜索不到設(shè)備的。所以要分別判斷涎劈。

  • 部分手機(jī)(三星遇到過)广凸,在藍(lán)牙關(guān)閉情況下,仍然可以使用ble功能蛛枚,但是經(jīng)常會(huì)有問題谅海,所以請(qǐng)務(wù)必打開藍(lán)牙再開始通信。

  • 部分手機(jī)蹦浦,如果1次scan不到扭吁,后續(xù)都不會(huì)scan到了,這時(shí)要停止scan盲镶,重新啟動(dòng)一次侥袜。

連接錯(cuò)誤

國(guó)內(nèi)Android手機(jī)的藍(lán)牙不知道為何,非常不穩(wěn)定溉贿。在連接的過程中枫吧,會(huì)有各種各樣的錯(cuò)誤,我用了github上總結(jié)的錯(cuò)誤宇色,
https://github.com/Twelvelines/AndroidMuseumBleManager/blob/ef5e866c0aa8955320bac7ee9884f60be5bbe1d5/bluetooth-manager-lib/src/main/java/com/blakequ/bluetooth_manager_lib/connect/GattError.java
或者
https://blog.csdn.net/ocean20/article/details/65431478

這些錯(cuò)誤會(huì)頻繁的遇到九杂,所以為了成功率更高颁湖,需要在連接的時(shí)候增加重試機(jī)制。
遇到這些錯(cuò)誤例隆,立刻斷開連接甥捺,等待1-2秒后再次嘗試連接。
具體見以下代碼:

 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);

            BleLog.d("onConnectionStateChange", "status: " + status + " newState: " + newState);
            if (status != BluetoothGatt.GATT_SUCCESS) {
                BleLog.e(TAG, "連接狀態(tài)異常->" + GattError.parseConnectionError(status));
                disconnectInternal(false);
                onConnectError(status);
               
            }

onConnectError的回調(diào)里裳擎,可以直接去重試連接涎永。這樣可以提高成功率。但是重新連接之前一定要close當(dāng)前連接鹿响,否則會(huì)出現(xiàn)各種問題羡微。

但是重試隨之而來的問題是老的gatt對(duì)象可能仍然會(huì)回調(diào)(系統(tǒng)回調(diào)不知道什么時(shí)候回來),所以在接收回調(diào)的時(shí)候惶我,最好判斷一下當(dāng)前的gatt對(duì)象是否是最新的妈倔。

然后還有就是通信問題,通信相對(duì)于連接來說绸贡,是穩(wěn)定很多的盯蝴,但是仍然會(huì)有一定的幾率,收不到消息听怕。所以硬件設(shè)備和手機(jī)的藍(lán)牙代碼捧挺,最好有一方有重試機(jī)制,一般來說手機(jī)重試就足夠了尿瞭。

參考文獻(xiàn)

后續(xù)會(huì)對(duì)android連接的源碼分析一波

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闽烙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子声搁,更是在濱河造成了極大的恐慌黑竞,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疏旨,死亡現(xiàn)場(chǎng)離奇詭異很魂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)檐涝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門遏匆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谁榜,你說我怎么就攤上這事拉岁。” “怎么了惰爬?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)惫企。 經(jīng)常有香客問我撕瞧,道長(zhǎng)陵叽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任丛版,我火速辦了婚禮巩掺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘页畦。我一直安慰自己胖替,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布豫缨。 她就那樣靜靜地躺著独令,像睡著了一般。 火紅的嫁衣襯著肌膚如雪好芭。 梳的紋絲不亂的頭發(fā)上燃箭,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音舍败,去河邊找鬼招狸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛邻薯,可吹牛的內(nèi)容都是我干的裙戏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼厕诡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼累榜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起木人,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤信柿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后醒第,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渔嚷,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年稠曼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了形病。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霞幅,死狀恐怖漠吻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情司恳,我是刑警寧澤途乃,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站扔傅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唉锌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杠纵。 院中可真熱鬧,春花似錦钩骇、人聲如沸比藻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽银亲。三九已至,卻和暖如春唐瀑,著一層夾襖步出監(jiān)牢的瞬間群凶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工哄辣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留请梢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓力穗,卻偏偏與公主長(zhǎng)得像毅弧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子当窗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 背景 藍(lán)牙歷史說到藍(lán)牙够坐,就不得不說下藍(lán)牙技術(shù)聯(lián)盟(Bluetooth SIG),它負(fù)責(zé)藍(lán)牙規(guī)范制定和推廣的國(guó)際組織...
    徐正峰閱讀 12,314評(píng)論 6 33
  • 藍(lán)牙 藍(lán)牙的波段為2400-2483.5MHz(包括防護(hù)頻帶)。這是全球范圍內(nèi)無需取得執(zhí)照(但定不是無管制的)的工...
    蘇永茂閱讀 6,147評(píng)論 0 11
  • 初識(shí)低功耗藍(lán)牙 Android 4.3(API Level 18)開始引入Bluetooth Low Energy...
    JBD閱讀 112,643評(píng)論 46 342
  • BLE簡(jiǎn)述 藍(lán)牙是一套非常龐大復(fù)雜的協(xié)議棧崖面,通俗的說就是一組應(yīng)用于無線系統(tǒng)通信的約定元咙,各個(gè)廠家根據(jù)這個(gè)約定生產(chǎn)出了...
    dxdingdu閱讀 11,319評(píng)論 5 37
  • 寄生式發(fā)展 在南美的熱帶雨林里有一種名叫“絞殺榕”的植物,它緊緊纏繞在高大的樹木上巫员,通過奪取對(duì)方的陽光和養(yǎng)分而生存...
    陽明心閱讀 571評(píng)論 0 0