藍(lán)牙

隨著智能家具点楼、智能穿戴等的興起,藍(lán)牙開發(fā)應(yīng)用越來越廣泛悴品,有關(guān)藍(lán)牙方面的問題禀综,今天就給大家進(jìn)行詳細(xì)的講解郎哭,想要了解藍(lán)牙在iOS開發(fā)中的具體實(shí)現(xiàn),首先我們必須知道什么是藍(lán)牙菇存?

藍(lán)牙概念:

隨著藍(lán)牙低功耗技術(shù)BLE(Bluetooth Low Energy)的發(fā)展夸研,藍(lán)牙技術(shù)正在一步步成熟,如今的大部分移動(dòng)設(shè)備都配備有藍(lán)牙4.0依鸥,相比之前的藍(lán)牙技術(shù)耗電量大大降低亥至。從ios的發(fā)展史也不難看出蘋果目前對(duì)藍(lán)牙技術(shù)也是越來越關(guān)注,例如蘋果于2013年9月發(fā)布的iOS7就配備了iBeacon技術(shù)贱迟,這項(xiàng)技術(shù)完全基于藍(lán)牙傳輸姐扮。但是眾所周知蘋果的設(shè)備對(duì)于權(quán)限要求也是比較高的,因此在iOS中并不能像Android一樣隨意使用藍(lán)牙進(jìn)行文件傳輸(除非你已經(jīng)越獄)衣吠。知道什么是藍(lán)牙之后,那么在iOS中進(jìn)行藍(lán)牙傳輸應(yīng)用開發(fā)常用的框架有哪幾種呢?

藍(lán)牙在開發(fā)中的框架

GameKit.framework:iOS7之前的藍(lán)牙通訊框架茶敏,從iOS7開始過期,但是目前多數(shù)應(yīng)用還是基于此框架缚俏。

MultipeerConnectivity.framework:iOS7開始引入的新的藍(lán)牙通訊開發(fā)框架惊搏,用于取代GameKit。

CoreBluetooth.framework:功能強(qiáng)大的藍(lán)牙開發(fā)框架忧换,要求設(shè)備必須支持藍(lán)牙4.0恬惯。

因?yàn)镚ameKit.framework和MultipeerConnectivity.framework框架都只能在iOS設(shè)備之間進(jìn)行數(shù)據(jù)傳輸,這個(gè)框架最大的特點(diǎn)就是完全基于BLE4.0標(biāo)準(zhǔn)并且支持非iOS設(shè)備亚茬。當(dāng)前BLE應(yīng)用相當(dāng)廣泛酪耳,不再僅僅是兩個(gè)設(shè)備之間的數(shù)據(jù)傳輸,它還有很多其他應(yīng)用市場(chǎng)刹缝,例如室內(nèi)定位碗暗、無線支付、智能家居等等梢夯,這也使得CoreBluetooth成為當(dāng)前最熱門的藍(lán)牙技術(shù)言疗。

今天我們只講目前使用最多的一種藍(lán)牙框架CoreBluetooth.framework,在做藍(lán)牙開發(fā)之前厨疙,最好先了解一些概念:

外圍設(shè)備和中央設(shè)備在CoreBluetooth中使用CBPeripheralManager和CBCentralManager表示洲守。

CBPeripheralManager:外圍設(shè)備通常用于發(fā)布服務(wù)、生成數(shù)據(jù)沾凄、保存數(shù)據(jù)梗醇。外圍設(shè)備發(fā)布并廣播服務(wù),告訴周圍的中央設(shè)備它的可用服務(wù)和特征撒蟀。

CBCentralManager:中央設(shè)備使用外圍設(shè)備的數(shù)據(jù)叙谨。中央設(shè)備掃描到外圍設(shè)備后會(huì)就會(huì)試圖建立連接,一旦連接成功就可以使用這些服務(wù)和特征保屯。

外圍設(shè)備和中央設(shè)備之間交互的橋梁是服務(wù)(CBService)和特征(CBCharacteristic)手负,二者都有一個(gè)唯一的標(biāo)識(shí)UUID(CBUUID類型)來唯一確定一個(gè)服務(wù)或者特征涤垫,每個(gè)服務(wù)可以擁有多個(gè)特征,下面是他們之間的關(guān)系:

服務(wù)(services):藍(lán)牙外設(shè)對(duì)外廣播的必定會(huì)有一個(gè)服務(wù)竟终,可能也有多個(gè)蝠猬,服務(wù)下面包含著一些特征,服務(wù)可以理解成一個(gè)模塊的窗口统捶;

特征(characteristic):存在于服務(wù)下面的榆芦,一個(gè)服務(wù)下面也可以存在多個(gè)特征,特征可以理解成具體實(shí)現(xiàn)功能的窗口喘鸟,一般特征都會(huì)有value匆绣,也就是特征值,特征是與外界交互的最小單位什黑;

UUID:可以理解成藍(lán)牙上的唯一標(biāo)識(shí)符(硬件上肯定不是這個(gè)意思崎淳,但是這樣理解便于我們開發(fā)),為了區(qū)分不同的服務(wù)和特征愕把,或者給服務(wù)和特征取名字拣凹,我們就用UUID來代表服務(wù)和特征。

藍(lán)牙連接可以大致分為以下幾個(gè)步驟

1.創(chuàng)建一個(gè)Central Manage中心設(shè)備管理器

2.搜索外圍設(shè)備

3.連接外圍設(shè)備

4.獲得外圍設(shè)備的服務(wù)

5.獲得服務(wù)的特征

6.從外圍設(shè)備讀數(shù)據(jù)

7.給外圍設(shè)備發(fā)送數(shù)據(jù)

首先我們先導(dǎo)入系統(tǒng)的BLE的框架 #import <CoreBluetooth/CoreBluetooth.h>

必須遵守2個(gè)協(xié)議<CBCentralManagerDelegate, CBPeripheralDelegate>

/*中心管理者*/

@property(strong,nonatomic)CBCentralManager *CM;

/*連接到的外設(shè)*/

@property (nonatomic, strong)CBPeripheral *peripheral;

//1.創(chuàng)建一個(gè)Central Manage中心設(shè)備管理器并設(shè)置當(dāng)前控制器視圖為代理

_CM = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

#pragma mark - CBCentralManager代理方法

//2.中心服務(wù)器狀態(tài)更新后

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {

NSString *message = nil;

switch (central.state) {

case CBManagerStateUnknown:

message = @"該設(shè)備藍(lán)牙狀態(tài)未知,請(qǐng)檢查系統(tǒng)設(shè)置";

break;

case CBManagerStateResetting:

message = @"該設(shè)備不支持藍(lán)牙功能,請(qǐng)檢查系統(tǒng)設(shè)置";

break;

case CBManagerStateUnsupported:

message = @"該設(shè)備不支持藍(lán)牙功能,請(qǐng)檢查系統(tǒng)設(shè)置";

break;

case CBManagerStateUnauthorized:

message = @"該設(shè)備藍(lán)牙未授權(quán),請(qǐng)檢查系統(tǒng)設(shè)置";

break;

case CBManagerStatePoweredOff:

message = @"該設(shè)備尚未打開藍(lán)牙,請(qǐng)?jiān)谠O(shè)置中打開";

break;

case CBManagerStatePoweredOn:

message = @"藍(lán)牙已經(jīng)成功開啟";

//開始掃描外圍設(shè)備

[self startScanPeripheralsWithServices];

break;

default:

message = @"該設(shè)備不支持藍(lán)牙功能或者該設(shè)備尚未打開藍(lán)牙,請(qǐng)檢查系統(tǒng)設(shè)置";

break;

}

if(message != nil && message.length != 0) {

[MBProgressHUD showSuccess:message toView:nil];

}

}

#pragma mark - 掃描外圍設(shè)備單獨(dú)寫一個(gè)方法

//掃描外圍設(shè)備方法

- (void)startScanPeripheralsWithServices {

//NO表示不會(huì)重復(fù)搜索已經(jīng)搜索到的設(shè)備

NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];

//當(dāng)?shù)弥{(lán)牙為開啟狀態(tài)時(shí)礼华,直接搜索周圍的藍(lán)牙設(shè)備

//開始搜索藍(lán)牙

[self.CM scanForPeripheralsWithServices:nil options:options];

}

//2.發(fā)現(xiàn)外設(shè)后調(diào)用的方法

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {

/**

* 第一個(gè)參數(shù):中心管理者

* 第二個(gè)參數(shù):外圍設(shè)備

* 第三個(gè)參數(shù):外圍設(shè)備攜帶的數(shù)據(jù)

* 第四個(gè)參數(shù):外圍設(shè)備發(fā)出的藍(lán)牙信號(hào)強(qiáng)度

**/

//在此處對(duì)我們的advertisementData(外圍設(shè)備攜帶的廣播數(shù)據(jù))進(jìn)行一些處理

//把廣播包advertisementData里的參數(shù)kCBAdvDataManufacturerData數(shù)據(jù)拼接Mac地址

NSString *facturerDataString = [[[[advertisementData[@"kCBAdvDataManufacturerData"] description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];

NSString *macAddress = [facturerDataString substringFromIndex:4];

/**

** 需要對(duì)連接到的外圍設(shè)備進(jìn)行過濾(外圍設(shè)備的名稱或者M(jìn)ac地址或者能區(qū)分的標(biāo)示UUID都可以)

** 1.信號(hào)強(qiáng)度(40以上才連接,80以上連接)

** 2.通過設(shè)備名(設(shè)備字符串前綴是 OBand)

** 我們的過濾規(guī)則自定義:譬如:有******前綴并且信號(hào)強(qiáng)度大于**

** if ([peripheral.name hasPrefix:@"Tv221u-169E665D"])

** 通過打印,我們知道RSSI一般是帶-的

**/

//此處設(shè)置需要的過濾規(guī)則

if ([macAddress isEqualToString:@"04a3169e665d"]) {

[self.CM stopScan];

//通常通過過濾,我們會(huì)得到一些外設(shè),然后將外圍設(shè)備儲(chǔ)存到我們的可變數(shù)組中

//將符合要求的設(shè)備進(jìn)行數(shù)據(jù)持久化,以便下面連接的時(shí)候使用

self.peripheral = peripheral;

//發(fā)現(xiàn)完之后就是進(jìn)行連接

[self.CM connectPeripheral:self.peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];

}

}

//3.連接外圍設(shè)備

//中心管理者連接外圍設(shè)備成功

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

/**

* 第一個(gè)參數(shù):中心管理者

* 第二個(gè)參數(shù):外圍設(shè)備

**/

//連接成功后,設(shè)置外圍設(shè)備的代理

self.peripheral.delegate = self;

//中心管理者連接外圍設(shè)備成功后停止掃以達(dá)到節(jié)省電量的目的

[self.CM stopScan];

//外圍設(shè)備發(fā)現(xiàn)服務(wù),傳nil代表不過濾

//這里會(huì)觸發(fā)外圍設(shè)備的代理方法

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

[self.peripheral discoverServices:nil];

}

//外圍設(shè)備連接失敗

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {

[MBProgressHUD showSuccess:@"外圍設(shè)備連接失敗" toView:nil];

}

//丟失連接

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {

[MBProgressHUD showSuccess:@"外圍設(shè)備丟失連接" toView:nil];

}

//4.獲得外圍設(shè)備的服務(wù)

#pragma mark - CBPeripheral Delegate

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error? {

if (error) {

[MBProgressHUD showSuccess:@"搜索服務(wù)時(shí)發(fā)生錯(cuò)誤" toView:nil];

DLog(@"搜索服務(wù)%@時(shí)發(fā)生錯(cuò)誤:%@", peripheral.name, [error localizedDescription]);

return;

}

if ([peripheral isEqual:self.peripheral]) {

//便利獲得的外圍設(shè)備的服務(wù)

for (CBService *services in peripheral.services) {

[peripheral discoverCharacteristics:nil forService:services];

}

}

}

//5.獲得服務(wù)的特征值

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

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

if (error) {

[MBProgressHUD showSuccess:@"搜索特征時(shí)發(fā)生錯(cuò)誤" toView:nil];

DLog(@"搜索特征%@時(shí)發(fā)生錯(cuò)誤:%@", service.UUID, [error localizedDescription]);

return;

}

if ([peripheral isEqual:self.peripheral]) {

//新建特征值數(shù)組

NSArray *characteristics = [service characteristics];

CBCharacteristic *characteristic;

?if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"aaaaaaaaaa"]]) {

for (characteristic in characteristics) {

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

if ([[characteristic UUID] isEqual:[CBUUID UUIDWithString:@"bbbbbbbbbbb"]]) {

//記錄特征

_cbchar = characteristic;

}

}

}

}

}

//訂閱的特征值發(fā)生改變會(huì)調(diào)用此方法

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

NSData *data = characteristic.value;

NSString *characteristicValue = [[[[characteristic.value description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];

//根據(jù)藍(lán)牙響應(yīng)的數(shù)據(jù)去判斷向特征下的藍(lán)牙發(fā)送命令

if ([str isEqualToString:@"c c c c c c c c c c"]) {

if ([_cbchar.UUID isEqual:[CBUUID UUIDWithString:@"FFE9"]]) {

NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0];

int8_t byte0 = 0x7B;

[data appendBytes:&byte0 length:sizeof(byte0)];

int8_t byte1 = 0x00;

[data appendBytes:&byte1 length:sizeof(byte1)];

int8_t byte2 = 0x00;

[data appendBytes:&byte2 length:sizeof(byte2)];

int8_t byte3 = 0x01;

[data appendBytes:&byte3 length:sizeof(byte3)];

int8_t byte4 = 0x00;

[data appendBytes:&byte4 length:sizeof(byte4)];

int8_t byte5 = 0xFF;

[data appendBytes:&byte5 length:sizeof(byte5)];

int8_t byte6 = 0x7D;

[data appendBytes:&byte6 length:sizeof(byte6)];

//發(fā)送命令

[_peripheral writeValue:data forCharacteristic:_cbchar type:CBCharacteristicWriteWithResponse];

}

}else if ([str isEqualToString:@"dddddddddddd"]) {

if ([_cbchar.UUID isEqual:[CBUUID UUIDWithString:@"1111111111"]]) {

NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0];

int8_t byte0 = 0x7B;

[data appendBytes:&byte0 length:sizeof(byte0)];

int8_t byte1 = 0x00;

[data appendBytes:&byte1 length:sizeof(byte1)];

int8_t byte2 = 0x00;

[data appendBytes:&byte2 length:sizeof(byte2)];

int8_t byte3 = 0x01;

[data appendBytes:&byte3 length:sizeof(byte3)];

int8_t byte4 = 0x01;

[data appendBytes:&byte4 length:sizeof(byte4)];

int8_t byte5 = 0xFE;

[data appendBytes:&byte5 length:sizeof(byte5)];

int8_t byte6 = 0x7D;

[data appendBytes:&byte6 length:sizeof(byte6)];

//發(fā)送命令

[_peripheral writeValue:data forCharacteristic:_cbchar type:CBCharacteristicWriteWithResponse];

}

}

}

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {

if (error) {

DLog(@"Error writing characteristic value: %@",

[error localizedDescription]);

}else {

DLog(@"發(fā)送數(shù)據(jù)成功");

[peripheral readValueForCharacteristic:characteristic];

}

}

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

if (error) {

DLog(@"更新特征值數(shù)據(jù): %@ 錯(cuò)誤: %@", characteristic.UUID,[error localizedDescription]);

}

//發(fā)送命令

if ([characteristicValue isEqualToString:@"123456789"]) {

//狀態(tài)

}else if ([characteristicValue isEqualToString:@"987654321"]) {

//狀態(tài)

}

}

補(bǔ)充說明:一個(gè)設(shè)備可以擁有不同的服務(wù)拗秘,一個(gè)服務(wù)下面可以對(duì)應(yīng)不同的特征值 總體思路就是去找某一個(gè)服務(wù)下的某一個(gè)特征值圣絮,發(fā)送不同命令去操作 具體命令看硬件那邊,iOS 不同安卓雕旨,mac地址一開始不像安卓那樣直接可以獲取扮匠,想要獲取就要把地址放到廣播包advertisementData里

如果有不明白的可以去看蘋果官方API,解釋的很清楚

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凡涩,一起剝皮案震驚了整個(gè)濱河市棒搜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌活箕,老刑警劉巖力麸,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異育韩,居然都是意外死亡克蚂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門筋讨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來埃叭,“玉大人,你說我怎么就攤上這事悉罕〕辔荩” “怎么了立镶?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)类早。 經(jīng)常有香客問我媚媒,道長(zhǎng),這世上最難降的妖魔是什么涩僻? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任欣范,我火速辦了婚禮,結(jié)果婚禮上令哟,老公的妹妹穿的比我還像新娘恼琼。我一直安慰自己,他們只是感情好屏富,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布晴竞。 她就那樣靜靜地躺著,像睡著了一般狠半。 火紅的嫁衣襯著肌膚如雪噩死。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天神年,我揣著相機(jī)與錄音已维,去河邊找鬼。 笑死已日,一個(gè)胖子當(dāng)著我的面吹牛垛耳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播飘千,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼堂鲜,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了护奈?” 一聲冷哼從身側(cè)響起缔莲,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霉旗,沒想到半個(gè)月后痴奏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搞旭,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡想帅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年弦蹂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矗积。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片具伍。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澄惊,死狀恐怖搪柑,靈堂內(nèi)的尸體忽然破棺而出薄腻,到底是詐尸還是另有隱情岛马,我是刑警寧澤棉姐,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布屠列,位于F島的核電站,受9級(jí)特大地震影響伞矩,放射性物質(zhì)發(fā)生泄漏笛洛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一乃坤、第九天 我趴在偏房一處隱蔽的房頂上張望苛让。 院中可真熱鬧,春花似錦湿诊、人聲如沸狱杰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仿畸。三九已至,卻和暖如春朗和,著一層夾襖步出監(jiān)牢的瞬間错沽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工眶拉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留千埃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓忆植,卻偏偏與公主長(zhǎng)得像放可,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唱逢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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