iOS -藍(lán)牙簡介

藍(lán)牙功能,必不可少的就是CoreBluetooth框架。
在使用藍(lán)牙的過程中增炭,如果是iOS10及以上機(jī)型需要在info.plist文件中添加NSBluetoothPeripheralUsageDescription描述字段睦刃,向用戶聲明使用藍(lán)牙的意圖

藍(lán)牙開發(fā)分為兩種:中心者模式和管理者模式
  • 中心者模式
    我們的手機(jī)作為中心設(shè)備,連接藍(lán)牙設(shè)備(這也是最常用的一種模式禾嫉,比如使用我們的手機(jī)連接小米手環(huán)、空氣凈化器等湃番;我們以下的開發(fā)也是基于這種模式夭织。)
  • 管理者模式
    我們的手機(jī)作為外設(shè),自己創(chuàng)建服務(wù)于特征吠撮,供其他設(shè)備連接我們的手機(jī)
關(guān)鍵詞
  • 中心設(shè)備:用于掃描周邊藍(lán)牙外設(shè)的設(shè)備尊惰,比如我們上面所說的中心者模式讲竿,此時(shí)我們的手機(jī)就是中心設(shè)備。
  • 外設(shè):被掃描的藍(lán)牙設(shè)備弄屡,比如我們上面所說的用我們的手機(jī)連接小米手環(huán)题禀,這時(shí)候小米手環(huán)就是外設(shè)。
  • 廣播:外部設(shè)備不停的散播的藍(lán)牙信號(hào)膀捷,讓中心設(shè)備可以掃描到迈嘹,也是我們開發(fā)中接收數(shù)據(jù)的入口。
  • 服務(wù)(Service):外部設(shè)備在與中心設(shè)備連接后會(huì)有服務(wù)全庸,可以理解成一個(gè)功能模塊秀仲,中心設(shè)備可以讀取服務(wù),篩選我們想要的服務(wù)壶笼,并從中獲取出我們想要特征神僵。(外設(shè)可以有多個(gè)服務(wù))
  • 特征(Characteristic):服務(wù)中的一個(gè)單位,一個(gè)服務(wù)可以多個(gè)特征覆劈,而特征會(huì)有一個(gè)value保礼,一般我們向藍(lán)牙設(shè)備寫入數(shù)據(jù)、從藍(lán)牙設(shè)備讀取數(shù)據(jù)就是這個(gè)value
  • UUID:區(qū)分不同服務(wù)和特征的唯一標(biāo)識(shí)责语,使用該字端我們可以獲取我們想要的服務(wù)或者特征硼莽。
CBCentralManager
  1. 初始化
    CBCentralManager 對(duì)象用于掃描萧朝、發(fā)現(xiàn)仔役、連接遠(yuǎn)程的外圍設(shè)備间护。系統(tǒng)提供了兩個(gè)初始化該類的方法
- (instancetype)initWithDelegate:(id)delegate  queue:(dispatch_queue_t)queue;
- (instancetype)initWithDelegate:(id)delegate queue:(dispatch_queue_t)queue options:(NSDictionary *)options;

在第二個(gè)初始化方法中,初始化的選項(xiàng)情況如下:

  • CBCentralManagerOptionShowPowerAlertKey 用于當(dāng)中心管理類被初始化時(shí)若此時(shí)藍(lán)牙系統(tǒng)為關(guān)閉狀態(tài)铐拐,是否向用戶顯示警告對(duì)話框徘键。該字段對(duì)應(yīng)的是NSNumber類型的對(duì)象,默認(rèn)值為NO
  • CBCentralManagerOptionRestoreIdentifierKey 中心管理器的唯一標(biāo)識(shí)符遍蟋,系統(tǒng)根據(jù)這個(gè)標(biāo)識(shí)識(shí)別特定的中心管理器,為了繼續(xù)執(zhí)行應(yīng)用程序螟凭,標(biāo)識(shí)符必須保持不變虚青,才能還原中心管理類

(1) 導(dǎo)入CoreBluetooth頭文件,并遵循兩個(gè)協(xié)議
(2) 建立中心角色(將手機(jī)作為中心角色螺男,可以搜索外部藍(lán)牙設(shè)備)

/*中心角色*/
@property (nonatomic, strong) CBCentralManager *mgr;
/*外設(shè)角色棒厘,我們當(dāng)選中某個(gè)外設(shè)連接成功后,將外設(shè)對(duì)象賦值給該對(duì)象*/
@property (nonatomic, strong) CBPeripheral *peripheral;
/*在viewDidLoad中實(shí)例化并設(shè)置代理*/
_mgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
  • 狀態(tài)的監(jiān)控
    當(dāng)藍(lán)牙狀態(tài)發(fā)現(xiàn)變化時(shí)回調(diào)
/*
* CBCentralManagerStateUnknown 狀態(tài)未知
* CBCentralManagerStateResetting 連接斷開 即將重置
* CBCentralManagerStateUnsupported 該平臺(tái)不支持藍(lán)牙
* CBCentralManagerStateUnauthorized 未授權(quán)藍(lán)牙使用
* CBCentralManagerStatePoweredOff 藍(lán)牙關(guān)閉
* CBCentralManagerStatePoweredOn 藍(lán)牙正常開啟
*
* 該方法必須實(shí)現(xiàn)下隧,否則系統(tǒng)報(bào)錯(cuò)
*/
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;

當(dāng)系統(tǒng)恢復(fù)時(shí)先調(diào)用此方法

/*
*app狀態(tài)的保存或者恢復(fù)奢人,這是第一個(gè)被調(diào)用的方法當(dāng)APP進(jìn)入后臺(tái)去完成一些藍(lán)牙有關(guān)的工作設(shè)置,使用這個(gè)方法同步app狀態(tài)通過藍(lán)牙系統(tǒng)
*
* dic中的信息
* CBCentralManagerRestoredStatePeripheralsKey 在系統(tǒng)終止程序時(shí)淆院,包含的已經(jīng)連接或者正在連接的外設(shè)的集合
* CBCentralManagerRestoredStateScanServicesKey 系統(tǒng)終止應(yīng)用程序時(shí)中心管理器連接的服務(wù)UUID數(shù)組
* CBCentralManagerRestoredStateScanOptionsKey  系統(tǒng)終止應(yīng)用程序時(shí)中心管理器正在使用的外設(shè)選項(xiàng)
*/
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)dict;

中心角色一旦創(chuàng)建何乎,藍(lán)牙狀態(tài)改變的代理方法就會(huì)自動(dòng)執(zhí)行,返回當(dāng)前藍(lán)牙狀態(tài)

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    switch (central.state) {
        case 0:
            NSLog(@"CBCentralManagerStateUnknown");//設(shè)備類型位置
            break;
        case 1:
            NSLog(@"CBCentralManagerStateResetting");//設(shè)備初始化中
            break;
        case 2:
            NSLog(@"CBCentralManagerStateUnsupported");//不支持藍(lán)牙
            break;
        case 3:
            NSLog(@"CBCentralManagerStateUnauthorized");//設(shè)備未授權(quán)
            break;
        case 4:
        {
            NSLog(@"CBCentralManagerStatePoweredOff");//藍(lán)牙未開啟
        }
            break;
        case 5:
        {
            NSLog(@"CBCentralManagerStatePoweredOn");//藍(lán)牙已開始
            //如果想自動(dòng)掃描,在此處開始掃描即可
         }
            break;
        default:
            break;
    }
}

3.開啟掃描
當(dāng)系統(tǒng)第一次發(fā)現(xiàn)外設(shè)時(shí)支救,會(huì)根據(jù)mac地址等生成一個(gè)特定的標(biāo)識(shí)符抢野,此時(shí)我們可以保存這個(gè)特定的標(biāo)識(shí)符用于后續(xù)的重連操作 系統(tǒng)提供唯一的方法進(jìn)行對(duì)周邊設(shè)備的掃描

//掃描設(shè)備
- (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs ptions:(NSDictionary *)options;
//停止掃描設(shè)備
- (void)stopScan;
  • 初始化的options選項(xiàng)詳情如下:
    CBCentralManagerScanOptionAllowDuplicatesKey 是否允許重復(fù)掃描設(shè)備,默認(rèn)為NO各墨,官方建議此值為NO指孤,當(dāng)為YES時(shí),可能對(duì)電池壽命產(chǎn)生影響贬堵,建議在必要時(shí)才使用
    UUID 表示外設(shè)的服務(wù)標(biāo)識(shí)恃轩,當(dāng)serviceUUIDs參數(shù)為nil時(shí),將返回所有發(fā)現(xiàn)的外設(shè)(蘋果不推薦此種做法)黎做;當(dāng)填寫改服務(wù)標(biāo)識(shí)時(shí)详恼,系統(tǒng)將返回對(duì)應(yīng)該服務(wù)標(biāo)識(shí)的外設(shè)
  • 可以指定允許應(yīng)用程序在后臺(tái)掃描設(shè)備,前提必須滿足兩個(gè)條件:
    必須允許在藍(lán)牙的后臺(tái)模式
    必須指定一個(gè)或多個(gè)UUID服務(wù)標(biāo)識(shí)
    手動(dòng)開啟掃描
- (void)searchDevcie{
    if(_mgr!=nil&& _mgr.state==5){
         [self. mgr scanForPeripheralsWithServices:nil options:nil];
    }
}

每當(dāng)搜索到一個(gè)外設(shè)之后引几,都會(huì)自動(dòng)觸發(fā)以下方法昧互,每發(fā)現(xiàn)一個(gè)藍(lán)牙設(shè)備都會(huì)調(diào)用此函數(shù),因?yàn)槟壳八{(lán)牙設(shè)備越來越多(小米伟桅、顯示器敞掘、ofo...),所以我在此處做了篩選楣铁,只要我的設(shè)備

- (void)centralManager:(CBCentralManager *)central  // 中心管理者didDiscoverPeripheral:(CBPeripheral *)peripheral  // 外設(shè)advertisementData:(NSDictionary *)advertisementData // 外設(shè)攜帶的數(shù)據(jù)
RSSI:(NSNumber *)RSSI{  // 外設(shè)發(fā)出的藍(lán)牙信號(hào)強(qiáng)度
    if ([peripheral.name hasPrefix:@"HT"]) {
        NSLog(@"%@",peripheral.name);//能夠進(jìn)來的 都是我們想要的設(shè)備了
        //我們的邏輯是玖雁,搜索到一個(gè)設(shè)備(peripheral)放到一個(gè)集合,然后給用戶進(jìn)行選擇
    } 
}

4.根據(jù)上面掃描的設(shè)備列表盖腕,接下來做的就是連接設(shè)備了赫冬。

//連接外設(shè)
- (void)connectPeripheral:(CBPeripheral *)peripheral  options:(NSDictionary *)options;
//斷開與外設(shè)的連接
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;

連接設(shè)備時(shí)選項(xiàng)情況如下:

  • CBConnectPeripheralOptionNotifyOnConnectionKey 應(yīng)用程序被掛起時(shí),成功連接到外設(shè)溃列,是否向用戶顯示警告對(duì)話框劲厌,對(duì)應(yīng)NSNumber對(duì)象,默認(rèn)值為NO
  • CBConnectPeripheralOptionNotifyOnDisconnectionKey 應(yīng)用程序被掛起時(shí)听隐,與外設(shè)斷開連接补鼻,是否向用戶顯示警告對(duì)話框,對(duì)應(yīng)NSNumber對(duì)象雅任,默認(rèn)值為NO
  • CBConnectPeripheralOptionNotifyOnNotificationKey 應(yīng)用程序被掛起時(shí)风范,只要接收到給定peripheral的通知,是否就彈框顯示

官方建議如果連接設(shè)備不成功且沒有進(jìn)行重連沪么,要明確取消與外設(shè)的連接(即調(diào)用斷開與外設(shè)連接的方法) 當(dāng)調(diào)用斷開方法硼婿,斷開與設(shè)備連接時(shí),官方明確表明取消本地連接不能保證物理鏈接立即斷開禽车。當(dāng)設(shè)備連接時(shí)間超過8秒后寇漫,調(diào)用斷開的API能立即斷開刊殉;但是連接未超過8秒,就調(diào)用斷開API需要幾秒后系統(tǒng)才與設(shè)備斷開連接

  1. 通過搜索系統(tǒng)獲取periphera對(duì)象
//獲取與系統(tǒng)已經(jīng)連接的外設(shè)對(duì)象猪腕,如果設(shè)備已經(jīng)與系統(tǒng)連接冗澈,通過該方法獲取到外設(shè)對(duì)象后可以直接對(duì)CBPeripheral對(duì)象發(fā)起連接
- (NSArray *)retrieveConnectedPeripheralsWithServices:(NSArray *)serviceUUIDs;
//獲取一個(gè)已知CBPeripheral的列表(過去曾經(jīng)發(fā)現(xiàn)過或者連接過的peripheral)
- (NSArray *)retrievePeripheralsWithIdentifiers:(NSArray *)identifiers;
CBCentralManagerDelegate

啟動(dòng)搜索發(fā)現(xiàn)設(shè)備后調(diào)用此代理

/*
* advertisementData 廣播中的信息
*
* CBAdvertisementDataLocalNameKey 對(duì)應(yīng)設(shè)置NSString類型的廣播名
* CBAdvertisementDataManufacturerDataKey 外設(shè)制造商的NSData數(shù)據(jù)
* CBAdvertisementDataServiceDataKey  外設(shè)制造商的CBUUID數(shù)據(jù)
* CBAdvertisementDataServiceUUIDsKey 服務(wù)的UUID與其對(duì)應(yīng)的服務(wù)數(shù)據(jù)字典數(shù)組
* CBAdvertisementDataOverflowServiceUUIDsKey 附加服務(wù)的UUID數(shù)組
* CBAdvertisementDataTxPowerLevelKey 外設(shè)的發(fā)送功率 NSNumber類型
* CBAdvertisementDataIsConnectable 外設(shè)是否可以連接
* CBAdvertisementDataSolicitedServiceUUIDsKey 服務(wù)的UUID數(shù)組
*
* RSSI 收到當(dāng)前信號(hào)的強(qiáng)度 單位為分貝
*
* 如果后續(xù)要使用peripheral對(duì)象,必須在引用該對(duì)象陋葡,否則后續(xù)操作無法進(jìn)行
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;

當(dāng)設(shè)備連接成功后調(diào)用此代理

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;

當(dāng)設(shè)備斷開連接后調(diào)用此代理方法

//當(dāng)斷開連接后亚亲,外設(shè)中所有的服務(wù)、特征腐缤、特征描述都將失效
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;

當(dāng)連接失敗后調(diào)用此代理方法

//在ios系統(tǒng)的藍(lán)牙連接中捌归,不存在超時(shí)的現(xiàn)象,因此連接通常表示為一個(gè)暫時(shí)性的問題岭粤,可在改方法中再次嘗試連接
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;

下面

//從設(shè)備列表選中我們要的設(shè)備  連接設(shè)備
- (void)connectDevice:(CBPeripheral *)peripheral{
    [self.mgr connectPeripheral:peripheral options:nil];
}

連接相關(guān)的代理方法:

// 中心管理者連接外設(shè)成功(如果外設(shè)成功與中心設(shè)備連接成功之后會(huì)自動(dòng)觸發(fā)該方法)
- (void)centralManager:(CBCentralManager *)central // 中心管理者
  didConnectPeripheral:(CBPeripheral *)peripheral // 外設(shè)
{
    NSLog(@"連接成功");
    // 標(biāo)記我們的外設(shè),讓他的生命周期 = ViewController
    self.peripheral = peripheral;
    //  設(shè)置外設(shè)的代理
    self.peripheral.delegate = self;
    // 外設(shè)發(fā)現(xiàn)服務(wù),傳nil代表不過濾
    //調(diào)用該方法后惜索,將會(huì)自動(dòng)調(diào)用didDiscoverServices()代理方法,用于發(fā)現(xiàn)服務(wù)
    [self.peripheral discoverServices:nil];
    //既然連接上了剃浇,為了解決性能巾兆,就停止掃描吧
    [_mgr stopScan];
}
// 外設(shè)連接失敗
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"連接失敗");
}
// 丟失連接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"斷開連接");
}

發(fā)現(xiàn)特征

/*
* 參數(shù)characteristicUUIDs不為空時(shí),返回對(duì)應(yīng)的特征值虎囚,當(dāng)參數(shù)為nil時(shí)角塑,返回該服務(wù)中的所有特征值
*/
- (void)discoverCharacteristics:(NSArray *)characteristicUUIDs forService:(CBService *)service;
//發(fā)現(xiàn)特征值描述
- (void)discoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic;

發(fā)現(xiàn)服務(wù)的回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error;

發(fā)現(xiàn)服務(wù)中的服務(wù)的回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(NSError *)error;

發(fā)現(xiàn)特征的回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;

發(fā)現(xiàn)特征描述的回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

當(dāng)調(diào)用[self.peripheral discoverServices:nil];方法之后,當(dāng)發(fā)現(xiàn)服務(wù)后就會(huì)自動(dòng)調(diào)用以下的didDiscoverServices()代理方法

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    NSLog(@"didDiscoverServices");
    if (error)
    {
        NSLog(@"Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
        return;
    }
    //服務(wù)并不是我們的目標(biāo)淘讥,也沒有實(shí)際意義圃伶。我們需要用的是服務(wù)下的特征,查詢(每一個(gè)服務(wù)下的若干)特征
    for (CBService *service in peripheral.services)
    {
       //好蒲列,發(fā)現(xiàn)了服務(wù) 接下來就是最重要的特征了
      //調(diào)用該方法后窒朋,當(dāng)發(fā)現(xiàn)了service服務(wù)中的特征后,將會(huì)自動(dòng)觸發(fā)didDiscoverCharacteristicsForService()方法
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

當(dāng)特征值發(fā)現(xiàn)變化時(shí)回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

當(dāng)特征值描述發(fā)生變化時(shí)回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error;

發(fā)現(xiàn)外設(shè)服務(wù)里的特征的時(shí)候調(diào)用的代理方法(這個(gè)是比較重要的方法蝗岖,你在這里可以通過事先知道UUID找到你需要的特征侥猩,訂閱特征,或者這里寫入數(shù)據(jù)給特征也可以)

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    NSLog(@"didDiscoverCharacteristicsForService");
    //在此處可以獲取服務(wù)中的特征了剪侮,與硬件工程師溝通后得知拭宁,uuid為ffe2的特征是用于向外部藍(lán)牙設(shè)備寫數(shù)據(jù)的,uuid為ffe1的特征是用于從外部藍(lán)牙設(shè)備讀取數(shù)據(jù)的瓣俯,那讀信息,因?yàn)槲以谙旅嫦蛩{(lán)牙外部設(shè)備寫數(shù)據(jù)的時(shí)候兵怯,我使用了一個(gè)參數(shù):CBCharacteristicWriteWithoutResponse(也就是說彩匕,沒有回調(diào),因那沒有回調(diào)就取不到數(shù)據(jù)咯媒区?但是我們可以給讀取數(shù)據(jù)的服務(wù)設(shè)置一個(gè)通知驼仪,也就是下面的setNotifyValue:方法了掸犬,一旦設(shè)置,如果外部藍(lán)牙設(shè)備給中心設(shè)備發(fā)送信息绪爸,我們就能在didUpdateValueForCharacteristic代理方法中進(jìn)行獲取了)
    for (CBCharacteristic *cha in service.characteristics) {
        if([[cha.UUID.UUIDString lowercaseString] isEqualToString:@"ffe2"]){
            self.characteristic = cha;
        }else if([[cha.UUID.UUIDString lowercaseString] isEqualToString:@"ffe1"]){
            self.readcharacteristic = cha;
            [self.peripheral setNotifyValue:YES forCharacteristic:self.readcharacteristic];
        }
    }
}

接下來我們就能從外部藍(lán)牙設(shè)備中接收數(shù)據(jù)了
讀取數(shù)據(jù)

/* 讀取特征值
 * 并非所有特征都有特征值湾碎,需通過CBCharacteristicProperties枚舉判斷該特征是否可讀
 */
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic;
//讀取特征值描述
- (void)readValueForDescriptor:(CBDescriptor *)descriptor;
//從外圍設(shè)備獲取數(shù)據(jù)
// 更新特征的value的時(shí)候會(huì)調(diào)用 (凡是從藍(lán)牙傳過來的數(shù)據(jù)都要經(jīng)過這個(gè)回調(diào),簡單的說這個(gè)方法就是你拿數(shù)據(jù)的唯一方法) 你可以判斷是否
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
//因?yàn)槲覀兪莾蓚€(gè)特征 一個(gè)讀 一個(gè)寫 所以我讀取數(shù)據(jù)奠货,只要從讀的特征中獲取就可以了
  if (characteristic ==  self.readcharacteristic) {
        //characteristic.value 就是我們想要的數(shù)據(jù)了
  }
}

重頭戲到了介褥,接下來就是向外部藍(lán)牙設(shè)備寫數(shù)據(jù)了
寫入數(shù)據(jù)

/*
 * 寫入數(shù)據(jù),只允許對(duì)其特定的類型進(jìn)行寫入操作
 * 通過characteristic的properties的判斷递惋,判別支持那種類型的寫入
 * CBCharacteristicWriteWithResponse 如果寫入不成功柔滔,將以詳細(xì)錯(cuò)誤說明進(jìn)行響應(yīng)
 * CBCharacteristicWriteWithoutResponse 如果寫入失敗,則不會(huì)收到通知
 */
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;
//寫入特征值的描述萍虽,一般為字符串對(duì)characteristic的描述
- (void)writeValue:(NSData *)data forDescriptor:(CBDescriptor *)descriptor;

設(shè)置特征值的通知

//如果設(shè)置為YES睛廊,當(dāng)特征值發(fā)現(xiàn)變化時(shí)則會(huì)發(fā)起通知
- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic;
只需要調(diào)用以下方法就可以實(shí)現(xiàn)數(shù)據(jù)寫入了,【subData就是我們的需要寫入外部藍(lán)牙設(shè)備的數(shù)據(jù)了;self.characteristic是我們寫的服務(wù)杉编;CBCharacteristicWriteWithoutResponse 不需要回調(diào)函數(shù)(一開始踩了個(gè)坑設(shè)置的CBCharacteristicWriteWithResponse超全,報(bào)錯(cuò)"writing is not permitted")】
[self.peripheral writeValue:subData  forCharacteristic:self.characteristic    type:CBCharacteristicWriteWithoutResponse];

遇到的坑:我們?cè)谙驍z像頭藍(lán)牙設(shè)備寫入指令的時(shí)候,我們的指令比較長(達(dá)到了200byte左右邓馒,但是我們的攝像頭設(shè)備藍(lán)牙內(nèi)部接收緩沖區(qū)只有16個(gè)字節(jié))嘶朱,所以我們只能選擇分包發(fā)送了,將200byte的指令拆開發(fā)送
如果你們的指令正好在你們的緩沖區(qū)閥值之內(nèi)绒净,那就無須使用以下代碼了

-(void)sendDataWithSubPackage:(NSData*)goalData
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        for (int i = 0; i < [msgData length]; i += 16) {  
            // 預(yù)加 最大包長度见咒,如果依然小于總數(shù)據(jù)長度,可以取最大包數(shù)據(jù)大小
            if ((i + 16) < [msgData length]) {
                NSRange range  = NSMakeRange(i, 16);  
                NSData *subData = [msgData subdataWithRange:range];
                [self.peripheral writeValue:subData forCharacteristic:self.characteristic type:CBCharacteristicWriteWithoutResponse];
                //根據(jù)接收模塊的處理能力做相應(yīng)延時(shí),因?yàn)樗{(lán)牙設(shè)備處理指令需要時(shí)間挂疆,所以我這邊給了500毫秒
                usleep(500 * 1000);
            }else {
                NSString *rangeStr = [NSString stringWithFormat:@"%i,%i", i, (int)([msgData length] - i)];
                NSData *subData = [msgData subdataWithRange:NSRangeFromString(rangeStr)];
                [self.peripheral writeValue:subData forCharacteristic:self.characteristic type:CBCharacteristicWriteWithoutResponse];
                usleep(500 * 1000);
            }
        }
    });
}

發(fā)現(xiàn)服務(wù)

  • 發(fā)現(xiàn)外設(shè)指定的服務(wù)
* 當(dāng)設(shè)置serviceUUIDs參數(shù)時(shí)改览,只返回指定的服務(wù)(官方推薦做法)
* 當(dāng)為指定serviceUUIDs參數(shù)時(shí),將返回外設(shè)中所有可用的服務(wù)
*/
- (void)discoverServices:(NSArray *)serviceUUIDs;

從服務(wù)中發(fā)現(xiàn)服務(wù)

- (void)discoverIncludedServices:(NSArray *)includedServiceUUIDs forService:(CBService *)service;

讀取RSSI

//當(dāng)調(diào)用此方法時(shí)缤言,外設(shè)會(huì)調(diào)用peripheral:didReadRSSI:error:代理方法
- (void)readRSSI;

獲取最大的寫入長度

/* 根據(jù)寫入類型獲取最大的寫入長度
 * CBCharacteristicWriteWithResponse宝当、CBCharacteristicWriteWithoutResponse
 */
- (NSUInteger)maximumWriteValueLengthForType:(CBCharacteristicWriteType)type;

CBPeripheralDelegate
寫入特征值時(shí)回調(diào)

//只有指定寫入類型有回應(yīng)時(shí)才回到此方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

寫入特征值描述時(shí)回調(diào)

//當(dāng)調(diào)用寫入特征值描述時(shí)回調(diào)該方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error;

當(dāng)外設(shè)啟動(dòng)或者停止指定特征值的通知時(shí)回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

當(dāng)連接外設(shè)成功后獲取信號(hào)強(qiáng)度的方法后回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error;

當(dāng)外設(shè)名稱發(fā)生變化時(shí)回調(diào)

//由于外設(shè)可以改變其設(shè)備名稱,因此若要顯示該設(shè)備的當(dāng)前名稱可在該方法中處理
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral;

當(dāng)外設(shè)服務(wù)發(fā)生變化時(shí)回調(diào)

- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray *)invalidatedServices;

CBCharacteristic
擁有的屬性

  • service 該特征所屬的服務(wù)
  • value 該特征的值
  • isNotifying 是否訂閱特征值
  • descriptors 該特征的描述列表
  • properties 性質(zhì)特點(diǎn)
    1胆萧、CBCharacteristicPropertyBroadcast 該屬性為廣播類型
    2庆揩、CBCharacteristicPropertyRead 可讀
    3、CBCharacteristicPropertyWriteWithoutResponse 寫-沒有響應(yīng)
    4跌穗、CBCharacteristicPropertyWrite 可寫
    5订晌、CBCharacteristicPropertyNotify 通知
    6、CBCharacteristicPropertyIndicate 聲明
    7蚌吸、CBCharacteristicPropertyAuthenticatedSignedWrites 通過驗(yàn)證的
    8锈拨、CBCharacteristicPropertyExtendedProperties 拓展
    9、CBCharacteristicPropertyNotifyEncryptionRequired 需要加密的通知
    10羹唠、CBCharacteristicPropertyIndicateEncryptionRequired 需要加密的申明
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奕枢,一起剝皮案震驚了整個(gè)濱河市娄昆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缝彬,老刑警劉巖萌焰,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谷浅,居然都是意外死亡扒俯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門壳贪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陵珍,“玉大人,你說我怎么就攤上這事违施』ゴ浚” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵磕蒲,是天一觀的道長留潦。 經(jīng)常有香客問我,道長辣往,這世上最難降的妖魔是什么兔院? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮站削,結(jié)果婚禮上坊萝,老公的妹妹穿的比我還像新娘。我一直安慰自己许起,他們只是感情好十偶,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著园细,像睡著了一般惦积。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猛频,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天狮崩,我揣著相機(jī)與錄音,去河邊找鬼鹿寻。 笑死睦柴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的毡熏。 我是一名探鬼主播爱只,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼招刹!你這毒婦竟也來了恬试?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤疯暑,失蹤者是張志新(化名)和其女友劉穎训柴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妇拯,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幻馁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了越锈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仗嗦。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖甘凭,靈堂內(nèi)的尸體忽然破棺而出稀拐,到底是詐尸還是另有隱情,我是刑警寧澤丹弱,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布德撬,位于F島的核電站,受9級(jí)特大地震影響躲胳,放射性物質(zhì)發(fā)生泄漏蜓洪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一坯苹、第九天 我趴在偏房一處隱蔽的房頂上張望隆檀。 院中可真熱鬧,春花似錦粹湃、人聲如沸恐仑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽菊霜。三九已至,卻和暖如春济赎,著一層夾襖步出監(jiān)牢的瞬間鉴逞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國打工司训, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留构捡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓壳猜,卻偏偏與公主長得像勾徽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子统扳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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