首先進(jìn)一則廣告:
藍(lán)牙技術(shù)聯(lián)盟(Bluetooth SIG)2010年7月7日宣布,正式采納藍(lán)牙4.0核心規(guī)范(Bluetooth Core Specification Version 4.0 ),并啟動(dòng)對(duì)應(yīng)的認(rèn)證計(jì)劃彤断。會(huì)員廠商可以提交其產(chǎn)品進(jìn)行測(cè)試赃梧,通過后將獲得藍(lán)牙4.0標(biāo)準(zhǔn)認(rèn)證紧憾。 該技術(shù)擁有極低的運(yùn)行和待機(jī)功耗结笨,使用一粒紐扣電池甚至可連續(xù)工作數(shù)年之久反惕。所以藍(lán)牙技術(shù)還是可以被長久利用的一種鏈接技術(shù)尝艘。
然后直接進(jìn)入正題。
大家也許會(huì)注意到一個(gè)問題姿染。安卓和安卓手機(jī)可以用藍(lán)牙相互鏈接背亥,但是蘋果和蘋果手機(jī)就不行。因?yàn)樘O果鏈接用的是airdrop悬赏,所以也就不需要藍(lán)牙傳輸大文件狡汉。大家也就忽略了這個(gè)現(xiàn)象。其實(shí)airdrop本質(zhì)也是藍(lán)牙建立鏈接的闽颇。盾戴。。兵多。尖啡。。剩膘。
今天主要就是用一款app去讀寫藍(lán)牙設(shè)備衅斩。應(yīng)用場(chǎng)景就是手機(jī)掃描外設(shè),鏈接外設(shè)怠褐,找到外設(shè)的服務(wù)和屬性畏梆,對(duì)服務(wù)和屬性進(jìn)行讀寫操作;
代碼實(shí)現(xiàn)流程。
1.建立中心管理控制對(duì)象
<#import <CoreBluetooth/CoreBluetooth.h>(導(dǎo)入頭文件)
<CBCentralManagerDelegate,CBPeripheralDelegate>繼承代理協(xié)議
//系統(tǒng)藍(lán)牙設(shè)備中心管理對(duì)象可以去掃描和鏈接外設(shè)
CBCentralManager *_manager;
//用于保存被發(fā)現(xiàn)設(shè)備
NSMutableArray *_discoverPeripherals;
//初始化并設(shè)置委托和線程隊(duì)列奠涌,最好一個(gè)線程的參數(shù)可以為nil宪巨,默認(rèn)會(huì)就main線程
manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
//創(chuàng)建外設(shè)數(shù)組,保存發(fā)現(xiàn)的設(shè)備铣猩。不保存設(shè)備會(huì)導(dǎo)致代理方法不可用
discoverPeripherals = [[NSMutableArray array];
2.掃描外設(shè),只有設(shè)備打開狀態(tài)茴丰,才可以掃描达皿。(錯(cuò)誤原因之一)
介紹一下
CBCentralManagerDelegate的主要代理方法。
必須實(shí)現(xiàn)的:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;//主設(shè)備狀態(tài)改變的委托贿肩,在初始化CBCentralManager的時(shí)候會(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è)的委托
//工程中的代碼不適合直接放出來汰规,就用示例demo中的了汤功。(感謝這些偉大的開發(fā)工作者)
這些狀態(tài)不需要我一一翻譯了吧,如果不懂可以私信我溜哮。我一定讓你自己查字典去滔金。。茂嗓。
-(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è)********** 一定要在設(shè)備開啟狀態(tài)再掃描外設(shè)*********餐茵。
/*
第一個(gè)參數(shù)nil就是掃描周圍所有的外設(shè),可以特定你想掃描到的符合要求的設(shè)備述吸。掃描到外設(shè)后會(huì)進(jìn)入
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
*/
[central scanForPeripheralsWithServices:nil options:nil];
break;
default:
break;
}
}
3.掃描完成忿族,進(jìn)入連接方法進(jìn)行設(shè)備連接。
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"當(dāng)掃描到設(shè)備:%@",peripheral.name);
//這里自己去設(shè)置下連接規(guī)則蝌矛,這里就是不同廠商特定設(shè)備鏈接的判斷規(guī)則道批。L是我喜歡的字母。
// if ([peripheral.name hasPrefix:@"L"]){
/*
一個(gè)主設(shè)備最多能連7個(gè)外設(shè)入撒,每個(gè)外設(shè)最多只能給一個(gè)主設(shè)備連接,連接成功隆豹,失敗,斷開會(huì)進(jìn)入各自的委托
//這個(gè)沒有驗(yàn)證過茅逮。沒有那么多測(cè)試設(shè)備噪伊。。氮唯。鉴吹。
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托
- (void)centra`lManager:(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)用6估!(錯(cuò)誤之一)
[discoverPeripherals addObject:peripheral];
[central connectPeripheral:peripheral options:nil];
// }
}
//連接到Peripherals-成功(其他操作不是我們想要的,自己做相關(guān)處理)
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ NSLog(@">>>連接到名稱為(%@)的設(shè)備-成功",peripheral.name); //設(shè)置的peripheral委托CBPeripheralDelegate //@interface ViewController : UIViewController[peripheral setDelegate:self];
//掃描外設(shè)Services良蒸,成功后會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
[peripheral discoverServices:nil];
}
4.接下來是發(fā)現(xiàn)設(shè)備的服務(wù)技扼,對(duì)相關(guān)服務(wù)中的特性進(jìn)行操作。
//掃描到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];
}
}
//掃描到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都是對(duì)于characteristic的描述纺棺,一般都是字符串榄笙,所以這里我們轉(zhuǎn)換成字符串去解析
NSLog(@"characteristic uuid:%@ value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
}
5.解析到這些數(shù)據(jù),就可以滿足日常計(jì)步祷蝌,心率等一系列只讀數(shù)據(jù)的操作茅撞。接下來是向外設(shè)寫數(shù)據(jù)。
//寫數(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(@"該字段不可寫睛挚!");
}
}
//訂閱外設(shè)特性的通知邪蛔。
//設(shè)置通知,數(shù)據(jù)通知會(huì)進(jìn)入:didUpdateValueForCharacteristic方法
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
//取消通知
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
//設(shè)置通知和取消通知均可以觸發(fā)此方法扎狱。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
6.操作完成 或者電量不足要進(jìn)行停止掃描斷開設(shè)備操作侧到。
//停止掃描并斷開連接
-(void)disconnectPeripheral:(CBCentralManager *)centralManager
peripheral:(CBPeripheral *)peripheral{
//停止掃描
[centralManager stopScan];
//斷開連接
[centralManager cancelPeripheralConnection:peripheral];
}
本來藍(lán)牙開發(fā)就需要硬件工程師定義好每個(gè)硬件對(duì)應(yīng)的服務(wù),每個(gè)服務(wù)的描述以及服務(wù)特性的屬性淤击。所以開發(fā)中的代碼一般都已經(jīng)定義好了匠抗。最重要的就是了解各種方法是什么作用。在各自開發(fā)的過程中是否必須用到污抬。為了讓大家有個(gè)跟更加進(jìn)一步的了解汞贸。接下來我再說一下服務(wù)特性的具體讀寫與訂閱绳军。當(dāng)然我也是搜索了很多資料。借鑒了很多大神的資源矢腻。當(dāng)時(shí)在github上下載了好多東西门驾。具體是哪位大神寫的也不曉得。反正很崇拜這些人多柑。奶是。。O(∩_∩)O哈哈哈~
我們看一下外設(shè)管理類
以上寫的是centralManager的內(nèi)容接下來是peripheralManager竣灌。
1.初始化外設(shè)管理者
//外設(shè)管理者
CBPeripheralManager *_peripheralManager;
//首先初始化外設(shè)管理者
peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];(默認(rèn)為主隊(duì)列)
//在你用過之后記得停止廣播
//[peripheralManager stopAdvertising];
2.配置藍(lán)牙讀寫的屬性以及描述
//注意此方法是在保證外設(shè)在CBPeripheralManagerStatePoweredOn狀態(tài)下才可調(diào)用***********
-(void)initService{
//characteristics字段描述
CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
/*
可以給主設(shè)備發(fā)送通知的特性
properties:CBCharacteristicPropertyNotify
許可特性可讀
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
/*
可讀寫的特性
properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable
*/
CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
//設(shè)置description
CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"name"];
[readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];
/*
只讀的Characteristic
properties:CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
//serviceOne初始化并加入兩個(gè)characteristics
CBMutableService *serviceOne = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
NSLog(@"%@",serviceOne.UUID);
[serviceOne setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];
//serviceTwo初始化并加入一個(gè)characteristics
CBMutableService *serviceTwo = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
[serviceTwo setCharacteristics:@[readCharacteristic]];
//添加后就會(huì)調(diào)用代理的
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
[peripheralManager addService:serviceOne];
[peripheralManager addService:serviceTwo];
}
3.外設(shè)管理者相關(guān)的代理方法介紹
//perihpheral添加了service就調(diào)用此方法
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
if (!error) {
//兩次都添加完成后才去發(fā)送廣播
//添加服務(wù)后可以在此向外界發(fā)出通告 調(diào)用完這個(gè)方法后會(huì)調(diào)用代理的
//-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
//開始發(fā)送廣播
[peripheralManager startAdvertising:@{
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
CBAdvertisementDataLocalNameKey : LocalNameKey
}
];
}
}
//peripheral開始發(fā)送advertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
//在這里做一些發(fā)送廣播時(shí)需要的記錄或者動(dòng)作聂沙。
}
//This method is invoked when a central configurescharacteristicto notify or indicate. * It should be used as a cue to start sending updates as the characteristic value changes.
//當(dāng)特性的值發(fā)生改變就會(huì)調(diào)用這個(gè)方法
//訂閱characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"訂閱了 %@的數(shù)據(jù)",characteristic.UUID);
//每秒執(zhí)行一次給主設(shè)備發(fā)送一個(gè)當(dāng)前時(shí)間的秒數(shù)(這里就是最主要的發(fā)送數(shù)據(jù)的方法)
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendDate:) userInfo:characteristic repeats:YES];
}
//發(fā)送數(shù)據(jù),發(fā)送當(dāng)前時(shí)間的秒數(shù)(這里自己定制)
-(BOOL)sendData:(NSTimer *)t {
CBMutableCharacteristic *characteristic = t.userInfo;
NSDateFormatter *dft = [[NSDateFormatter alloc]init];
[dft setDateFormat:@"ss"];
NSLog(@"%@",[dft stringFromDate:[NSDate date]]);
//執(zhí)行回應(yīng)Central通知數(shù)據(jù)
return [peripheralManager updateValue:[[dft stringFromDate:[NSDate date]] dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil];
}
//讀characteristics請(qǐng)求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
NSLog(@"didReceiveReadRequest");
//判斷是否有讀數(shù)據(jù)的權(quán)限
if (request.characteristic.properties & CBCharacteristicPropertyRead) {
NSData *data = request.characteristic.value;
[request setValue:data];
//對(duì)請(qǐng)求作出成功響應(yīng)
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
//寫characteristics請(qǐng)求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
NSLog(@"didReceiveWriteRequests");
CBATTRequest *request = requests[0];
//判斷是否有寫數(shù)據(jù)的權(quán)限
if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
//需要轉(zhuǎn)換成CBMutableCharacteristic對(duì)象才能進(jìn)行寫值
CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
c.value = request.value;
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
至此藍(lán)牙的整個(gè)連接發(fā)送數(shù)據(jù)的過程也就完成了帐偎。蘋果的文檔中也介紹的很清楚逐纬。如果你覺得某種情況需要操作但是本文沒有寫到的蛔屹∠鞣可以看看蘋果的文檔。也許你會(huì)發(fā)現(xiàn)更多利用起來很方便的方法兔毒。
學(xué)習(xí)新知識(shí)最好站在巨人的肩膀上漫贞。這樣可以少走彎路。感謝那些大公無私的為大家提供學(xué)習(xí)知識(shí)的大牛育叁。你覺得挺不錯(cuò)可以給個(gè)喜歡喲迅脐。動(dòng)動(dòng)手指關(guān)注我也是可以的,做個(gè)好基友也是可以的豪嗽,玩?zhèn)€lol也是可以的谴蔑,做人呢最重要是開心。龟梦。隐锭。哈哈哈哈