iOS 藍(lán)牙開發(fā)(框架集成和數(shù)據(jù)交互)

最近幾個(gè)月都在做藍(lán)牙的項(xiàng)目跃闹,趁現(xiàn)在有空次乓,就把在藍(lán)牙開發(fā)過程中的心得和踩過的坑給記錄下來疆液,分享給大家颓鲜,避免大家在藍(lán)牙開發(fā)過程中能避免踩相同的坑表窘。

網(wǎng)上關(guān)于藍(lán)牙開發(fā)的原理知識(shí)都有了,這里就不在重復(fù)累贅甜滨,只簡單說一下實(shí)現(xiàn)過程邏輯:

  1. 首先需要在info.plist添加請求藍(lán)牙功能的權(quán)限“NSBluetoothPeripheralUsageDescription”
  2. 創(chuàng)建全局藍(lán)牙管理類
  3. 用藍(lán)牙管理類進(jìn)行掃描
  4. 從掃描的結(jié)果中找到你的藍(lán)牙外設(shè)蚊丐,進(jìn)行手動(dòng)或自動(dòng)連接
  5. 連接成功收掃描外設(shè)的服務(wù)和特征,每個(gè)服務(wù)都包含一個(gè)特征的數(shù)組艳吠,這些不同的特征就是用來跟藍(lán)牙外設(shè)進(jìn)行數(shù)據(jù)通信的對象麦备,特征一般分兩種,一種是可讀characteristic_notify昭娩,一種是可讀寫characteristic_readWrite凛篙。這兩個(gè)特征藍(lán)牙硬件工程師會(huì)告訴你,你只需要找到這兩個(gè)特征用私有變量保存起來即可栏渺,然后把可讀特征characteristic_notify設(shè)置為notify呛梆,你就能收到藍(lán)牙外設(shè)發(fā)送過來的實(shí)時(shí)數(shù)據(jù)。characteristic_readWrite特征就是用來發(fā)送數(shù)據(jù)了磕诊。

接下來看一下代碼實(shí)現(xiàn):
一. 定義全局藍(lán)牙管理類

  1. 這個(gè)類負(fù)責(zé)藍(lán)牙的掃描填物,停止掃描,連接霎终,斷開連接
    它有一個(gè)代理CBCentralManagerDelegate滞磺,必須實(shí)現(xiàn)它的centralManagerDidUpdateState方法,用來返回藍(lán)牙功能打開和關(guān)閉的狀態(tài)
self.centralManage = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];

這句代碼實(shí)現(xiàn)后會(huì)自動(dòng)判斷你的設(shè)備有沒有打開藍(lán)牙功能莱褒,如果沒有回自動(dòng)彈框讓你去設(shè)置里打開開關(guān)击困。

  1. 然后我們在centralManagerDidUpdateState代理中實(shí)現(xiàn)藍(lán)牙掃描:
#pragma mark - CBCentralManagerDelegate

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    if (central.state == CBCentralManagerStatePoweredOn) {
        [self.centralManage scanForPeripheralsWithServices:nil options:nil];
    }else{
        [MBProgressHUD wj_showPlainText:@"藍(lán)牙已關(guān)閉" view:nil];
    }
    
}
  1. 掃描結(jié)果會(huì)在didDiscoverPeripheral返回
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
    NSData *data = advertisementData[@"kCBAdvDataManufacturerData"];
    DLog(@"廣播數(shù)據(jù)---------------%@",data);
   if ([peripheral.name isEqualToString:xxx]) {
        // 這里找到你匹配的外設(shè)名字
       [self.centralManage connectPeripheral:peripheral options:nil];
    }
    
    NSString *dataStr = [FGDataChangeTool hexadecimalString:data];
    if ([dataStr isEqualToString:xxx]) {
        // 這里找到外設(shè)給你發(fā)的廣播數(shù)據(jù)匹配
         [self.centralManage connectPeripheral:peripheral options:nil];
    }
}

注意:上面實(shí)例代碼中的兩個(gè)if判斷條件為非必須,這個(gè)需要根據(jù)你的業(yè)務(wù)需求广凸,比如可以根據(jù)你的外設(shè)名稱進(jìn)行匹配(不靠譜阅茶,因?yàn)槊挚勺儯⒒蛘咦屇愕乃{(lán)牙硬件工程師發(fā)一個(gè)標(biāo)識(shí)碼用來區(qū)分谅海。
隨便說一句 *由于iOS藍(lán)牙開發(fā)不能直接獲取藍(lán)牙的Mac地址脸哀,如果需要用到Mac地址作為外設(shè)的唯一標(biāo)識(shí)碼,只能讓硬件工程師把Mac地址用廣播發(fā)送過來扭吁,字段還是用“ kCBAdvDataManufacturerData” *
4.1. 連接后撞蜂,會(huì)走didConnectPeripheral代理方法盲镶,在這個(gè)方法里面需要把peripheral藍(lán)牙外設(shè)對象設(shè)置一下代理CBPeripheralDelegate,并且實(shí)現(xiàn)對服務(wù)的掃描:

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{

    // 4.對外設(shè)進(jìn)行掃描
    peripheral.delegate = self;
    [peripheral discoverServices:nil];

}

4.2. 斷開連接,用戶主動(dòng)斷開或者代碼執(zhí)行斷開連接都會(huì)執(zhí)行這個(gè)方法:

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
    [MBProgressHUD hideAllHUDsForView:kKeyWindow animated:YES];
    [MBProgressHUD wj_showError:@"藍(lán)牙已斷開連接" toView:nil];
}

4.3 連接失斄律恪:

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
    [MBProgressHUD hideAllHUDsForView:kKeyWindow animated:YES];
    [MBProgressHUD wj_showError:@"連接失敗,請重試" toView:nil];
}

二. 前面4.1我們進(jìn)行對服務(wù)掃描后徒河,如果成功沒什么問題就會(huì)走CBPeripheralDelegate代理方法didDiscoverServices發(fā)現(xiàn)服務(wù),返回的服務(wù)是多個(gè)送漠,每個(gè)服務(wù)又包含多個(gè)特征顽照,這里只需要把所有的服務(wù)遍歷然后掃描所有的特征即可。

#pragma mark - CBPeripheralDelegate
// 外設(shè)發(fā)現(xiàn)服務(wù)回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error{
        // 保存私有變量peripheral
        self.peripheral = peripheral;
        for (CBService *service in peripheral.services) {
        //4. 1對外設(shè)掃描到的服務(wù)進(jìn)行特征掃描
        [self.peripheral discoverCharacteristics:nil forService:service];
        
    }
    
    [self beginRefresh];
   //停止掃描
    [self.centralManager stopScan];
}

掃描特征后會(huì)在代理方法返回所有的特征:

// 外設(shè)發(fā)現(xiàn)特征回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    
    
    for (CBCharacteristic *characteristic in service.characteristics) {
        DLog(@"特征---------%@",characteristic);
        if ([characteristic.UUID.UUIDString isEqualToString:@"你的讀寫特征值"]) {
            self.characteristic_write = characteristic;
            
        }
        else if ([characteristic.UUID.UUIDString isEqualToString:@"你的只讀特征值"]){
            self.characteristic_notify = characteristic;
            [self.peripheral setNotifyValue:YES forCharacteristic:self.characteristic_notify];
        }

        //電池特征值闽寡,注意打印的時(shí)候是顯示Battery Level而不是2A19代兵,但是代碼中需要用2A19進(jìn)行匹配。
        else if ([characteristic.UUID.UUIDString isEqualToString:@"2A19"]){
            self.characteristic_Battery = characteristic;
            // 設(shè)置readValueForCharacteristic才能獲得電量值
            [peripheral readValueForCharacteristic:characteristic];
           // 設(shè)置setNotifyValue才能收到電量改變的通知
            [self.peripheral setNotifyValue:YES forCharacteristic:characteristic];
       }
    }

}

三. 藍(lán)牙發(fā)送的所有數(shù)據(jù)都會(huì)在這個(gè)代理方法didUpdateValueForCharacteristic返回一個(gè)NSData對象:

// 當(dāng)特征的值發(fā)生變化時(shí)回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {

    for (CBService *sever in peripheral.services) {
        for (CBCharacteristic *character in sever.characteristics) {
            if ([character.UUID.UUIDString isEqualToString:@"2A19"]) {
                DLog(@"電量信息----------%@",character);
            }
        }
    }

        // 其他數(shù)據(jù)處理
        [self dealWithData];
    
}

四. 上面的代碼基本已經(jīng)完成大部分的需求了爷狈,包括藍(lán)牙的連接植影,斷開,掃描設(shè)備涎永,保存特征值思币,如果需要進(jìn)行下一步的藍(lán)牙數(shù)據(jù)通訊,只需要用writeValue方法進(jìn)行發(fā)送即可羡微。發(fā)送后的同樣在上面的didUpdateValueForCharacteristic代理方法接收數(shù)據(jù)谷饿。

Byte *bytes = (Byte *)malloc(6);
    bytes[0] = 0xaa;
    bytes[1] = data1;
    bytes[2] = data2;
    bytes[3] = (Byte)(((bytes[1] & 0xff) + (bytes[2] & 0xff)) / 3);
    bytes[4] = (Byte)(((bytes[1] & 0xff) + (bytes[2] & 0xff)) % 3);
    bytes[5] = 0xdc;
NSData *sendData = [NSData dataWithBytes:sendByte length:6];
[self.peripheral writeValue:sendData forCharacteristic:self.characteristic type:0];

五. 在藍(lán)牙手法數(shù)據(jù)過程中需要對數(shù)據(jù)進(jìn)行解析,數(shù)據(jù)都是16進(jìn)制的NSData對象妈倔,這些數(shù)據(jù)可不是像HTTP通訊那樣簡單傳個(gè)Json對象給你解析就完事了博投,需要按照硬件工程師給你的協(xié)議進(jìn)行一一解析,解碼成看得懂的數(shù)據(jù)盯蝴。這里發(fā)一份我在工作中常用到的一些解析工具類毅哗,包括一些進(jìn)制間的轉(zhuǎn)化。喜歡的點(diǎn)個(gè)star捧挺,這是給我最好的鼓勵(lì)??
iOSBlueTool

最后:我在第二篇介紹藍(lán)牙固件升級(jí)的相關(guān)操作

iOS 藍(lán)牙開發(fā)(固件升級(jí)&空中升級(jí))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末虑绵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子松忍,更是在濱河造成了極大的恐慌蒸殿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸣峭,死亡現(xiàn)場離奇詭異,居然都是意外死亡酥艳,警方通過查閱死者的電腦和手機(jī)摊溶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來充石,“玉大人莫换,你說我怎么就攤上這事。” “怎么了拉岁?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵坷剧,是天一觀的道長。 經(jīng)常有香客問我喊暖,道長惫企,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任陵叽,我火速辦了婚禮狞尔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巩掺。我一直安慰自己偏序,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布胖替。 她就那樣靜靜地躺著研儒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪独令。 梳的紋絲不亂的頭發(fā)上端朵,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音记焊,去河邊找鬼逸月。 笑死,一個(gè)胖子當(dāng)著我的面吹牛遍膜,可吹牛的內(nèi)容都是我干的碗硬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瓢颅,長吁一口氣:“原來是場噩夢啊……” “哼恩尾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挽懦,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤翰意,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后信柿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冀偶,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年渔嚷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了进鸠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡形病,死狀恐怖客年,靈堂內(nèi)的尸體忽然破棺而出霞幅,到底是詐尸還是另有隱情,我是刑警寧澤量瓜,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布司恳,位于F島的核電站,受9級(jí)特大地震影響绍傲,放射性物質(zhì)發(fā)生泄漏扔傅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一唧取、第九天 我趴在偏房一處隱蔽的房頂上張望铅鲤。 院中可真熱鬧,春花似錦枫弟、人聲如沸邢享。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骇塘。三九已至,卻和暖如春韩容,著一層夾襖步出監(jiān)牢的瞬間款违,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工群凶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留插爹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓请梢,卻偏偏與公主長得像赠尾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子毅弧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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