藍(lán)牙開發(fā)-中心設(shè)備開發(fā)

藍(lán)牙開發(fā)
中心設(shè)備開發(fā)
外設(shè)開發(fā)

概念理解

中心設(shè)備: 主動(dòng)連接其他藍(lán)牙設(shè)備的一方,可以通過(guò)藍(lán)牙指令操作藍(lán)牙設(shè)備或讀取藍(lán)牙設(shè)備數(shù)據(jù)。如手機(jī)藍(lán)牙耳機(jī),手機(jī)就是中心設(shè)備。
外設(shè): 提供藍(lán)牙功能的設(shè)備,可以被中心設(shè)備連接。如手機(jī)藍(lán)牙耳機(jī),耳機(jī)就是外設(shè)。
服務(wù): 外設(shè)提供的功能模塊。一個(gè)外設(shè)可以有多個(gè)服務(wù),一個(gè)服務(wù)可以有一個(gè)或多個(gè)特征值。
特征值: 中心設(shè)備通過(guò)修改特征值或讀取特征值來(lái)使用外設(shè)的提供的服務(wù)。如手機(jī)可以通過(guò)修改音量對(duì)應(yīng)的特征值的大小來(lái)實(shí)現(xiàn)控制耳機(jī)的音量。

服務(wù)的UUID: 服務(wù)的UUID是服務(wù)的標(biāo)識(shí),外設(shè)上的每個(gè)服務(wù)都有一個(gè)UUID,但不同的外設(shè)可能會(huì)有相同uuid的服務(wù)

特征的UUID: 特征的UUID是特征的標(biāo)識(shí),外設(shè)上的各服務(wù)的每個(gè)特征都有一個(gè)UUID,但不同的外設(shè)可能會(huì)有相同uuid的特征

中心設(shè)備的開發(fā)流程

image

中心設(shè)備開發(fā) API

引入藍(lán)牙框架

#import <CoreBluetooth/CoreBluetooth.h>

創(chuàng)建中心設(shè)備

//創(chuàng)建中心設(shè)備管理者
self.centralManager = [[CBCentralManager alloc] initWithDelegate:_sharedInstance queue:nil options:nil];

///藍(lán)牙狀態(tài)變化回調(diào)。
///回調(diào)時(shí)機(jī):
///1、創(chuàng)建中心設(shè)備管理者后,系統(tǒng)會(huì)請(qǐng)求獲取藍(lán)牙權(quán)限,用戶同意或拒絕后,回調(diào)該方法
///2、用戶開啟或關(guān)閉藍(lán)牙后回調(diào)該方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    switch (central.state) {
        case CBManagerStateUnknown:
        case CBManagerStateUnsupported:
            NSLog(@"模擬器不支持藍(lán)牙調(diào)試");
        case CBManagerStateUnauthorized:
            NSLog(@"未開啟藍(lán)牙權(quán)限");
            break;
        case CBManagerStatePoweredOff:
            NSLog(@"藍(lán)牙處于關(guān)閉狀態(tài)");
            break;
        case CBManagerStateResetting:
            NSLog(@"藍(lán)牙處于重置狀態(tài)");
            break;
        case CBManagerStatePoweredOn:
            NSLog(@"藍(lán)牙已開啟");
            //掃描外設(shè)
            [self scanForPeripherals];
            break;
    }

    //轉(zhuǎn)為了block回調(diào)
    if (self.centralManagerStateDidUpdate) {
        self.centralManagerStateDidUpdate(central.state);
    }
}

掃描外設(shè)

外設(shè)的屬性:名字、信號(hào)強(qiáng)度、狀態(tài)、服務(wù)、標(biāo)志符。


//掃描外設(shè)
//services為nil,會(huì)掃描所有的外設(shè)
//serviceUUIDs 是[CBUUID UUIDWithString:@"uuid1"]的集合
//已連接狀態(tài)的外設(shè)是不會(huì)被掃描到的
//因?yàn)橛锌赡懿煌庠O(shè)的服務(wù)的UUID相同,所以是無(wú)法指定掃描某個(gè)外設(shè)的
[self.centralManager scanForPeripheralsWithServices:serviceUUIDs options:nil];

///掃描到外設(shè)回調(diào)
///外設(shè)減少不會(huì)回調(diào)該方法,所以掃描到的藍(lán)牙列表可能過(guò)段時(shí)間后有些外設(shè)是連不上的??梢哉{(diào)用掃描外設(shè)方法重新掃描。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
    NSString *name = peripheral.name;
    NSLog(@"發(fā)現(xiàn)了外設(shè):%@", name);
    
    /** 添加外設(shè)到外設(shè)列表中,根據(jù)外設(shè)的identifier.UUIDString避免重復(fù)添加
        為什么可以作為唯一標(biāo)識(shí)符:
        1、不同的中心設(shè)備掃描同一臺(tái)外設(shè)identifier.UUIDString是不一樣的
        2、同一臺(tái)中心設(shè)備每次掃描同一臺(tái)外設(shè),identifier.UUIDString是不變的 */
    if (name.length > 0) {
        BOOL exist = NO;
        for (CBPeripheral *p in self.peripheralList) {
            if ([p.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) {
                exist = YES;
                break;
            }
        }
        if (!exist) {
            [self.peripheralList addObject:peripheral];
        }
    }
    
    //轉(zhuǎn)為了block回調(diào)
    self.peripheralsDidUpdate(self.peripheralList);
}

連接外設(shè)

//peripheral是從peripheralList中找到的自己需要的一臺(tái)外設(shè)
[self.centralManager connectPeripheral:peripheral options:nil];


///外設(shè)連接失敗回調(diào)
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
   //轉(zhuǎn)為了block回調(diào)
    self.connectStateDidUpdate(peripheral.state);
    NSLog(@"外設(shè)連接失敗");
}

//外設(shè)與中心設(shè)備連接成功回調(diào)
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    //保存當(dāng)前連接的外設(shè)
    self.curPeripheral = peripheral;
    //停止掃描
    [self.centralManager stopScan];
    //設(shè)置代理
    peripheral.delegate = self;
    //根據(jù)服務(wù)的UUID來(lái)尋找服務(wù), 傳nil則尋找該外設(shè)的所有服務(wù)
    [peripheral discoverServices:serviceUUIDs];
    //轉(zhuǎn)為了block回調(diào)
    self.connectStateDidUpdate(peripheral.state);
    NSLog(@"外設(shè)連接成功");
}

掃描服務(wù)

 //根據(jù)服務(wù)的UUID來(lái)尋找服務(wù), 傳nil則尋找該外設(shè)的所有服務(wù)
    [peripheral discoverServices:serviceUUIDs];


///外設(shè)發(fā)現(xiàn)服務(wù)回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
   if (error) {
        //錯(cuò)誤處理
        return;
    }
    for (CBService *service in peripheral.services) {
        NSLog(@"發(fā)現(xiàn)的服務(wù):%@", service.UUID);
        //尋找該服務(wù)的特征,characteristicUUIDs為nil,找的是該服務(wù)的所有的特征
        [peripheral discoverCharacteristics:characteristicUUIDs forService:service];
    }
}

掃描特征

 //尋找該服務(wù)的特征,characteristicUUIDs為nil,找的是該服務(wù)的所有的特征
 [peripheral discoverCharacteristics:characteristicUUIDs forService:service];

///外設(shè)發(fā)現(xiàn)特征回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    if (error) {
        return;
    }

    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"發(fā)現(xiàn)的特征:%@", characteristic.UUID.UUIDString);
        /** 訂閱支持通知的特征 */
        if (characteristic.properties & CBCharacteristicPropertyNotify) {
            [self.curPeripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
        //記錄下所有的特征
        [self.characteristics addObject:characteristic];
    }
}

///訂閱結(jié)果回調(diào)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"訂閱失敗");
    }
    if (characteristic.isNotifying) {
        NSLog(@"訂閱成功");
    } else {
        NSLog(@"取消訂閱");
    }
}

使用服務(wù)---修改特征值

//修改UUID為characteristicUUIDStr的特征的值,自定義方法
- (void)writeData:(NSData *)data characteristicUUIDStr:(NSString *)characteristicUUIDStr callback:(void (^)(BOOL))callback {
    //記錄寫入回調(diào)block
    self.writeCallback = callback;
    ///判斷該特征是否在掃描到的特征中存在
    BOOL existUUID = NO;
    for (CBCharacteristic *character in self.characteristics) {
        /** 找到對(duì)應(yīng)UUID的特征進(jìn)行寫操作 */
        if ([character.UUID.UUIDString isEqualToString:characteristicUUIDStr]) {
            existUUID = YES;
            /** 如果該特征有寫權(quán)限,進(jìn)行寫權(quán)限 */
            if (character.properties & CBCharacteristicPropertyWrite) {
                [self.curPeripheral writeValue:data forCharacteristic:character type:CBCharacteristicWriteWithResponse];
            }else if (character.properties & CBCharacteristicPropertyWriteWithoutResponse) {
                [self.curPeripheral writeValue:data forCharacteristic:character type:CBCharacteristicWriteWithoutResponse];
            }
            /** 如果該特征沒(méi)有寫權(quán)限,回調(diào)寫入失敗 */
            else {
                self.writeCallback(NO);
            }
            
        }
    }
    
    /** 如果沒(méi)有找到該UUID對(duì)應(yīng)的特征,回調(diào)寫入失敗 */
    if (!existUUID) {
        NSLog(@"未找到UUID為%@的特征", characteristicUUIDStr);
        self.writeCallback(NO);
    }
}

//寫入特征值結(jié)果回調(diào)。如果特征支持寫入回調(diào)才會(huì)調(diào)用
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
    if (error) {
        self.writeCallback(NO);
        return;
    }
    NSLog(@"寫入成功");
    //回調(diào)block
    self.writeCallback(YES);
}

使用服務(wù)---讀取特征值&訂閱特征通知

///主動(dòng)讀取某特征的值,自定義方法
- (void)readValueForCharacteristicUUIDStr:(NSString *)UUIDStr {
    ///從特征列表中找到特征,并讀取
    for (CBCharacteristic *character in self.characteristics) {
        if ([character.UUID.UUIDString isEqualToString:UUIDStr]) {
            //判斷該特征是否可讀
            if (character.properties & CBCharacteristicPropertyRead) {
                //讀取該特征值
                [self.curPeripheral readValueForCharacteristic:character];
            }
        }
    }
}

//訂閱某特征
 [self.curPeripheral setNotifyValue:YES forCharacteristic:characteristic];

//1、讀取到特征值回調(diào)
//2、收到特征通知也會(huì)回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    // 拿到外設(shè)發(fā)送過(guò)來(lái)的 NSData 數(shù)據(jù)
    NSData *data = characteristic.value;
    //轉(zhuǎn)為了block回調(diào)
    if (self.valueForCharacteristicDidUpdate) {
        self.valueForCharacteristicDidUpdate(characteristic.UUID.UUIDString, data);
    }
    NSLog(@"接收到外設(shè)數(shù)據(jù)回調(diào):%@", data);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 以太坊在昨日白盤受到下行通道中軌壓制在下區(qū)間短暫運(yùn)行,最終行情在晚間一根大陽(yáng)成功突破中軌壓制開啟了短期的反彈走勢(shì),...
    0efb64704c91閱讀 262評(píng)論 0 0
  • 2020.12.11 大象焦點(diǎn)中一期 方方第67天 給自己:今天頭腦中一直盤旋著運(yùn)氣兩字,昨天老公說(shuō)他這幾年運(yùn)氣怎...
    方方1976閱讀 196評(píng)論 0 1
  • 年輕人,未來(lái)還有無(wú)限的可能。生活也許還會(huì)給你一記又一記的重?fù)?dān),也許還會(huì)讓你孤獨(dú)好一陣子,但總歸是要走出來(lái)的,
    小寶燈兒閱讀 136評(píng)論 0 0
  • 沒(méi)有人可以回到過(guò)去重新活過(guò),但你我都可以從現(xiàn)在開始,界定我們明天未來(lái)的模樣
    天長(zhǎng)地久盼三生閱讀 135評(píng)論 0 0
  • 他是我在大學(xué)軍訓(xùn)時(shí)認(rèn)識(shí)的,也是一種機(jī)遇巧合。我們一起玩的一個(gè)女生覺(jué)得他朋友有點(diǎn)帥,想相互認(rèn)識(shí)一下,她又不太好意...
    奶茶要三分糖閱讀 175評(píng)論 0 0

友情鏈接更多精彩內(nèi)容