iOS 藍(lán)牙開發(fā)

Bluetooth.png

藍(lán)牙介紹

本文要介紹的CoreBluetooth,專門用于與BLE設(shè)備通訊荆永。并且現(xiàn)在很多藍(lán)牙設(shè)備都支持4.0,4.0以其低功耗著稱成艘,所以一般也叫BLE(Bluetooth Low Energy)盆顾,所以也是在iOS比較推薦的一種開發(fā)方法类咧。

基本概念

image.png
  • 中心設(shè)備(Central):發(fā)起連接丐谋,用來(lái)掃描周圍藍(lán)牙硬件的設(shè)備还惠;
  • 周邊設(shè)備(Peripheral):被連接的設(shè)備饲握;
  • 服務(wù)(Service):特征和關(guān)系的集合,它封裝了設(shè)備的一部分的行為;
  • 特征(Characteristic):可以理解成一個(gè)Service模塊具體提供哪些服務(wù)蚕键,特征會(huì)有一個(gè)value救欧,一般我們向藍(lán)牙設(shè)備寫入數(shù)據(jù)、從藍(lán)牙設(shè)備讀取數(shù)據(jù)就是這個(gè)value;
  • UUID:區(qū)分不同服務(wù)和特征的唯一標(biāo)識(shí)锣光,使用該字端我們可以獲取我們想要的服務(wù)或者特征笆怠。
  • 廣播:外部設(shè)備不停的散播的藍(lán)牙信號(hào),讓中心設(shè)備可以掃描到誊爹,也是我們開發(fā)中接收數(shù)據(jù)的入口蹬刷。

CoreBluetooth介紹

image.png

在CoreBluetooth中有兩個(gè)主要的部分,Central和Peripheral,CBPeripheralManager 作為周邊設(shè)備频丘。CBCentralManager作為中心設(shè)備办成。所有可用的iOS設(shè)備可以作為周邊設(shè)備(Peripheral)也可以作為中心設(shè)備(Central),但不可以同時(shí)既是周邊設(shè)備也是中心設(shè)備搂漠。

Central

作為 Central 模式使用的操作步驟 :

  1. 創(chuàng)建一個(gè) Central Manager迂卢;
  2. 發(fā)現(xiàn)周邊設(shè)備;
  3. 發(fā)現(xiàn)周邊設(shè)備的服務(wù)及特征桐汤;
  4. 讀取特征值或?qū)χ付ㄌ卣髟O(shè)置監(jiān)聽而克;
  5. 設(shè)置可寫入的特征值;
  6. 讀取特征的描述Descriptor惊科;

創(chuàng)建了一個(gè) tableView 來(lái)掃描到的周邊設(shè)備的名字拍摇、信號(hào)強(qiáng)度等;

導(dǎo)入CoreBluetooth

#import <CoreBluetooth/CoreBluetooth.h>

創(chuàng)建一個(gè)管理者centralManager馆截,給當(dāng)前類簽訂協(xié)議<cbcentralmanagerdelegate>:</cbcentralmanagerdelegate>

_centralManager=[[CBCentralManager alloc] initWithDelegate:self queue:nil];

實(shí)現(xiàn) centralManagerDidUpdateState: 監(jiān)測(cè)藍(lán)牙狀態(tài)變化:

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {

    switch (central.state) {
        case CBCentralManagerStatePoweredOff:
        {
            NSLog(@"藍(lán)牙功能未開啟");
        }
            break;
        case CBCentralManagerStateUnsupported:
        {
            NSLog(@"不支持藍(lán)牙功能");
        }
            break;
        case CBCentralManagerStatePoweredOn:
        {
            NSLog(@"藍(lán)牙功能已開啟");
        }
            break;
        default:
            break;
    }
}

掃描周邊設(shè)備

[_centralManager scanForPeripheralsWithServices:nil options:nil];

實(shí)現(xiàn)協(xié)議方法獲取掃描到的周邊設(shè)備

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    __block BOOL isContain = NO;
    SABLEInfoObject *bleInfo = [[SABLEInfoObject alloc] init];
    bleInfo.name = peripheral.name ?: @"--";
    bleInfo.rssi = RSSI;
    bleInfo.deviceUUID = peripheral.identifier.UUIDString;
    bleInfo.servicesCount = peripheral.services.count;
    [self.dataArray enumerateObjectsUsingBlock:^(SABLEInfoObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj.deviceUUID isEqualToString:bleInfo.deviceUUID]) {
            [self.dataArray replaceObjectAtIndex:idx withObject:bleInfo];
            isContain = YES;
        }
    }];

    if (!isContain) {
        [self.dataArray addObject:bleInfo];
        [self.peripheralList addObject:peripheral];
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:kDiscoverPeripheralRefreshNotificationName object:self.dataArray];
}

在創(chuàng)建的 tableView 中實(shí)時(shí)顯示 peripheral 的信號(hào)強(qiáng)度充活、名稱、服務(wù)個(gè)數(shù)蜡娶,選取某一個(gè)發(fā)起連接:

[_centralManager connectPeripheral:self.peripheralList[index] options:nil];

實(shí)現(xiàn)協(xié)議方法 centralManager:didConnectPeripheral 在連接成功時(shí)去獲取當(dāng)前設(shè)備包含的所有服務(wù):

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@"%@ Connect Success", peripheral.name);
    peripheral.delegate = self;
    [peripheral discoverServices:nil];
}

協(xié)議方法 didDiscoverServices混卵,遍歷服務(wù),查詢服務(wù)下的特征:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
    NSLog(@"%@ Discover Services Success", peripheral.name);
    [peripheral.services enumerateObjectsUsingBlock:^(CBService * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"Service UUID: %@", obj.UUID.UUIDString);
        [peripheral discoverCharacteristics:nil forService:obj];
    }];
}

協(xié)議方法 didDiscoverCharacteristicsForService窖张,遍歷當(dāng)前服務(wù)下的特征幕随,讀取特征值或者監(jiān)測(cè)特征值,還可獲取特征的Descriptor:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    [service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"\nservicesUUID: %@\nCharacteristicUUID: %@", service.UUID.UUIDString, obj);
        //讀取特征
//        if ([obj.UUID.UUIDString isEqualToString:@"2A19"]) {
//            [peripheral readValueForCharacteristic:obj];
//        }
//
//        if ([service.UUID.UUIDString isEqualToString:@"180A"]) {
//            [peripheral readValueForCharacteristic:obj];
//        }
        [peripheral readValueForCharacteristic:obj];
        if (obj.properties == CBCharacteristicPropertyWrite) {
            NSLog(@"writeValue");
            [peripheral writeValue:[@"testValue " dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:obj type:CBCharacteristicWriteWithResponse];
        }

        //監(jiān)測(cè)特征
//        [peripheral setNotifyValue:YES forCharacteristic:obj];

        // 外設(shè)發(fā)現(xiàn)特征的描述
        [peripheral discoverDescriptorsForCharacteristic:obj]; 
    }];
}

讀取或者監(jiān)測(cè)的特征值可在協(xié)議方法 didUpdateValueForCharacteristic 查看,如下宿接,讀取電量值赘淮,2A19為電量值特征的UUID:

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    if ([characteristic.UUID.UUIDString isEqualToString:@"2A19"]) {
        NSString *hexStr = [SADataConvertManager convertDataToHexStr:characteristic.value];
        NSUInteger batteryLevel = [[SADataConvertManager convertHexStrToDecimalString:hexStr] integerValue];
        NSLog(@"\nCharacteristicUUID: %@\nCharacteristicValue: %@", characteristic.UUID, [@(batteryLevel).stringValue stringByAppendingString:@"%"]);
    }
}

發(fā)現(xiàn)的 Descriptor 需要實(shí)現(xiàn)協(xié)議方法讀仍肌:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    [characteristic.descriptors enumerateObjectsUsingBlock:^(CBDescriptor * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"\nCharacteristicUUID: %@\nDescriptor: %@", characteristic.UUID, obj.value);
    }];
}

周邊設(shè)備讀取信號(hào)強(qiáng)度:

[peripheral readRSSI];  //讀取信號(hào)強(qiáng)度

實(shí)現(xiàn)協(xié)議方法 didReadRSSI 獲取讀取的信號(hào)強(qiáng)度:

- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error {
    NSLog(@"信號(hào)強(qiáng)度:%@", RSSI.stringValue);
}

寫入特征值:

if (obj.properties == CBCharacteristicPropertyWrite) {
            [peripheral writeValue:[@"testValue " dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:obj type:CBCharacteristicWriteWithResponse];
        }

Peripheral

作為 Peripheral 模式使用的操作步驟 :

  1. 創(chuàng)建一個(gè) Peripheral Manager;
  2. 為外設(shè)添加服務(wù)梢卸;
  3. 發(fā)送廣播

創(chuàng)建一個(gè)管理者peripheralManager走诞,給當(dāng)前類簽訂協(xié)議<cbperipheralmanagerdelegate>:</cbperipheralmanagerdelegate>

_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

實(shí)現(xiàn) centralManagerDidUpdateState: 獲取藍(lán)牙狀態(tài)變化:

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    switch (peripheral.state) {
        case CBPeripheralManagerStatePoweredOn:
        {
            NSLog(@"藍(lán)牙已打開");
            [self setupPeripheral];
        }
            break;
        case CBPeripheralManagerStatePoweredOff:
            NSLog(@"藍(lán)牙已關(guān)閉");
            break;
        case CBPeripheralManagerStateUnsupported:
            NSLog(@"不支持藍(lán)牙");
            break;

        default:
            break;
    }
}

為藍(lán)牙添加服務(wù)

- (void)setupPeripheral {
    CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"] properties:CBCharacteristicPropertyRead value:[@"123456789" dataUsingEncoding:NSUTF8StringEncoding] permissions:CBAttributePermissionsReadable];
    CBMutableService *myService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"12AB"] primary:YES];
    myService.characteristics = @[myCharacteristic];
    [_peripheralManager addService:myService];
}

添加服務(wù)成功執(zhí)行代理方法 didAddService :

- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
    [service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"\nserviceUUID: %@\nCharacteristicUUID: %@\nCharacteristicValue: %@", service.UUID.UUIDString, obj.UUID.UUIDString, [[NSString alloc] initWithData:obj.value encoding:NSUTF8StringEncoding]);
    }];
}

打印結(jié)果:
serviceUUID: 12AB
CharacteristicUUID: 71DA3FD1-7E10-41C1-B16F-4430B506CDE7
CharacteristicValue: 123456789

發(fā)送廣播:

- (void)setupPeripheralAdvertising {
    if (!_peripheralManager.isAdvertising) {
        CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"] properties:CBCharacteristicPropertyRead value:[@"123456789" dataUsingEncoding:NSUTF8StringEncoding] permissions:CBAttributePermissionsReadable];
        CBMutableService *myService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"12AB"] primary:YES];
        myService.characteristics = @[myCharacteristic];
        [_peripheralManager addService:myService];
        [_peripheralManager startAdvertising:@{
                                               CBAdvertisementDataLocalNameKey : @"測(cè)試設(shè)備",
                                               CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"12AB"]]
                                               }];
        [SAAudioSound sa_playPassSound];
    }else {
        [_peripheralManager stopAdvertising];
        [SAAudioSound sa_playWarningSound];
    }
}

廣播發(fā)送開啟成功后會(huì)走協(xié)議方法:peripheralManagerDidStartAdvertising

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error {
    NSLog(@"正在廣播");
}

當(dāng)連接到中心設(shè)備時(shí)便可接收讀或?qū)懙恼?qǐng)求,接收到請(qǐng)求時(shí)會(huì)走相應(yīng)的協(xié)議方法:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {

}

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {

}

附:

GATT服務(wù)UUID: https://www.bluetooth.com/specifications/gatt/services

GATT特征UUID: https://www.bluetooth.com/specifications/gatt/characteristics

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛤高,一起剝皮案震驚了整個(gè)濱河市蚣旱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌戴陡,老刑警劉巖塞绿,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異恤批,居然都是意外死亡异吻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門开皿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涧黄,“玉大人篮昧,你說(shuō)我怎么就攤上這事赋荆。” “怎么了懊昨?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵窄潭,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我酵颁,道長(zhǎng)嫉你,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任躏惋,我火速辦了婚禮幽污,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘簿姨。我一直安慰自己距误,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布扁位。 她就那樣靜靜地躺著准潭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪域仇。 梳的紋絲不亂的頭發(fā)上刑然,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音暇务,去河邊找鬼泼掠。 笑死怔软,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的择镇。 我是一名探鬼主播爽雄,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沐鼠!你這毒婦竟也來(lái)了挚瘟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤饲梭,失蹤者是張志新(化名)和其女友劉穎乘盖,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憔涉,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡订框,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兜叨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穿扳。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖国旷,靈堂內(nèi)的尸體忽然破棺而出矛物,到底是詐尸還是另有隱情,我是刑警寧澤跪但,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布履羞,位于F島的核電站,受9級(jí)特大地震影響屡久,放射性物質(zhì)發(fā)生泄漏忆首。R本人自食惡果不足惜被环,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一糙及、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧筛欢,春花似錦浸锨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至漠酿,卻和暖如春冯凹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工宇姚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匈庭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓浑劳,卻偏偏與公主長(zhǎng)得像阱持,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子魔熏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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