前言
最近一直在開發關於藍牙的功能,原本是不想寫這一篇文章,由於網上關於ios藍牙開發的文章實在太多了,成噸成噸的文章出現,可是很遺憾都只是一些皮毛,或者只是簡單的介紹一下基本概念而已,對於一些小白可能還有不少不少疑惑,因此萌生了寫一篇文章,並附上實際例子的demo,供即將項目中準備開發的夥伴參考。css
正文
首先
咱們得明確一下很重要的幾個概念
1.當前ios中開發藍牙所運用的系統庫是<CoreBluetooth/CoreBluetooth.h>
。
2.藍牙外設必須爲4.0及以上,不然沒法開發,藍牙4.0設備由於低耗電,因此也叫作BLE。
3.CoreBluetooth框架的核心實際上是兩個東西,peripheral和central, 能夠理解成外設和中心,就是你的蘋果手機就是中心,外部藍牙稱爲外設。
4.服務和特徵(service and characteristic):簡而言之,外部藍牙中它有若干個服務service(服務你能夠理解爲藍牙所擁有的能力),而每一個服務service下擁有若干個特徵characteristic(特徵你能夠理解爲解釋這個服務的屬性)。
5.Descriptor(描述)用來描述characteristic變量的屬性。例如,一個descriptor能夠規定一個可讀的描述,或者一個characteristic變量可接受的範圍,或者一個characteristic變量特定的單位。
6.跟硬件親測,Ios藍牙每次最多接收155字節的數據,安卓5.0如下最大接收20字節,5.0以上能夠更改最大接收量,能達到500多字節。ios
其次
經過以上關鍵信息的解釋,而後看一下藍牙的開發流程:git
- 創建中心管理者
- 掃描外設(discover)
- 鏈接外設(connect)
- 掃描外設中的服務和特徵(discover)
4.1 獲取外設的services
4.2 獲取外設的Characteristics,獲取Characteristics的值,
獲取Characteristics的Descriptor和Descriptor的值
- 與外設作數據交互(explore and interact)
- 斷開鏈接(disconnect)
具體代碼github
1.建立一箇中心管理者
當發現藍牙狀態是開啓狀態,你就能夠利用中央設備進行掃描外設,若是爲關閉狀態,系統會自動彈出讓用戶去設置藍牙,這個不須要咱們開發者關心。算法
2.利用中心去掃描外設
2.1當掃描到外設,觸發如下代理方法
在這裏須要說明的是,
一.當掃描到外設,咱們能夠讀到相應外設廣播信息,RSSI信號強度(能夠利用RSSI計算中心和外設的距離)。
二.咱們能夠根據必定的規則進行鏈接,通常是默認名字或者名字和信號強度的規則來鏈接。
三.像我如今作的無鑰匙啓動車輛鎖定車輛,就須要加密通信,不能誰來鏈接均可以操做車輛,須要和外設進行加密通信,可是一切的經過算法的校驗都是在和外設鏈接上的基礎上進行,例如鏈接上了,你發送一種和硬件約定好的算法數據,硬件接收到校驗經過了就正常操做,沒法經過則由硬件(外設)主動斷開。api
3.當鏈接到外設,會調用如下代理方法
這裏須要說明的是
當成功鏈接到外設,須要設置外設的代理,爲了掃描服務調用相應代理方法數組
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ NSLog(@"鏈接外設成功!%@",peripheral.name); [peripheral setDelegate:self]; [peripheral discoverServices:nil]; }
4.掃描外設中的服務和特徵
5.與外設作數據交互
須要說明的是蘋果官方提供發送數據的方法很簡單,只須要調用下面的方法bash
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type
咱們只須要在搜索每一個服務的特徵,記錄這個特徵,而後向這個特徵發送數據就能夠了。app
6.斷開鏈接
調用如下代碼,須要說明的是中心斷開與外設的鏈接。框架
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;
以上呢是整個藍牙的開發過程,系統提供的框架api就這麼多,開發起來也不是很難,要是你認爲這篇文章到這裏就結束了,你就大錯特錯了,這篇文章的精華內容將從這裏開始,因爲公司項目的保密性,我不能以它爲例,那我就以小米手環爲實例,主要分享一下數據解析。
精華部分
1.當調用瞭如下代理方法的時候,咱們須要處理接收到的數據
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
小米手環所定義的幾個UUID以下:
@"FF06" 這個UUID定義的是步數
@"FF0C" 這個UUID定義的是電量
@"2A06"這個UUID定義的是震動
@"FF01"這個UUID定義的是相關的設備信息
經過以上的UUID,咱們能夠讀取到步數,電量,操做手環震動,並讀取手環相應設備的信息,這裏須要說明的是我並不清楚設備信息的具體協議,因此這裏無法解析。
if ([characteristic.UUID.UUIDString isEqualToString:STEP]) { Byte *steBytes = (Byte *)characteristic.value.bytes; int steps = bytesValueToInt(steBytes); NSLog(@"步數:%d",steps); }
當咱們讀到步數這個UUID時,咱們能夠拿到value,小米手環所定義的協議是4個字節,咱們須要將4個字節轉換爲int 類型便可
方法以下
須要說明的是這個方法是C語言的方法,採用位與運算,固然若是項目中須要另外一種方式的轉換,如:發過來兩字節須要你轉換爲int,若是你不會轉換,能夠去網上搜索,我會在文章後附一些經常使用的轉換方法。
這裏重點說明的是步數讀取,剩餘相似。
2.當咱們給外設發送數據時,咱們須要跟硬件定協議,固然這是在開始項目以前作好的事情。
小米手環協議中震動命令的觸發,是向硬件發送一個10進制的 2
這裏須要說明的是咱們發送數據給硬件通常是字節數組,而後將他轉換爲NSData
發送。
這裏須要再添加一點,若是協議要求你發ASCII碼,例如‘SHAKE’,只須要這麼處理
if (thePerpher && theSakeCC) { Byte zd[] = {'S','H','A','K','E'}; NSData *theData = [NSData dataWithBytes:zd length:1]; [thePerpher writeValue:theData forCharacteristic:theSakeCC type:CBCharacteristicWriteWithoutResponse]; }
3.項目中實際開發的運用
當咱們面對實際開發時,咱們不可能這麼直接去在一個控制器中去寫這麼多代碼,若是你說這沒多少啊,那我無話可說了😄。。。固然有人會說運用三方庫的啊,babyBluetooth在github上star仍是挺高的,個人觀點是沒有必要去依賴所謂的三方庫,有的三方庫高度封裝性會導致咱們若是遇到錯誤時沒法排查,因此三方庫慎用,固然你能夠參考一些Star很高的三方庫,看大神的代碼思想,有利於本身讀寫代碼的能力。
個人主要思路是封裝一個單例類,封裝好掃描的方法,讀取數據的方法(通常是代理回調),發送指令(例如小米的震動)方法,而在讀取數據中咱們能夠採用模型的思想,當收到藍牙外設發送過來的數據時候,咱們解析完後包裝成模型經過代理回傳過去,之後咱們在控制器中每次拿到的都是模型數據,這樣處理起來方便大大的。
下面將小米手環demo附上,供須要的朋友參考學習,若是文章中我有什麼沒有說的很明白,或者什麼疑惑能夠留言。
demo https://github.com/markdashi/MIBLE
附經常使用轉換方法
@interface NSString (Extension)
2016.8.29補充
因爲項目中須要作關於後臺持續掃描,相似於常見的藍牙音箱,打開手機APP鏈接藍牙音箱,播放音樂,當手機遠離藍牙音箱後,中止播放,當手機靠近的時候,藍牙音箱又開始播放了,對於這中需求的實現我開始很困惑,藍牙如何後臺持續掃描呢,我嘗試了不少方法是失敗的,通過我多方面查詢資料弄清楚如何實現這個需求:
1.須要後臺運行需申請後臺權限
勾選便可擁有後臺權限,若是外設持續發送數據,APP端能夠接收到數據。
2.掃描時需指定serviceUUID,需外設廣播出本身的SeviceUUID,APP端做爲掃描的條件。
這是蘋果掃描方法的的官方解釋:
Applications that have specified the bluetooth-central
background mode are allowed to scan while backgrounded, with two
- caveats: the scan must specify one or more service types in serviceUUIDs, and the
CBCentralManagerScanOptionAllowDuplicatesKey
- scan option will be ignored.
顯而易見的說的很清楚,後臺模式時藍牙做爲central,必須指定serviceUUIDs,scan option忽略。
例子
掃描方法:
[self.centralManger scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FEE0"],[CBUUID UUIDWithString:@"FEE7"]] options:nil];
這樣當在後臺的時候是能夠持續掃描的。
3.當後臺斷開鏈接時候會調用系統方法
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
咱們須要在這裏設置自動重連,便可實現上述的需求。
2016.10.19補充
關於藍牙須要作一些補充及修改,首先對於後臺掃描時候,咱們申請後臺權限只須要申請
權限申請
若是兩個都申請,若是提交安裝包到APPStore,你會被拒絕
If your app is meant to work with external hardware, supported protocols must be included in the UISupportedExternalAccessoryProtocols key in your app's Info.plist file - and the hardware's PPID # should be provided in the Review Notes field of your app in iTunes Connect.Additionally, your app must be authorized by MFi to use the desired hardware. If you are not yet in the MFi Program, you can enroll at MFi program.Please either revise your Info.plist to include the UISupportedExternalAccessoryProtocols key and update your Review Notes to include the PPID # - or remove the external-accessory value from the UIBackgroundModes key.