前言:
蘋果在IOS 6系統之后開始支持BLE 4.0,iPhone4s,iPod 5,iPad 3等之后的機型開始內嵌BLE4.0硬件,因此在開發前請先確認你的開發環境符合上述要求,並且蘋果在BLE4.0之后,對外部的連接設備已經不在需要MFI認證了,當然你的外設肯定得要有藍牙4.0模塊了
綜述:
開發BLE4.0的App,你需要在你的項目里面導入框架:
CoreBluetooth.framework
在需要使用到藍牙的文件里面你需要導入頭文件:
#import <CoreBluetooth/CoreBluetooth.h>
#import<CoreBluetooth/CBService.h>
並且你需要在你的藍牙類里面實現兩個協議,CBCentralManagerDelegate ,CBPeripheralDelegate
現在我們需要有兩個實例對象CBCentralManager和CBPeripheral,第一個是藍牙中心管理器,主要用來搜索外設,連接外設以及處理外設斷開的情況;第二個主要用於在藍牙中心管理器成功連接外設之后的一系列動作,如:讀取外設的服務號CBService,特征值號CBCharacteristic以及對這些特征值號進行讀寫操作等,下面我們來看一下IOS這邊和外設聯機的一個大概流程:
1):IOS這邊藍牙中心管理器開始掃描廣播包(掃描的時長可以自己寫一個定時器控制,並且可以設定掃描的具體條件)
2):外設開始廣播(當然外設的廣播時長也是可以設定的,這是硬件那邊的事了,我不太懂)
3):IOS發現有廣播包,就請求連接外設(這是底層自動實現,我們不需要寫代碼),之后外設會接收到連接請求,如果外設接收這個連接請求,就會給IOS那邊發送一個連接請求的確認包,當IOS這邊收到這個包后,兩邊設備就完成了連接(當然,由於連接底層已經寫好,比較復雜,我講的比較膚淺,只是一個大概步驟而已)
4):連接成功之后IOS這邊就可以讀取外設的相關信息了,比如服務號CBService,特征值號CBCharacteristic,還有外設的一些硬件信息,電池電量,信號強度RSSI什么的
5):當然在正常連接的過程中總會出現點意外,如果兩個設備突然斷掉了連接,一般我們還是希望它們能夠再次連接的,這里就得要看硬件和IOS程序里對於連接斷開的處理代碼了
6):當然,外設和IOS端也可以主動發起斷開連接的請求
下面我們看看IOS端具體的代碼流程和基本的含義,你只需要跟着我的順序走就可以了,以下順序符合IOS BLE4.0聯機的流程:
在你的藍牙類的初始化里面,先實例化一下中心設備管理器
_centralMan = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
代碼很簡單吧,只需要一行就可以了,主要是要設置委托的對象,至於是否要加入某個隊列,就看你的需求了,還記得我們藍牙類里面繼承了CBCentralManagerDelegate ,CBPeripheralDelegate兩個協議吧,你可以跟進去看一下里面的協議方法,我就不多說了,先講第一個需要實現的協議方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
這個方法主要是來檢查IOS設備的藍牙硬件的狀態的,比如說你的設備不支持藍牙4.0,或者說你的設備的藍牙沒有開啟,沒有被授權什么的,一般是在你確定了你的IOS設備的藍牙處於打開的情況下,你才應該執行掃描的動作,[_centralMan scanForPeripheralsWithServices:Nil options:Nil]; 我記得我之前沒有檢查藍牙的狀態就直接掃描了,雖然項目可以運行,但是控制台給出了一大堆的警告什么的,看着很煩人,那么我們執行掃描的動作之后,如果掃描到外設了,就會自動回調下面的協議方法了
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
這個方法里面的信息量可是比較大啊,看那個CBPeripheral,你在這個方法里面你就已經可以獲取它的名稱了可以使用peripheral.name來獲取名稱,當然,這里還可以獲取設備的一個廣播名稱 NSString *CBName=[advertisementData valueForKeyPath:CBAdvertisementDataLocalNameKey]; 這兩個名稱一般是不一樣的,除非你們硬件那邊把人家寫一樣了,當然還有其他的很多信息,具體看代碼吧;在這個回調里我們可以獲取外設的廣播包信息,當然我們就可以根據廣播包的信息符合不符合你們的自定義的協議,符合就可以發起連接請求了,這樣可以避免連接上其它一些自己不希望連接上的外設;當然你應該也看到了這里還有一個RSSI的值,這里是廣播包附帶過來的,根據這個值我們可以大概估計出廣播設備里我們IOS設備有多遠,如果信號太弱距離過遠,我們是不是就不考慮連接這個外設了;這里我們假設廣播包數據符合條件,我們發起了連接請求[_centralMan connectPeripheral:_peripheral options:Nil];那么如果連接成功,就會回調下面的協議方法了:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
如果你能回調到上面這個方法,說明你已經成功連接外設了,具體你可以打個斷點試試,既然連接已經成功,我們就該考慮是不是要停止中心管理設備的掃描動作了,要不然在你和已經連接好的外設進行數據溝通時,如果又有一個外設進行廣播且符合你的連接條件,那么你的IOS設備就會也去連接這個設備(因為IOS BLE4.0是支持一對多連接的),導致數據的混亂。所以我們在這個方法里面停止掃描動作:[_centralMan stopScan]; 現在是時候讓我們的第二個實例對像出場了 CBPeripheral,這里需要注意的是,記得要設置CBPeripheral的委托對象 _peripheral.delegate =self;為了以后使用方便,我們還是一次性讀出外設的所有服務UUID:[_peripheral discoverServices:nil];這樣一旦我們讀取到外設的相關服務UUID就會回調下面的方法:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
接着在這個方法里面我們還可以接着往里面剝,讀取某個CBService 里面的特征值UUID:[peripheral discoverCharacteristics:nil forService:s];同上,如果我們成功讀取某個特征值UUID,就會回調下面的方法:
- (void)peripheral:(CBPeripheral *)peripheraldidDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
能進入到這個方法,說明你已經讀取到某個特征值UUID了,到達這一步,我們基本上也完成對外設的層層剝離了,接下來就是一些對某個特征值的讀寫操作了,當然我們首先要找到我們需要寫數據的特征值UUID了,這里我們可以簡單地通過循環來找出外設含有的某個特征值UUID:
for(int i=0; i < service.characteristics.count; i++) {
CBCharacteristic *c = [service.characteristicsobjectAtIndex:i];
if ([[c UUID] isEqual:[CBUUIDUUIDWithString:@"FFF1"]])
{ //你的動作}
}
在這里我們需要強調一下,由於特征值UUID是外設設定的,並且特征值UUID的屬性properties有很多種,比如Read,WriteWithoutResponse,Write,Notify等等,因此你需要根據你們之間的協議還確定對某個特征值UUID怎么處理了,當然你也可以自己讀出來這個特征值UUID的屬性:c.properties,這里我們假設一個特征值UUID :“FFF1”的屬性為WriteWithoutResponse,我們往里面寫一個數據
Byte dataArr[2];
dataArr[0]=0xaa; dataArr[1]=0xbb;
NSData * myData = [NSData dataWithBytes:dataArr length:2];
[_peripheral writeValue:myData forCharacteristic:c type:CBCharacteristicWriteWithResponse];
這樣我們就完成了一次向外設寫數據,但是如果外設的特征值UUID是Read,怎么辦呢?比如說獲取外設的電池電量信息(注:這個屬於標准服務里面自帶),服務UUID:“180F”,特征值UUID:“2A19”,我們可以這樣寫:
[_peripheral readValueForCharacteristic:cbc];
cbc就是指特征值為“2A19”的特征值對象,當然還有屬性為Notify的,方法和這個類似:[peripheralsetNotifyValue:YES forCharacteristic:c];
那么我們設置讀這個特征值對象的值了,怎么接收它發過來的值呢,其實如果外設有特征值對象的值更新了,會自動回調下面的方法:
- (void)peripheral:(CBPeripheral *)peripheraldidUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
在這個回調函數里面我們就可以根據characteristic的值來判斷是哪個特征值UUID發送過來的值了,那個peripheral主要是用來判斷是哪個外設發送的值,主要用於藍牙一對多的情況,如果是一對一的,基本沒用。那么我們再說一下怎么輸出接收到的值,就以接收電池電量來說吧,看代碼:
if ([[characteristic UUID] isEqual:[CBUUID UUIDWithString:@"2A19"]]){
const unsigned char *hexBytesLight = [characteristic.valuebytes];
NSString * battery = [NSStringstringWithFormat:@"%02x", hexBytesLight[0]];
NSLog(@"batteryInfo:%@",battery);
}
其實,外設向IOS這邊發數據,都會自動回調上面的那個方法,因此你接收外設發送過來的數據,也只能在這個方法里面,通過不同的特征值判斷是哪個UUID發送過來的,解析出數據再執行相應的動作就可以了,好了到這里我們應該很清楚IOS和外設是怎么進行數據溝通的了,如果還想深入就靠你自己了,當然我們最后再說一點,如果連接上的兩個設備突然斷開了,程序里面會自動回調下面的方法:
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
在這個方法里面,我們可以做一些補救措施,比如回連,我們就可以在這個方法里寫上:
[_centralMan connectPeripheral:_peripheral options:nil];
從這個方法里,我們還可以輸出連接斷開的原因:我們這樣寫:error.code,跟進去看會發現錯誤的原因有很多種,比如說CBErrorConnectionTimeout,CBErrorOperationCancelled,CBErrorPeripheralDisconnected,你可以根據錯誤的原因來進行不同的動作,好了,其實還有很多的回調方法,大家可以去看蘋果官方的文檔,這里只列出了一些常用的而已,水平有限,有不到之處,還請指證!如果大家還有什么疑問可以加我 QQ:631965569;額,最好還是發郵件到QQ郵箱吧!
最近兩天寫了一個demo,放在Github上面了,地址是:https://github.com/Allen-L/BLE4.0-Demo-IOS.git ,模仿lightblue寫的,由於各種原因,沒有寫完全,可供參考,轉載http://m.blog.csdn.net/blog/liang_ke_ke/39251951