<iOS開發(fā)>之藍(lán)牙使用

本文介紹了藍(lán)牙的概念以及具體的使用步驟.

一.藍(lán)牙概念

藍(lán)牙2.0為傳統(tǒng)藍(lán)牙,傳統(tǒng)藍(lán)牙也稱為經(jīng)典藍(lán)牙.
藍(lán)牙4.0因?yàn)榈秃碾?所以也叫做低功耗藍(lán)(BLE).它將三種規(guī)格集一體届谈,包括傳統(tǒng)藍(lán)牙技術(shù)、高速技術(shù)和低耗能技術(shù).

二.BLE支持兩種部署方式

  1. 雙模式
    低功耗藍(lán)牙功能集成在現(xiàn)有的經(jīng)典藍(lán)牙控制器中弯汰,或在現(xiàn)有經(jīng)典藍(lán)牙技術(shù)芯片上增加低功耗堆棧艰山,整體架構(gòu)基本不變,因此成本增加有限.
  2. 單模式
    面向高度集成咏闪、緊湊的設(shè)備曙搬,使用一個(gè)輕量級(jí)連接層(Link Layer)提供超低功耗的待機(jī)模式操作、簡(jiǎn)單設(shè)備恢復(fù)和可靠的點(diǎn)對(duì)多點(diǎn)數(shù)據(jù)傳輸鸽嫂,還能讓聯(lián)網(wǎng)傳感器在藍(lán)牙傳輸中安排好低功耗藍(lán)牙流量的次序纵装,同時(shí)還有高級(jí)節(jié)能和安全加密連接.

三.藍(lán)牙各版本使用選擇

  1. 藍(lán)牙2.0,不上架
    使用私有API,手機(jī)需要越獄.
  2. 藍(lán)牙2.0,要上架
    進(jìn)行MFI認(rèn)證,使用ExternalAccessory框架.手機(jī)不需要越獄.
  3. 藍(lán)牙4.0,要上架
    使用CoreBluetooth框架,手機(jī)不需要越獄.(CoreBluetooth是基于BLE來開發(fā)的)
  4. 說明
    對(duì)于小的硬件廠商來說,MFI認(rèn)證通過幾率不大,不僅耗錢還耗時(shí),所以,還是推薦使用藍(lán)牙4.0.
    (MFI:Make for ipad ,iphone, itouch 專們?yōu)樘O果設(shè)備制作的設(shè)備)

四.問題描述

公司要求iOS端需要和鋼琴進(jìn)行藍(lán)牙連接并進(jìn)行數(shù)據(jù)通信,我以為鋼琴是藍(lán)牙4.0,然后快速集成CoreBluetooth框架寫了一個(gè)demo,掃描外設(shè)時(shí),沒有發(fā)現(xiàn)鋼琴的藍(lán)牙名稱,可是用iphone打開系統(tǒng)設(shè)置,可以發(fā)現(xiàn)鋼琴對(duì)應(yīng)的藍(lán)牙.問了安卓的同事,得知鋼琴的藍(lán)牙只有2.0的模塊,所以,安卓端是用2.0藍(lán)牙進(jìn)行交互的.公司決定不做MFI認(rèn)證,改用藍(lán)牙4.0.在與硬件廠商交涉的過程中,得知鋼琴中的藍(lán)牙是4.0的,但是,他們?cè)谠O(shè)計(jì)藍(lán)牙板子的時(shí)候,沒有集成低功耗技術(shù).之后,板子寄回硬件廠商,添加BLE模塊.這才踏上藍(lán)牙4.0的正軌.

五.藍(lán)牙4.0使用解析

1.基本知識(shí)

central:中心,連接硬件的設(shè)備.
peripheral:外設(shè),被連接的硬件.
說明:外設(shè)在一直廣播,當(dāng)你創(chuàng)建的中心對(duì)象在掃描外設(shè)時(shí),就能夠發(fā)現(xiàn)外設(shè).
如圖所示:

[圖片上傳失敗...(image-53233a-1527600504149)]

service:服務(wù).
characteristic:特征.
說明:一個(gè)外設(shè)包含多個(gè)服務(wù),而每一個(gè)服務(wù)中又包含多個(gè)特征,特征包括特征的值和特征的描述.每個(gè)服務(wù)包含多個(gè)字段,字段的權(quán)限有read(讀)、write(寫)据某、notify(通知).

[圖片上傳失敗...(image-107c5e-1527600504149)]

2.藍(lán)牙4.0分為兩種模式
  • 中心模式流程
    1. 建立中心角色 [[CBCentralManager alloc] initWithDelegate:self queue:nil]
    2. 掃描外設(shè) cancelPeripheralConnection
    3. 發(fā)現(xiàn)外設(shè) didDiscoverPeripheral
    4. 連接外設(shè) connectPeripheral
      4.1 連接失敗 didFailToConnectPeripheral
      4.2 連接斷開 didDisconnectPeripheral
      4.3 連接成功 didConnectPeripheral
    5. 掃描外設(shè)中的服務(wù) discoverServices
      5.1 發(fā)現(xiàn)并獲取外設(shè)中的服務(wù) didDiscoverServices
    6. 掃描外設(shè)對(duì)應(yīng)服務(wù)的特征 discoverCharacteristics
      6.1 發(fā)現(xiàn)并獲取外設(shè)對(duì)應(yīng)服務(wù)的特征 didDiscoverCharacteristicsForService
      6.2 給對(duì)應(yīng)特征寫數(shù)據(jù) writeValue:forCharacteristic:type:
    7. 訂閱特征的通知 setNotifyValue:forCharacteristic:
      7.1 根據(jù)特征讀取數(shù)據(jù) didUpdateValueForCharacteristic
  • 外設(shè)模式流程
    1. 建立外設(shè)角色
    2. 設(shè)置本地外設(shè)的服務(wù)和特征
    3. 發(fā)布外設(shè)和特征
    4. 廣播服務(wù)
    5. 響應(yīng)中心的讀寫請(qǐng)求
    6. 發(fā)送更新的特征值搂擦,訂閱中心

六.藍(lán)牙4.0開發(fā)步驟

1.本文采用中心模式
導(dǎo)入CoreBluetooth框架,#import <CoreBluetooth/CoreBluetooth.h>
2.遵守CBCentralManagerDelegate,CBPeripheralDelegate協(xié)議
3.添加屬性

// 中心管理者(管理設(shè)備的掃描和連接)
@property (nonatomic, strong) CBCentralManager *centralManager;
// 存儲(chǔ)的設(shè)備
@property (nonatomic, strong) NSMutableArray *peripherals;
// 掃描到的設(shè)備
@property (nonatomic, strong) CBPeripheral *cbPeripheral;
// 文本
@property (weak, nonatomic) IBOutlet UITextView *peripheralText;
// 外設(shè)狀態(tài)
@property (nonatomic, assign) CBManagerState peripheralState;

常量,具體服務(wù)和特征是讀還是寫的類型,問公司硬件廠商,或者問同事.

// 藍(lán)牙4.0設(shè)備名
static NSString * const kBlePeripheralName = @"公司硬件藍(lán)牙名稱";
// 通知服務(wù)
static NSString * const kNotifyServerUUID = @"FFE0";
// 寫服務(wù)
static NSString * const kWriteServerUUID = @"FFE1";
// 通知特征值
static NSString * const kNotifyCharacteristicUUID = @"FFE2";
// 寫特征值
static NSString * const kWriteCharacteristicUUID = @"FFE3";

4.創(chuàng)建中心管理者

- (CBCentralManager *)centralManager
{
    if (!_centralManager)
    {
        _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    }
    return _centralManager;
}

創(chuàng)建存儲(chǔ)設(shè)備數(shù)組

- (NSMutableArray *)peripherals
{
    if (!_peripherals) {
        _peripherals = [NSMutableArray array];
    }
    return _peripherals;
}

5.掃描設(shè)備之前會(huì)調(diào)用中心管理者狀態(tài)改變的方法

// 當(dāng)狀態(tài)更新時(shí)調(diào)用(如果不實(shí)現(xiàn)會(huì)崩潰)
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    switch (central.state) {
        case CBManagerStateUnknown:{
            NSLog(@"未知狀態(tài)");
            self.peripheralState = central.state;
        }
            break;
        case CBManagerStateResetting:
        {
            NSLog(@"重置狀態(tài)");
            self.peripheralState = central.state;
        }
            break;
        case CBManagerStateUnsupported:
        {
            NSLog(@"不支持的狀態(tài)");
            self.peripheralState = central.state;
        }
            break;
        case CBManagerStateUnauthorized:
        {
            NSLog(@"未授權(quán)的狀態(tài)");
            self.peripheralState = central.state;
        }
            break;
        case CBManagerStatePoweredOff:
        {
            NSLog(@"關(guān)閉狀態(tài)");
            self.peripheralState = central.state;
        }
            break;
        case CBManagerStatePoweredOn:
        {
            NSLog(@"開啟狀態(tài)-可用狀態(tài)");
            self.peripheralState = central.state;
        }
            break;
        default:
            break;
    }
}

掃描設(shè)備

// 掃描設(shè)備
- (IBAction)scanForPeripherals
{
    [self.centralManager stopScan];
    NSLog(@"掃描設(shè)備");
    [self showMessage:@"掃描設(shè)備"]; 
    if (self.peripheralState ==  CBManagerStatePoweredOn)
    {
        // 掃描所有設(shè)備,傳入nil,代表所有設(shè)備.
        [self.centralManager scanForPeripheralsWithServices:nil options:nil];
    }
}

6.掃描到設(shè)備并開始連接

/**
 掃描到設(shè)備

 @param central 中心管理者
 @param peripheral 掃描到的設(shè)備
 @param advertisementData 廣告信息
 @param RSSI 信號(hào)強(qiáng)度
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral 
advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    [self showMessage:[NSString stringWithFormat:@"發(fā)現(xiàn)設(shè)備,設(shè)備名:%@",peripheral.name]];

    if (![self.peripherals containsObject:peripheral])
    {
        [self.peripherals addObject:peripheral];
        NSLog(@"%@",peripheral);

        if ([peripheral.name isEqualToString:kBlePeripheralName])
        {
            [self showMessage:[NSString stringWithFormat:@"設(shè)備名:%@",peripheral.name]];
            self.cbPeripheral = peripheral;

            [self showMessage:@"開始連接"];
            [self.centralManager connectPeripheral:peripheral options:nil];
        }
    }
}

7.連接的三種狀態(tài),如果連接成功,則掃描所有服務(wù)(也可以掃描指定服務(wù))
連接失敗重連

/**
 連接失敗

 @param central 中心管理者
 @param peripheral 連接失敗的設(shè)備
 @param error 錯(cuò)誤信息
 */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    [self showMessage:@"連接失敗"];
    if ([peripheral.name isEqualToString:kBlePeripheralName])
    {
        [self.centralManager connectPeripheral:peripheral options:nil];
    }
}

連接斷開重連

/**
 連接斷開

 @param central 中心管理者
 @param peripheral 連接斷開的設(shè)備
 @param error 錯(cuò)誤信息
 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    [self showMessage:@"斷開連接"];
    if ([peripheral.name isEqualToString:kBlePeripheralName])
    {
        [self.centralManager connectPeripheral:peripheral options:nil];
    }
}

連接成功并掃描服務(wù)

/**
 連接成功

 @param central 中心管理者
 @param peripheral 連接成功的設(shè)備
 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"連接設(shè)備:%@成功",peripheral.name);
    [self showMessage:[NSString stringWithFormat:@"連接設(shè)備:%@成功",peripheral.name]];

    // 設(shè)置設(shè)備的代理
    peripheral.delegate = self;
    // services:傳入nil代表掃描所有服務(wù)
    [peripheral discoverServices:nil];
}

8.發(fā)現(xiàn)服務(wù)并掃描服務(wù)對(duì)應(yīng)的特征

/**
 掃描到服務(wù)

 @param peripheral 服務(wù)對(duì)應(yīng)的設(shè)備
 @param error 掃描錯(cuò)誤信息
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    // 遍歷所有的服務(wù)
    for (CBService *service in peripheral.services)
    {
        NSLog(@"服務(wù):%@",service.UUID.UUIDString);
        // 獲取對(duì)應(yīng)的服務(wù)
        if ([service.UUID.UUIDString isEqualToString:kWriteServerUUID] || [service.UUID.UUIDString isEqualToString:kNotifyServerUUID])
        {
            // 根據(jù)服務(wù)去掃描特征
            [peripheral discoverCharacteristics:nil forService:service];
        }
    }
}

9.掃描到對(duì)應(yīng)的特征,寫入特征的值,并訂閱指定的特征通知.

/**
 掃描到對(duì)應(yīng)的特征

 @param peripheral 設(shè)備
 @param service 特征對(duì)應(yīng)的服務(wù)
 @param error 錯(cuò)誤信息
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    // 遍歷所有的特征
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        NSLog(@"特征值:%@",characteristic.UUID.UUIDString);
        // 獲取對(duì)應(yīng)的特征
        if ([characteristic.UUID.UUIDString isEqualToString:kWriteCharacteristicUUID])
        {
            // 寫入數(shù)據(jù)
            [self showMessage:@"寫入特征值"];
            for (Byte i = 0x0; i < 0x73; i++)
            {
                // 讓鋼琴的每顆燈都亮一次
                Byte byte[] = {0xf0, 0x3d, 0x3d, i,
                    0x02,0xf7};
                NSData *data = [NSData dataWithBytes:byte length:6];
                [peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
            }
        }
        if ([characteristic.UUID.UUIDString isEqualToString:kNotifyCharacteristicUUID])
        {
            // 訂閱特征通知
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];

        }
    }
}

10.根據(jù)特征讀取到數(shù)據(jù)

/**
 根據(jù)特征讀到數(shù)據(jù)

 @param peripheral 讀取到數(shù)據(jù)對(duì)應(yīng)的設(shè)備
 @param characteristic 特征
 @param error 錯(cuò)誤信息
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    if ([characteristic.UUID.UUIDString isEqualToString:kNotifyCharacteristicUUID])
    {
        NSData *data = characteristic.value;
        NSLog(@"%@",data);
    }
}

讀取值打印結(jié)果:

2017-04-25 12:34:41.876974+0800 藍(lán)牙4.0Demo[1745:346611] <9f5436>
2017-04-25 12:34:41.983016+0800 藍(lán)牙4.0Demo[1745:346611] <8f5440>
2017-04-25 12:34:42.154821+0800 藍(lán)牙4.0Demo[1745:346611] <9f5649>
2017-04-25 12:34:42.239481+0800 藍(lán)牙4.0Demo[1745:346611] <8f5640>

提示:上Appstore下載LightBlue,進(jìn)行藍(lán)牙通信測(cè)試.

作者:o惜樂o
鏈接:http://www.reibang.com/p/b62081c427a4

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市哗脖,隨后出現(xiàn)的幾起案子瀑踢,更是在濱河造成了極大的恐慌,老刑警劉巖才避,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橱夭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡桑逝,警方通過查閱死者的電腦和手機(jī)棘劣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楞遏,“玉大人茬暇,你說我怎么就攤上這事」押龋” “怎么了糙俗?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)预鬓。 經(jīng)常有香客問我巧骚,道長(zhǎng),這世上最難降的妖魔是什么格二? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任劈彪,我火速辦了婚禮,結(jié)果婚禮上顶猜,老公的妹妹穿的比我還像新娘沧奴。我一直安慰自己,他們只是感情好长窄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布滔吠。 她就那樣靜靜地躺著远寸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屠凶。 梳的紋絲不亂的頭發(fā)上驰后,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音矗愧,去河邊找鬼灶芝。 笑死,一個(gè)胖子當(dāng)著我的面吹牛唉韭,可吹牛的內(nèi)容都是我干的夜涕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼属愤,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼女器!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起住诸,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤驾胆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后贱呐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丧诺,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年奄薇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驳阎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡馁蒂,死狀恐怖呵晚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沫屡,我是刑警寧澤饵隙,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站谁鳍,受9級(jí)特大地震影響癞季,放射性物質(zhì)發(fā)生泄漏劫瞳。R本人自食惡果不足惜倘潜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望志于。 院中可真熱鬧涮因,春花似錦、人聲如沸伺绽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至澜掩,卻和暖如春购披,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肩榕。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工刚陡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人株汉。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓筐乳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親乔妈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蝙云,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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