iOS Bluetooth開發(fā)基礎

藍牙知識簡介

藍牙硬件

1、藍牙4.0:包括藍牙4.0以上的版本税肪,iOS 6 以上才可以使用御板,不一定需要MFI認證锥忿,因為4.0以上是低功耗,現(xiàn)在大部分的藍牙設備都是4.0以上怠肋,本文主要講藍牙4.0及以上設備開發(fā)敬鬓,使用CoreBluetooth 框架。

2笙各、藍牙2.0:又稱經(jīng)典藍牙钉答,2.0的藍牙硬件模塊必須經(jīng)過蘋果的MFI認證,否則無法進行藍牙開發(fā)杈抢,使用ExternalAccessory 框架数尿。

藍牙基礎知識

MFI ---- 是apple公司 “Made for iOS”的英文縮寫,是蘋果公司對其授權配件廠商生產(chǎn)的外置配件的一種標識使用許可惶楼。有MFI標識的設備質(zhì)量普遍較好右蹦。

BLE ---- buletouch low energy,藍牙4.0以上設備因為低耗電歼捐,所以也叫做BLE何陆。

central,peripheral ---- 中心豹储,外設,發(fā)起連接的一端叫central贷盲,被連接的設備為perilheral。

service剥扣,Characteristic巩剖,Description ---- 服務,特征钠怯,描述佳魔。都可以從設備讀取到。一個設備可以有多個服務晦炊,一個服務可以有多個特征鞠鲜,特征的權限分為寫write,讀read刽锤,通知notify等幾種镊尺,每個服務都有相應的描述朦佩〔⑺迹可類比為:服務wervice=類Class,特征Characteristic=屬性property语稠,Description描述=注釋宋彼。

CoreBluetooth框架主要包含兩種業(yè)務模式:中心模式和外設模式弄砍,前者是用手機連接其他藍牙設備,后者是手機當做藍牙設備被連接输涕。本人最近只用到中心模式音婶。

連接藍牙流程:

1、創(chuàng)建中心對象

2莱坎、掃描周圍藍牙設備

3衣式、連接藍牙設備

4、掃描藍牙設備的服務和特征

5檐什、和藍牙設備的數(shù)據(jù)交互

6碴卧、斷開連接

中心模式的應用場景:例如用手機上的APP去連接藍牙設備(手環(huán)等)。藍牙設備的服務和特征一般有硬件工程師定義好乃正,以及每個特征的屬性(讀住册,寫,通知)瓮具,類似于后臺定義接口荧飞,APP端調(diào)用。讀屬性用于從藍牙設備獲取數(shù)據(jù)名党,寫屬性用于往藍牙設備傳數(shù)據(jù)叹阔,通知是訂閱一個屬性后,一旦屬性的值發(fā)生改變就會通知APP兑巾,類似于OC中觀察者模式条获。本人做的項目藍牙設備傳輸數(shù)據(jù)都是通過通知實現(xiàn)的。

具體實現(xiàn)步驟

1蒋歌、工程導入CoreBluetooth.framework.
2帅掘、在類里面導入頭文件 CoreBluetooth/CoreBluetooth.h,添加代理堂油,創(chuàng)建中心對象
#import <CoreBluetooth/CoreBluetooth.h>
 @interface ViewController : UIViewController<CBCentralManagerDelegate,CBPeripheralDelegate>
 @interface ViewController (){
        //系統(tǒng)藍牙設備管理對象修档,可以把他理解為主設備,通過他可以去掃描和連接外設
        CBCentralManager *manager;
        //用于保存被發(fā)現(xiàn)設備
        NSMutableDictionary *discoverPeripherals;
    }

這里一定要添加CBCentralManagerDelegate,CBPeripheralDelegate 2個代理府框,藍牙大部分的回調(diào)都是通過代理方法實現(xiàn)的吱窝。

    //存放持有發(fā)現(xiàn)的設備,如果不持有設備會導致CBPeripheralDelegate方法不能正確回調(diào)
    discoverPeripherals = [[NSMutableDictionary alloc] init];
    //初始化并設置委托,最好一個線程的參數(shù)可以為nil迫靖,默認會就main線程
    manager= [[CBCentralManager alloc] initWithDelegate:self queue:nil];

3院峡、掃描周圍藍牙設備
//此代理方法查看手機藍牙狀態(tài)
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{

    switch (central.state) {
        case CBCentralManagerStateUnknown:
            break;
        case CBCentralManagerStateResetting:
            break;
        case CBCentralManagerStateUnsupported:
            break;
        case CBCentralManagerStateUnauthorized:
            break;
        case CBCentralManagerStatePoweredOff:
            break;
        case CBCentralManagerStatePoweredOn:
            //只有藍牙開啟正常,才開始掃描周圍的外設
            /*
             第一個參數(shù)可以設置搜索條件系宜,nil就是掃描周圍所有的外設照激,掃描到外設后會進入
             - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
             */
            [manager scanForPeripheralsWithServices:nil options:nil];
            
            break;
        default:
            break;
    }
}

只有狀態(tài)為CBCentralManagerStatePoweredOn才能進行后續(xù)掃描,要不然掃描后不會走代理方法

//掃描到設備會進入此方法
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
  
      NSLog(@"當掃描到設備名稱:%@",peripheral.name);
//RSSI 藍牙信號強度
//這里可以根據(jù)設備名稱去連接你的設備
}
4盹牧、連接設備
//掃描到設備會進入此方法(還是上面這個方法??)
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
  
      NSLog(@"當掃描到設備名稱:%@",peripheral.name);
//RSSI 藍牙信號強度
//這里可以根據(jù)設備名稱去連接你的設備

    //接下連接我們的測試設備俩垃,如果你沒有設備励幼,可以下載一個app叫l(wèi)ightbule的app去模擬一個設備
    //這里自己去設置下連接規(guī)則,我設置的是P開頭的設備
    if ([peripheral.name isEqualToString:@"XXXX"]){

        //找到的設備必須持有它口柳,否則CBCentralManager中也不會保存peripheral苹粟,那么CBPeripheralDelegate中的方法也不會被調(diào)用!跃闹!
        //我開始就掉進這個坑??????嵌削,可以用個容器裝著或者全局對象(你也可以用數(shù)組,看自己需求來)
        [discoverPeripherals setObject:peripheral forKey:@"peripheral"];

         /*
         連接成功望艺,失敗掷贾,斷開會進入各自的委托
         - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設成功的委托
         - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設連接失敗的委托
         - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開外設的委托
         */

        //連接設備
        [manager connectPeripheral:peripheral options:nil];


     }
}

//連接到Peripherals-成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@">>>連接到名稱為(%@)的設備-成功",peripheral.name);
    //設置外設的代理,不設置的話連接成功不會走后續(xù)的方法了
    [peripheral setDelegate:self];

    //掃描外設Services荣茫,成功后會進入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    [peripheral discoverServices:nil];
}

//連接到Peripherals-失敗
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@">>>連接到名稱為(%@)的設備-失敗,原因:%@",[peripheral name],[error localizedDescription]);
}

//Peripherals 斷開連接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@">>>外設連接斷開連接 %@: %@\n", [peripheral name], [error localizedDescription]);
    

持有設備想帅!持有設備!持有設備啡莉!重要的是說三遍8圩肌!咧欣!

5浅缸、掃描服務、特征
//掃描到Services
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
      NSLog(@">>>掃描到服務:%@",peripheral.services);
    if (error)
    {
        NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
        return;
    }
    
    for (CBService *service in peripheral.services) {
        NSLog(@"服務UUID = %@",service.UUID);
        //掃描每個service的Characteristics魄咕,掃描到后會進入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
        [peripheral discoverCharacteristics:nil forService:service];
    }
    
}
//掃描到Characteristics
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    if (error)
    {
        NSLog(@"error Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
        return;
    }
    
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        //這里根據(jù)服務和特征的UUID去進行數(shù)據(jù)交互
            //訂閱特征
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            //往設備寫寫數(shù)據(jù)  xxx為NSData類型數(shù)據(jù)
            [peripheral writeValue:xxx forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]

    }

    //獲取Characteristic的值衩椒,讀到數(shù)據(jù)會進入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    for (CBCharacteristic *characteristic in service.characteristics){
        {
            [peripheral readValueForCharacteristic:characteristic];
        }
    }
    
    //搜索Characteristic的Descriptors,讀到數(shù)據(jù)會進入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    for (CBCharacteristic *characteristic in service.characteristics){
        [peripheral discoverDescriptorsForCharacteristic:characteristic];
    }

}

上面2個方法掃描主要是讀取服務和特征的UUID哮兰,好進行下一步操作毛萌。服務和特征都有UUID,UUID好比是類的名稱喝滞,屬性名稱阁将,特征的值就像屬性的值

6、讀取數(shù)據(jù)
//獲取的charateristic的值
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    //打印出characteristic的UUID和值
    //!注意右遭,value的類型是NSData做盅,具體開發(fā)時,會根據(jù)外設協(xié)議制定的方式去解析數(shù)據(jù)

 NSLog(@"設備數(shù)據(jù) == %@窘哈,%@吹榴,%@",characteristic.service.UUID,characteristic.UUID,characteristic.value)

}

上面就是讀取特征具體的值,根據(jù)協(xié)議去解析數(shù)據(jù)滚婉,并轉(zhuǎn)化為你需要的數(shù)據(jù)類型图筹。

下面2個方法感覺用處不大,實際開發(fā)中满哪,每個特征的的作用開發(fā)文檔會寫明的婿斥,或者硬件工程師會告訴你的

//搜索到Characteristic的Descriptors
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    

    //打印出Characteristic和他的Descriptors
    //NSLog(@"特征 uuid:%@",characteristic.UUID);
    for (CBDescriptor *d in characteristic.descriptors) {
        NSLog(@"特征描述 uuid:%@",d.UUID);
    }
}

//獲取到Descriptors的值
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{
    //打印出DescriptorsUUID 和value
    //這個descriptor都是對于characteristic的描述,一般都是字符串哨鸭,所以這里我們轉(zhuǎn)換成字符串去解析
    NSLog(@"characteristic uuid:%@  value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
}
//寫入數(shù)據(jù)回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    if (error) {
        
        NSLog(@"寫入數(shù)據(jù)失敗");
        return;
    }
        NSLog(@"寫入數(shù)據(jù)成功 %@",characteristic); 
}
//訂閱回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    
    if (error) {
        NSLog(@"訂閱特征 %@失敗",characteristic);
        return;
    }
    NSLog(@"訂閱特征 %@成功",characteristic);
}
//停止掃描并斷開連接---自己寫的不是代理??
-(void)disconnectPeripheral:(CBCentralManager *)centralManager
                 peripheral:(CBPeripheral *)peripheral{
    //停止掃描
    [centralManager stopScan];
    //斷開連接
    [centralManager cancelPeripheralConnection:peripheral];
}

我自己做的藍牙設備都是通過訂閱某個特征從而獲取數(shù)據(jù)的民宿。

其中需要用到UUID的比較,留下個CBUUID轉(zhuǎn)NSString的方法

- (NSString *)representativeString
{
    NSData *data = [self data];
    NSUInteger bytesToConvert = [data length];
    const unsigned char *uuidBytes = [data bytes];
    NSMutableString *outputString = [NSMutableString stringWithCapacity:16];
    for (NSUInteger currentByteIndex = 0; currentByteIndex < bytesToConvert; currentByteIndex++)
    {
        switch (currentByteIndex)
        {
            case 3:
            case 5:
            case 7:
            case 9:[outputString appendFormat:@"%02x-", uuidBytes[currentByteIndex]]; break;
            default:[outputString appendFormat:@"%02x", uuidBytes[currentByteIndex]];
        }
    }
    return outputString;
}

第一次做藍牙設備的開發(fā)像鸡,遇到過幾個坑活鹰,總結下:
1、沒有持有CBCentralManager對象只估,導致調(diào)用連接方法后不走是否成功的代理方法志群。
2、忘記設置外設peripheral的代理了蛔钙,導致不走后面掃描服務的代理方法
3锌云、由于這個項目是幫別人做的,所有沒有文檔吁脱,只有一份安卓源碼和安卓同事整理出來的一份簡單文檔桑涎,往哪個特征寫數(shù)據(jù),哪個特征讀數(shù)據(jù)兼贡,都不清楚攻冷,把我搞得半死。其中有設備是往同一個特征讀寫數(shù)據(jù)遍希;有往一個特征寫數(shù)據(jù)等曼,另外一個讀數(shù)據(jù)的??。我一個個去試凿蒜,頭疼啊禁谦。

項目背景

5個醫(yī)療類藍牙設備:心電儀,血糖儀废封,血壓儀枷畏,血氧儀,體脂稱虱饿。

本人第一次寫博客拥诡,第一次做藍牙開發(fā),如有錯誤氮发,還望指正渴肉!萬分謝謝!(內(nèi)容有參考網(wǎng)絡文章爽冕,侵刪3鸺馈)

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市颈畸,隨后出現(xiàn)的幾起案子乌奇,更是在濱河造成了極大的恐慌没讲,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件礁苗,死亡現(xiàn)場離奇詭異爬凑,居然都是意外死亡,警方通過查閱死者的電腦和手機试伙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門嘁信,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疏叨,你說我怎么就攤上這事潘靖。” “怎么了蚤蔓?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵卦溢,是天一觀的道長。 經(jīng)常有香客問我秀又,道長既绕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任涮坐,我火速辦了婚禮凄贩,結果婚禮上,老公的妹妹穿的比我還像新娘袱讹。我一直安慰自己疲扎,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布捷雕。 她就那樣靜靜地躺著椒丧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪救巷。 梳的紋絲不亂的頭發(fā)上壶熏,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音浦译,去河邊找鬼棒假。 笑死,一個胖子當著我的面吹牛精盅,可吹牛的內(nèi)容都是我干的帽哑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叹俏,長吁一口氣:“原來是場噩夢啊……” “哼妻枕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屡谐,失蹤者是張志新(化名)和其女友劉穎蒜撮,沒想到半個月后量瓜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艰管,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡巩割,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了亭珍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡枝哄,死狀恐怖肄梨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挠锥,我是刑警寧澤众羡,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蓖租,受9級特大地震影響粱侣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蓖宦,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一齐婴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稠茂,春花似錦柠偶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至电爹,卻和暖如春蔫仙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丐箩。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工摇邦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屎勘。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓涎嚼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挑秉。 傳聞我的和親對象是個殘疾皇子法梯,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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