ios藍(lán)牙開發(fā),ios連接外設(shè)的代碼實(shí)現(xiàn)

這里我們具體說明一下中心模式的應(yīng)用場景。主設(shè)備(手機(jī)去掃描連接外設(shè),發(fā)現(xiàn)外設(shè)服務(wù)和屬性,操作服務(wù)和屬性的應(yīng)用。一般來說,外設(shè)(藍(lán)牙設(shè)備,比如智能手環(huán)之類的東西), 會(huì)由硬件工程師開發(fā)好,并定義好設(shè)備提供的服務(wù),每個(gè)服務(wù)對于的特征,每個(gè)特征的屬性(只讀,只寫,通知等等),本文例子的業(yè)務(wù)場景,就是用一手機(jī)app去讀寫藍(lán)牙設(shè)備。

ios連接外設(shè)的代碼實(shí)現(xiàn)流程

建立中心角色

掃描外設(shè)(discover)

連接外設(shè)(connect)

掃描外設(shè)中的服務(wù)和特征(discover)

4.1 獲取外設(shè)的services

4.2 獲取外設(shè)的Characteristics,獲取Characteristics的值,獲取Characteristics的Descriptor和Descriptor的值

與外設(shè)做數(shù)據(jù)交互(explore and interact)

訂閱Characteristic的通知

斷開連接(disconnect)

準(zhǔn)備環(huán)境

1 xcode

2 開發(fā)證書和手機(jī)(藍(lán)牙程序需要使用使用真機(jī)調(diào)試,使用模擬器也可以調(diào)試,但是方法很蛋疼,我會(huì)放在最后說)

3 藍(lán)牙外設(shè)

實(shí)現(xiàn)步驟

1 導(dǎo)入CoreBluetooth頭文件,建立主設(shè)備管理類,設(shè)置主設(shè)備委托

#import

@interface ViewController : UIViewController<CBCentralManagerDelegate>

@interface ViewController (){

//系統(tǒng)藍(lán)牙設(shè)備管理對象,可以把他理解為主設(shè)備,通過他,可以去掃描和鏈接外設(shè)

CBCentralManager *manager;

//用于保存被發(fā)現(xiàn)設(shè)備

NSMutableArray *peripherals;

}

- (void)viewDidLoad {

[super viewDidLoad];

/*

設(shè)置主設(shè)備的委托,CBCentralManagerDelegate

必須實(shí)現(xiàn)的:

- (void)centralManagerDidUpdateState:(CBCentralManager *)central;//主設(shè)備狀態(tài)改變的委托,在初始化CBCentralManager的適合會(huì)打開設(shè)備,只有當(dāng)設(shè)備正確打開后才能使用

其他選擇實(shí)現(xiàn)的委托中比較重要的:

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; //找到外設(shè)的委托

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開外設(shè)的委托

*/

//初始化并設(shè)置委托和線程隊(duì)列,最好一個(gè)線程的參數(shù)可以為nil,默認(rèn)會(huì)就main線程

manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];

2 掃描外設(shè)(discover),掃描外設(shè)的方法我們放在centralManager成功打開的委托中,因?yàn)橹挥性O(shè)備成功打開,才能開始掃描,否則會(huì)報(bào)錯(cuò)。

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

switch (central.state) {

case CBCentralManagerStateUnknown:

NSLog(@">>>CBCentralManagerStateUnknown");

break;

case CBCentralManagerStateResetting:

NSLog(@">>>CBCentralManagerStateResetting");

break;

case CBCentralManagerStateUnsupported:

NSLog(@">>>CBCentralManagerStateUnsupported");

break;

case CBCentralManagerStateUnauthorized:

NSLog(@">>>CBCentralManagerStateUnauthorized");

break;

case CBCentralManagerStatePoweredOff:

NSLog(@">>>CBCentralManagerStatePoweredOff");

break;

case CBCentralManagerStatePoweredOn:

NSLog(@">>>CBCentralManagerStatePoweredOn");

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

/*

第一個(gè)參數(shù)nil就是掃描周圍所有的外設(shè),掃描到外設(shè)后會(huì)進(jìn)入

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

*/

[manager scanForPeripheralsWithServices:nil options:nil];

break;

default:

break;

}

}

//掃描到設(shè)備會(huì)進(jìn)入方法

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

NSLog(@"當(dāng)掃描到設(shè)備:%@",peripheral.name);

//接下來可以連接設(shè)備

}

3 連接外設(shè)(connect)

//掃描到設(shè)備會(huì)進(jìn)入方法

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

//接下連接我們的測試設(shè)備,如果你沒有設(shè)備,可以下載一個(gè)app叫l(wèi)ightbule的app去模擬一個(gè)設(shè)備

//這里自己去設(shè)置下連接規(guī)則,我設(shè)置的是P開頭的設(shè)備

if ([peripheral.name hasPrefix:@"P"]){

/*

一個(gè)主設(shè)備最多能連7個(gè)外設(shè),每個(gè)外設(shè)最多只能給一個(gè)主設(shè)備連接,連接成功,失敗,斷開會(huì)進(jìn)入各自的委托

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開外設(shè)的委托

*/

//找到的設(shè)備必須持有它,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用!!

[peripherals addObject:peripheral];

//連接設(shè)備

[manager connectPeripheral:peripheral options:nil];

}

}

//連接到Peripherals-成功

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

{

NSLog(@">>>連接到名稱為(%@)的設(shè)備-成功",peripheral.name);

}

//連接到Peripherals-失敗

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

{

NSLog(@">>>連接到名稱為(%@)的設(shè)備-失敗,原因:%@",[peripheral name],[error localizedDescription]);

}

//Peripherals斷開連接

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

NSLog(@">>>外設(shè)連接斷開連接 %@: %@\n", [peripheral name], [error localizedDescription]);

}

有一點(diǎn)非常容易出錯(cuò),大家請注意。在 didDiscoverPeripheral這個(gè)委托中有這一行

//找到的設(shè)備必須持有它,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用!!

[peripherals addObject:peripheral];

請?zhí)貏e注意,如果不保存,會(huì)影響到后面的方法執(zhí)行,這個(gè)地方很多人出錯(cuò),在我的藍(lán)牙交流群中每天幾乎都會(huì)因?yàn)檫@個(gè)問題導(dǎo)致無法連接和對外設(shè)后續(xù)的操作。

大家也可以看一下這個(gè)委托在xcode中的說明,重點(diǎn)看@discussion中的內(nèi)容,里面特別指出了需要retained對象。

/*!

@method centralManager:didDiscoverPeripheral:advertisementData:RSSI:

*

@param central The central manager providing this update.

@param peripheral A CBPeripheral object.

@param advertisementData A dictionary containing any advertisement and scan response data.

@param RSSI The current RSSI ofperipheral, in dBm. A value of 127 is reserved and indicates the RSSI

was not available.

*

@discussion This method is invoked while scanning, upon the discovery ofperipheralbycentral. A discovered peripheral must

be retained in order to use it; otherwise, it is assumed to not be of interest and will be cleaned up by the central manager. For

a list ofadvertisementDatakeys, see {@link CBAdvertisementDataLocalNameKey} and other similar constants.

*

@seealso CBAdvertisementData.h

*

*/

(void)centralManager:(CBCentralManager)central didDiscoverPeripheral:(CBPeripheral)peripheral advertisementData:(NSDictionary, id>)advertisementData RSSI:(NSNumber *)RSSI;

4掃描外設(shè)中的服務(wù)和特征(discover)

設(shè)備連接成功后,就可以掃描設(shè)備的服務(wù)了,同樣是通過委托形式,掃描到結(jié)果后會(huì)進(jìn)入委托方法。但是這個(gè)委托已經(jīng)不再是主設(shè)備的委托(CBCentralManagerDelegate),而是外設(shè)的委托(CBPeripheralDelegate),這個(gè)委托包含了主設(shè)備與外設(shè)交互的許多 回叫方法,包括獲取services,獲取characteristics,獲取characteristics的值,獲取characteristics的Descriptor,和Descriptor的值,寫數(shù)據(jù),讀rssi,用通知的方式訂閱數(shù)據(jù)等等。

4.1獲取外設(shè)的services

//連接到Peripherals-成功

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

{

NSLog(@">>>連接到名稱為(%@)的設(shè)備-成功",peripheral.name);

//設(shè)置的peripheral委托CBPeripheralDelegate

//@interface ViewController : UIViewController<CBCentralManagerDelegate,CBPeripheralDelegate>

[peripheral setDelegate:self];

//掃描外設(shè)Services,成功后會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{

[peripheral discoverServices:nil];

}

//掃描到Services

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

//? NSLog(@">>>掃描到服務(wù):%@",peripheral.services);

if (error)

{

NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);

return;

}

for (CBService *service in peripheral.services) {

NSLog(@"%@",service.UUID);

//掃描每個(gè)service的Characteristics,掃描到后會(huì)進(jìn)入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error

[peripheral discoverCharacteristics:nil forService:service];

}

}

4.2獲取外設(shè)的Characteristics,獲取Characteristics的值,獲取Characteristics的Descriptor和Descriptor的值

//掃描到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)

{

NSLog(@"service:%@ 的 Characteristic: %@",service.UUID,characteristic.UUID);

}

//獲取Characteristic的值,讀到數(shù)據(jù)會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error

for (CBCharacteristic *characteristic in service.characteristics){

{

[peripheral readValueForCharacteristic:characteristic];

}

}

//搜索Characteristic的Descriptors,讀到數(shù)據(jù)會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error

for (CBCharacteristic *characteristic in service.characteristics){

[peripheral discoverDescriptorsForCharacteristic:characteristic];

}

}

//獲取的charateristic的值

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

//打印出characteristic的UUID和值

//!注意,value的類型是NSData,具體開發(fā)時(shí),會(huì)根據(jù)外設(shè)協(xié)議制定的方式去解析數(shù)據(jù)

NSLog(@"characteristic uuid:%@? value:%@",characteristic.UUID,characteristic.value);

}

//搜索到Characteristic的Descriptors

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

//打印出Characteristic和他的Descriptors

NSLog(@"characteristic uuid:%@",characteristic.UUID);

for (CBDescriptor *d in characteristic.descriptors) {

NSLog(@"Descriptor uuid:%@",d.UUID);

}

}

//獲取到Descriptors的值

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

//打印出DescriptorsUUID 和value

//這個(gè)descriptor都是對于characteristic的描述,一般都是字符串,所以這里我們轉(zhuǎn)換成字符串去解析

NSLog(@"characteristic uuid:%@? value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);

}

5 把數(shù)據(jù)寫到Characteristic中

//寫數(shù)據(jù)

-(void)writeCharacteristic:(CBPeripheral *)peripheral

characteristic:(CBCharacteristic *)characteristic

value:(NSData *)value{

//打印出 characteristic 的權(quán)限,可以看到有很多種,這是一個(gè)NS_OPTIONS,就是可以同時(shí)用于好幾個(gè)值,常見的有read,write,notify,indicate,知知道這幾個(gè)基本就夠用了,前連個(gè)是讀寫權(quán)限,后兩個(gè)都是通知,兩種不同的通知方式。

/*

typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {

CBCharacteristicPropertyBroadcast? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x01,

CBCharacteristicPropertyRead? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x02,

CBCharacteristicPropertyWriteWithoutResponse? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x04,

CBCharacteristicPropertyWrite? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x08,

CBCharacteristicPropertyNotify? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x10,

CBCharacteristicPropertyIndicate? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x20,

CBCharacteristicPropertyAuthenticatedSignedWrites? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x40,

CBCharacteristicPropertyExtendedProperties? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x80,

CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)? ? ? ? = 0x100,

CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)? ? = 0x200

};

*/

NSLog(@"%lu", (unsigned long)characteristic.properties);

//只有 characteristic.properties 有write的權(quán)限才可以寫

if(characteristic.properties & CBCharacteristicPropertyWrite){

/*

最好一個(gè)type參數(shù)可以為CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,區(qū)別是是否會(huì)有反饋

*/

[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];

}else{

NSLog(@"該字段不可寫!");

}

}

6 訂閱Characteristic的通知

//設(shè)置通知

-(void)notifyCharacteristic:(CBPeripheral *)peripheral

characteristic:(CBCharacteristic *)characteristic{

//設(shè)置通知,數(shù)據(jù)通知會(huì)進(jìn)入:didUpdateValueForCharacteristic方法

[peripheral setNotifyValue:YES forCharacteristic:characteristic];

}

//取消通知

-(void)cancelNotifyCharacteristic:(CBPeripheral *)peripheral

characteristic:(CBCharacteristic *)characteristic{

[peripheral setNotifyValue:NO forCharacteristic:characteristic];

}

7 斷開連接(disconnect)

//停止掃描并斷開連接

-(void)disconnectPeripheral:(CBCentralManager *)centralManager

peripheral:(CBPeripheral *)peripheral{

//停止掃描

[centralManager stopScan];

//斷開連接

[centralManager cancelPeripheralConnection:peripheral];

}

8 模擬器藍(lán)牙調(diào)試,慎用,最好還是用真機(jī)去調(diào)試。

由于在iPhone 4s之后的iOS才支持BLE,新一代的這些iOS設(shè)備又都不便宜,在做測試的時(shí)候,用iOS模擬器進(jìn)行調(diào)試,可以節(jié)約一些開發(fā)成本。怎么在iOS模擬器上調(diào)試BLE,

蘋果最初給出的說明是,支持BLE的mac機(jī)子上可以用模擬器進(jìn)行調(diào)試,并給出了一份技術(shù)文檔(傳送門),惡心的是,后來蘋果抽風(fēng),又把這份文檔移除,

并且把iOS 7.0的模擬器上對BLE的支持也移除掉了(難道是想讓大家多買設(shè)備測試?Apple sucks.)后面,網(wǎng)上搜了一下,解決辦法如下:

1. 買一個(gè)CSR藍(lán)牙4.0 USB適配器(某寶上大概30塊錢),在機(jī)子上插入該物(你懂的)

2. 在Terminal下敲入sudo nvram bluetoothHostControllerSwitchBehavior="never" , 重啟Mac。

3. 用XCode 4.6調(diào)試代碼,在iOS 6.1的模擬器上跑程序(用XCode 5.0跑iOS 7.0模擬器會(huì)拋異常,原因上面詳訴過了,Apple sucks,你懂的)

如何降低模擬器的IOS版本呢?

XCode->Preferences->Downloads里面有很多simulators你可以下載

選擇個(gè)6.1的下載好了

github,地址是:https://github.com/coolnameismy/demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,720評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,690評論 2 370

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