淺談iOS中的藍(lán)牙技術(shù)(二) -- CoreBluetooth

在上篇文章中我們提到了 iOS 開(kāi)發(fā)中,使用GameKit 框架實(shí)現(xiàn)相同網(wǎng)絡(luò)的 iOS 設(shè)備之間傳輸數(shù)據(jù).但是 GameKit 框架在 iOS7 之后就過(guò)時(shí)了,那么本文將介紹另一種關(guān)于藍(lán)牙的框架 CoreBluetooth.

CoreBluetooth

簡(jiǎn)介
  • CoreBlueTooth.framework
    使用 CoreBluetooth 要求設(shè)備必須要支持藍(lán)牙4.0(一般也叫BLE<Bluetooth low energy>)。,并且手機(jī)型號(hào)必須是iPhone4以上,也就是至少4s手機(jī).使用CoreBluetooth進(jìn)行開(kāi)發(fā)可實(shí)現(xiàn)與第三方設(shè)備的數(shù)據(jù)交互.
CoreBluetooth.h

上圖是CoreBluetooth 的頭文件,在這個(gè)框架中最核心的兩個(gè)東西就是 Central (中心)和 Peripheral(外設(shè)).

圖片來(lái)自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系
CoreBluetooth 中的對(duì)象模型
  • CBCentralManager
    它用于管理發(fā)現(xiàn)或遠(yuǎn)程連接外圍設(shè)備(CBPeripheral對(duì)象),包括掃描,發(fā)現(xiàn)和外圍設(shè)備連接到廣告.

  • CBPeripheralManager
    它用于管理服務(wù)在當(dāng)?shù)匕l(fā)表外圍設(shè)備的通用屬性配置文件數(shù)據(jù)庫(kù)和通知這些服務(wù)的中心設(shè)備(CBCentral對(duì)象).

  • CBPeripheral
    它代表遠(yuǎn)程周邊設(shè)備,你的應(yīng)用程序的中央管理器(CBCentralManager)的一個(gè)實(shí)例——發(fā)現(xiàn)廣告或當(dāng)前連接.

  • CBCentral
    它代表遠(yuǎn)程中央設(shè)備連接到一個(gè)應(yīng)用程序在本地設(shè)備上實(shí)現(xiàn)外圍的作用.

  • CBService 和 CBMutableService
    代表外圍的業(yè)務(wù)數(shù)據(jù)收集,相關(guān)行為完成一個(gè)功能或特性的設(shè)備(或部分設(shè)備).CBMutableService類(lèi)繼承CBService類(lèi)添加寫(xiě)訪問(wèn)的所有屬性.

  • CBCharacteristic及其子類(lèi)CBMutableCharacteristic
    代表進(jìn)一步外圍的信息服務(wù).CBMutableCharacteristic對(duì)象代表當(dāng)?shù)奶厣苓叺姆?wù)

  • CBUUID
    它的實(shí)例代表128位全局唯一標(biāo)識(shí)符(uuid)屬性中使用藍(lán)牙低能量溝通,比如周邊的服務(wù),特點(diǎn),和特征描述符

  • CBATTRequest
    它代表屬性協(xié)議(ATT)讀寫(xiě)請(qǐng)求從遠(yuǎn)程中央設(shè)備(CBCentral對(duì)象)

實(shí)現(xiàn)通訊

中心管理設(shè)計(jì)模式

  • First

導(dǎo)入 CoreBluetooth 頭文件,創(chuàng)建中心管理者屬性和外設(shè)屬性,并遵守中心管理者和外設(shè)的協(xié)議.

First
  • 建立中心控制器
    懶加載創(chuàng)建中心控制器, 創(chuàng)建的時(shí)候設(shè)置代理為當(dāng)前控制器,Peripheral Manager將跑在主線程中。如果你想用不同的線程做更加復(fù)雜的事情,你需創(chuàng)建一個(gè)隊(duì)列(queue)并將它放在這兒
- (CBCentralManager *)getCManager{
    if (!_cManager) {
        _cManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:nil];
    }
    return _cManager;
}
  • 更新?tīng)顟B(tài) (只要中心管理者初始化,就會(huì)觸發(fā)此代理方法)
    中心管理器的狀態(tài)


    中心管理器
  • 掃描外設(shè)
// 該方法用于告訴Central Manager,要開(kāi)始尋找一個(gè)指定的服務(wù)了.不能在state非ON的情況下對(duì)我們的中心管理者進(jìn)行操作,scanForPeripheralsWithServices方法中 sercices為空則會(huì)掃描所有的設(shè)備.
 - (void)centralManagerDidUpdateState:(CBCentralManager *)central{

    switch (central.state) {
        case CBCentralManagerStateUnknown:
            NSLog(@"中心管理器狀態(tài)未知");
            break;
        case CBCentralManagerStateResetting:
            NSLog(@"中心管理器狀態(tài)重置");
            break;
        case CBCentralManagerStateUnsupported:
            NSLog(@"中心管理器狀態(tài)不被支持");
            break;
        case CBCentralManagerStateUnauthorized:
            NSLog(@"中心管理器狀態(tài)未被授權(quán)");
            break;
        case CBCentralManagerStatePoweredOff:
            NSLog(@"中心管理器狀態(tài)電源關(guān)閉");
            break;
        case CBCentralManagerStatePoweredOn:
        {
            NSLog(@"中心管理器狀態(tài)電源開(kāi)啟");
            // 在中心管理者成功開(kāi)啟后開(kāi)始搜索外設(shè)
            
            [self.cManager scanForPeripheralsWithServices:nil options:nil]; 
            // 搜索成功之后,會(huì)調(diào)用我們找到外設(shè)的代理方法 sercices為空則會(huì)掃描所有的設(shè)備
            // - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; //找到外設(shè)
            
        }
            break;
        default:
            break;
    }
}
  • 過(guò)濾外設(shè),進(jìn)行連接
    如果找到了設(shè)備,則代理會(huì)調(diào)用該方法,過(guò)濾外設(shè)
/*
 *  @param central              中央管理器提供此更新
 *  @param peripheral           一個(gè)外設(shè)對(duì)象
 *  @param advertisementData    一個(gè)包含任何廣播和掃描響應(yīng)數(shù)據(jù)的字典。
 *  @param RSSI                 RSSI(Received Signal Strength Indicator)是接收信號(hào)的強(qiáng)度指示
 *
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
    
    if ([peripheral.name hasPrefix:@"XXX"] && (ABS(RSSI.integerValue) > 35)) {
        // 標(biāo)記我們的外設(shè),延長(zhǎng)他的生命周期
        self.peripheral = peripheral;
        // 進(jìn)行連接
        [self.cManager connectPeripheral:self.peripheral options:nil];
    }

}
  • 連接狀態(tài)(成功,失敗,斷開(kāi))
    中心管理者連接外設(shè)成功,連接成功之后,可以進(jìn)行服務(wù)和特征的發(fā)現(xiàn)
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"%@連接成功",peripheral.name);
    // 獲取外設(shè)的服務(wù)
    // 設(shè)置外設(shè)的代理
    self.peripheral.delegate = self;
    // 外設(shè)發(fā)現(xiàn)服務(wù),傳nil代表不過(guò)濾
    // 這里會(huì)觸發(fā)外設(shè)的代理方法 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    [self.peripheral discoverServices:nil];
}

// 外設(shè)連接失敗
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"%@連接失敗",peripheral.name);
}

// 丟失連接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"%@斷開(kāi)連接",peripheral.name);
}
  • 發(fā)現(xiàn)服務(wù)以及內(nèi)部的特征
// 發(fā)現(xiàn)外設(shè)的服務(wù)后調(diào)用的方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    // 判斷沒(méi)有失敗
    if (error) {
        NSLog(@"error:%@",error.localizedDescription);
        return;
    }
    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

// 發(fā)現(xiàn)服務(wù)后,讓設(shè)備再發(fā)現(xiàn)服務(wù)內(nèi)部的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    // 遍歷特征
    for (CBCharacteristic *characteristic in service.characteristics) {
        // 獲取特征對(duì)應(yīng)的描述
        [peripheral discoverDescriptorsForCharacteristic:characteristic];
        // 獲取特征的值
        [peripheral readValueForCharacteristic:characteristic];
    }
}
  • 更新特征
// 更新特征的描述的值的時(shí)候會(huì)調(diào)用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error
{
    [peripheral readValueForDescriptor:descriptor];
}

// 更新特征的value的時(shí)候會(huì)調(diào)用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    for (CBDescriptor *descriptor in characteristic.descriptors) {
        [peripheral readValueForDescriptor:descriptor];
    }
}
  • 外設(shè)寫(xiě)數(shù)據(jù)到特征中
    NS_OPTIONS類(lèi)型的枚舉特征屬性,可以是多個(gè)值,這里需要注意,由于枚舉屬性是NS_OPTIONS,所以一個(gè)枚舉可能對(duì)應(yīng)多個(gè)類(lèi)型,所以在判斷的時(shí)候不能用 = ,而應(yīng)該用包含&
NS_OPTIONS類(lèi)型的特征屬性
- (void)peripheral:(CBPeripheral *)peripheral didWriteData:(NSData *)data forCharacteristic:(nonnull CBCharacteristic *)characteristic
{
    
    // 
    if (characteristic.properties & CBCharacteristicPropertyWrite) {
    // 下面方法中參數(shù)的意義依次是:寫(xiě)入的數(shù)據(jù) 寫(xiě)給哪個(gè)特征 通過(guò)此響應(yīng)記錄是否成功寫(xiě)入
        [peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
    }
}
  • 通知的訂閱和取消訂閱
    一般來(lái)說(shuō)這兩個(gè)方法要根據(jù)產(chǎn)品需求來(lái)確定寫(xiě)在什么地方
- (void)peripheral:(CBPeripheral *)peripheral regNotifyWithCharacteristic:(nonnull CBCharacteristic *)characteristic
{
    // 外設(shè)為特征訂閱通知
    [peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
- (void)peripheral:(CBPeripheral *)peripheral CancleRegNotifyWithCharacteristic:(nonnull CBCharacteristic *)characteristic
{
    // 外設(shè)取消訂閱通知
    [peripheral setNotifyValue:NO forCharacteristic:characteristic];
}
  • 斷開(kāi)連接
- (void)dismissConentedWithPeripheral:(CBPeripheral *)peripheral
{
    // 停止掃描
    [self.cManager stopScan];
    // 斷開(kāi)連接
    [self.cManager cancelPeripheralConnection:peripheral];
}

END

外設(shè)管理設(shè)計(jì)模式,跟中心管理設(shè)計(jì)模式類(lèi)似,就不贅述,需要遵守 外設(shè)管理器協(xié)議 和中心協(xié)議,設(shè)置代理,遵守其中的方法.代碼地址:https://github.com/coderqiao/CoreBluetooth.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評(píng)論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,413評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,449評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,165評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,559評(píng)論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,781評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,084評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,278評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,495評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,927評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,172評(píng)論 1 291
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,010評(píng)論 3 396
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,241評(píng)論 2 375

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