iOS藍(lán)牙開發(fā)-1-通識(shí)

個(gè)人認(rèn)為iOS藍(lán)牙開發(fā)算是相對(duì)偏門的領(lǐng)域,做iOS藍(lán)牙相關(guān)的開發(fā)也有一段時(shí)間了,在此寫下藍(lán)牙相關(guān)的東西,一則方便日后查閱,二則給后來之人一點(diǎn)參考。
關(guān)于藍(lán)牙是什么就不多做解釋了,百度一搜一大把。iOS藍(lán)牙開發(fā)。藍(lán)牙開發(fā)有兩種:

    1.手機(jī)做中心 - CBCentralManager、外部硬件做外設(shè) - CBPeripheral
    2.外部硬件做中心 - CBCentralManager、手機(jī)做外設(shè) - CBPeripheral

一般情況下開發(fā),都是以手機(jī)(App)作為中心,硬件設(shè)備(智能硬件、手環(huán)之類的)作為外設(shè)。這里只記錄這種情況。
最開始主要有兩個(gè)概念:服務(wù)(CBService)、特征(CBCharacteristic)。
服務(wù):每個(gè)設(shè)備都會(huì)有一些服務(wù),每個(gè)服務(wù)里面都會(huì)有一些特征,特征就是具體鍵值對(duì),提供數(shù)據(jù)的地方。每個(gè)特征屬性分為這么幾種:讀,寫,通知這么幾種方式。
特征:讀、寫數(shù)據(jù)等。
畫了個(gè)簡(jiǎn)圖,沒有美術(shù)天賦。(“、、、”表示重復(fù)的省略)


外設(shè)-服務(wù)-特性結(jié)構(gòu)圖.png

剛接觸藍(lán)牙的人,估計(jì)會(huì)不太理解服務(wù)和特性是什么東西,我剛剛開始也沒搞明白。我最開始接觸藍(lán)牙是從大學(xué)玩單片機(jī)的藍(lán)牙2.0開始的(蘋果是4.0),學(xué)電子的應(yīng)該知道。那時(shí)候只需要設(shè)置AT指令,調(diào)用收發(fā)數(shù)據(jù)的API即可。
我的理解是:
【1】服務(wù)是用來提供傳輸數(shù)據(jù)通道的。比如,一個(gè)人體數(shù)據(jù)監(jiān)測(cè)儀,服務(wù)A用于傳輸心率、服務(wù)B用于傳輸血壓、服務(wù)C用于傳輸腦電波。。。嗯,是的,這么打個(gè)比方吧。
【2】服務(wù)A中(傳輸心率那個(gè))有:特性1->用于App讀取數(shù)據(jù)、特性2->用于App發(fā)送指令或者數(shù)據(jù)到硬件,特性3->用于讀取硬件的mac地址。這樣就將這些功能模塊(心率、血壓、腦電波等)及其接口(讀取、發(fā)送等)都很好的分開了,我理解成面向?qū)ο蟮慕怦睢?br> 下面是已手機(jī)App作為中心的大致步驟:

-[1]創(chuàng)建管理中心,并設(shè)置其代理。

-[2]判斷手機(jī)藍(lán)牙狀態(tài)。(是否打開、是否支持、是否授權(quán)),這里可以做提示用戶打開手機(jī)藍(lán)牙之類的。。。

-[3]掃描外設(shè)。每次掃描到一個(gè)藍(lán)牙外設(shè),都會(huì)通過管理中心的代理方法將外設(shè)相關(guān)信息回調(diào)過來(外設(shè)名字、信號(hào)值、廣播等),此時(shí)可以自定義一個(gè)對(duì)象用于保存這個(gè)外設(shè)。然后將這個(gè)模型添加進(jìn)數(shù)組中,利用自定義tableview顯示出來,就和網(wǎng)絡(luò)請(qǐng)求展示數(shù)據(jù)類似。

-[4]連接外設(shè)。

-[5]連接外設(shè)成功,尋找需要的服務(wù)(比如我只需要監(jiān)測(cè)心率,不需要發(fā)腦電波,那就只需要找到監(jiān)測(cè)心率那個(gè)服務(wù)就好了)。

-[6]尋找到需要的服務(wù)后,接下來 -> 尋找需要的特性(讀數(shù)據(jù)、寫指令、讀mac等需要的特性)。找到的這些特性,都將其作為屬性保存起來,后續(xù)發(fā)送數(shù)據(jù)等會(huì)需要用到。

-[7]訂閱讀數(shù)據(jù)的特性。這個(gè)特性是這樣的:每次硬件傳輸數(shù)據(jù)過來時(shí),都會(huì)走這里,所以需要訂閱它,以便能收到硬件的數(shù)據(jù)。

-[8]發(fā)送自己的指令,注意:每次最多只能發(fā)送20個(gè)字節(jié)。

-[9]有以上步驟,收發(fā)數(shù)據(jù)基本OK了,還有的就是寫完善功能:自動(dòng)連接,斷線重連,過濾不需要的設(shè)備,按指定mac地址連接等等。

以下是相關(guān)代碼:

///創(chuàng)建管理中心
    _manager = [[CBCentralManager alloc] initWithDelegate:self
                                                    queue:nil];///默認(rèn)在主線程中
///初始化數(shù)組,用于存放搜索到的外設(shè)
    _allPeripheral = [NSMutableArray array];
///開始掃描外設(shè)
    [_manager scanForPeripheralsWithServices:nil options:nil];

以下是管理中心代理方法:

#pragma mark - - - - - -管理中心代理 - - - - - - - - -

#pragma mark 【1】監(jiān)測(cè)藍(lán)牙狀態(tài)
///這里可以做相應(yīng)處理,當(dāng)藍(lán)牙狀態(tài): 未打開->提示用于打開藍(lán)牙
///                            其他->提示其他
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state){
        case CBManagerStateUnknown:
            NSLog(@"藍(lán)牙-未知");
            break;
        case CBManagerStateUnsupported:
            NSLog(@"藍(lán)牙-不支持");
            break;
        case CBManagerStateUnauthorized:
            NSLog(@"藍(lán)牙-未授權(quán)");
            break;
        case CBManagerStatePoweredOff:{///藍(lán)牙關(guān)閉
            NSLog(@"藍(lán)牙-已關(guān)閉");
        }
            break;
        case CBManagerStateResetting:
            NSLog(@"藍(lán)牙-復(fù)位");
            break;
        case CBManagerStatePoweredOn:{///藍(lán)牙打開
            NSLog(@"藍(lán)牙-已打開");
        }
            break;
    }
}

#pragma mark 【2】發(fā)現(xiàn)外部設(shè)備
- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI{
    BLEModel *model = [[BLEModel alloc] initWithPeripheral:peripheral rssi:RSSI advertisementData:advertisementData];
    for (BLEModel *saveModel in _allPeripheral) {
        if ([saveModel.peripheral isEqual:peripheral]) {
            return;
        }
    }
    [_allPeripheral addObject:model];
    [_tableView reloadData];
}

#pragma mark 【3】連接外部藍(lán)牙設(shè)備
- (void)connectToPeripheral:(CBPeripheral *)peripheral{
    if (!peripheral) {
        return;
    }
    [_manager connectPeripheral:peripheral options:nil];
}

#pragma mark 【4】連接外部藍(lán)牙設(shè)備成功
- (void)centralManager:(CBCentralManager *)central
  didConnectPeripheral:(CBPeripheral *)peripheral{
    ///連接成功
    NSLog(@"連接成功,開始尋找服務(wù)和特征");
    [peripheral discoverServices:nil];
}

#pragma mark 【5】連接外部藍(lán)牙設(shè)備失敗
- (void)centralManager:(CBCentralManager *)central
didFailToConnectPeripheral:(CBPeripheral *)peripheral
                 error:(NSError *)error{
    ///這里可以嘗試重連
    [_manager connectPeripheral:peripheral options:nil];
}

#pragma mark 【6】藍(lán)牙外設(shè)連接斷開,自動(dòng)重連
- (void)centralManager:(CBCentralManager *)central
didDisconnectPeripheral:(CBPeripheral *)peripheral
                 error:(NSError *)error{
    ///當(dāng)連接斷開時(shí),會(huì)走這個(gè)回調(diào),可以做重連等
}

以下是外設(shè)代理方法:

#pragma mark - - - - - -外設(shè)代理 - - - - - - - - - 
#pragma mark 【1】尋找藍(lán)牙服務(wù)
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    
    if(error){
        NSLog(@"外圍設(shè)備尋找服務(wù)過程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
    }
    
    CBUUID *serviceUUID=[CBUUID UUIDWithString:kServiceUUID];
    for (CBService *service in peripheral.services) {
        
        if([service.UUID isEqual:serviceUUID]){
            [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kNotifyUUID],[CBUUID UUIDWithString:kWriteUUID],[CBUUID UUIDWithString:kReadMacUUID]] forService:service];
        }
    }
}

#pragma mark 【2】尋找藍(lán)牙服務(wù)中的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    if (error) {//報(bào)錯(cuò)直接返回退出
        NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
        return;
    }
    
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyUUID]]){///訂閱讀數(shù)據(jù),執(zhí)行此行代碼,每次收到數(shù)據(jù)時(shí)才會(huì)走下面 “【8】直接讀取特征值被更新后”
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            self.readCharacteristic = characteristic;
        }
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kWriteUUID]]) {
            ///保存寫數(shù)據(jù)的特征,用于給硬件設(shè)備發(fā)送數(shù)據(jù)
            self.writeCharacteristic = characteristic;
        }
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kReadMacUUID]]) {
            ///這個(gè)特征,在我目前的項(xiàng)目中是讀取藍(lán)牙m(xù)ac地址的
            self.readMACCharacteristic = characteristic;
        }
    }
    if (_readCharacteristic && _writeCharacteristic) {
        NSLog(@"連接成功");///此時(shí)才算真正連接成功,因?yàn)榇藭r(shí)才有讀、寫特征,可以正常進(jìn)行數(shù)據(jù)交互
    }
}

#pragma mark 【3】直接讀取特征值被更新后、即收到訂閱的那個(gè)特征的數(shù)據(jù)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    
    if (error) {
        NSLog(@"更新特征值時(shí)發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
        return;
    }
    NSLog(@"收到數(shù)據(jù) -- %@",characteristic.value);
    ///這里就是處理數(shù)據(jù)了
}

然后是tableView代理

#pragma mark - tableViewDelegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _allPeripheral.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    BLETableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BLETableViewCell" forIndexPath:indexPath];
    cell.model = _allPeripheral[indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    BLEModel *bleModel = _allPeripheral[indexPath.row];
    bleModel.peripheral.delegate = self;
    [_manager connectPeripheral:bleModel.peripheral options:nil];
}

代碼中的幾個(gè)宏定義:

#pragma mark - 藍(lán)牙服務(wù)及屬性
#define         kServiceUUID                                        @"0001"
#define         kWriteUUID                                          @"0002"
#define         kNotifyUUID                                         @"0003"
#define         kReadMacUUID                                        @"0004"

為什么是0001、0002等,是有原因的,這里介紹個(gè)App,叫LightBlue,可以查看到藍(lán)牙設(shè)備的特性。當(dāng)然這些也可以找硬件工程師拿到,或者自己查DataSheet也可以查到。

IMG_1632.PNG

再介紹個(gè)串口調(diào)試助手,CoolTerm。能實(shí)時(shí)調(diào)試藍(lán)牙模塊。
代碼地址:https://github.com/chan106/BLEDemo.git

最后編輯于
?著作權(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)容

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