藍(lán)牙介紹
本文要介紹的CoreBluetooth,專門用于與BLE設(shè)備通訊荆永。并且現(xiàn)在很多藍(lán)牙設(shè)備都支持4.0,4.0以其低功耗著稱成艘,所以一般也叫BLE(Bluetooth Low Energy)盆顾,所以也是在iOS比較推薦的一種開發(fā)方法类咧。
基本概念
- 中心設(shè)備(Central):發(fā)起連接丐谋,用來(lái)掃描周圍藍(lán)牙硬件的設(shè)備还惠;
- 周邊設(shè)備(Peripheral):被連接的設(shè)備饲握;
- 服務(wù)(Service):特征和關(guān)系的集合,它封裝了設(shè)備的一部分的行為;
- 特征(Characteristic):可以理解成一個(gè)Service模塊具體提供哪些服務(wù)蚕键,特征會(huì)有一個(gè)value救欧,一般我們向藍(lán)牙設(shè)備寫入數(shù)據(jù)、從藍(lán)牙設(shè)備讀取數(shù)據(jù)就是這個(gè)value;
- UUID:區(qū)分不同服務(wù)和特征的唯一標(biāo)識(shí)锣光,使用該字端我們可以獲取我們想要的服務(wù)或者特征笆怠。
- 廣播:外部設(shè)備不停的散播的藍(lán)牙信號(hào),讓中心設(shè)備可以掃描到誊爹,也是我們開發(fā)中接收數(shù)據(jù)的入口蹬刷。
CoreBluetooth介紹
在CoreBluetooth中有兩個(gè)主要的部分,Central和Peripheral,CBPeripheralManager 作為周邊設(shè)備频丘。CBCentralManager作為中心設(shè)備办成。所有可用的iOS設(shè)備可以作為周邊設(shè)備(Peripheral)也可以作為中心設(shè)備(Central),但不可以同時(shí)既是周邊設(shè)備也是中心設(shè)備搂漠。
Central
作為 Central 模式使用的操作步驟 :
- 創(chuàng)建一個(gè) Central Manager迂卢;
- 發(fā)現(xiàn)周邊設(shè)備;
- 發(fā)現(xiàn)周邊設(shè)備的服務(wù)及特征桐汤;
- 讀取特征值或?qū)χ付ㄌ卣髟O(shè)置監(jiān)聽而克;
- 設(shè)置可寫入的特征值;
- 讀取特征的描述Descriptor惊科;
創(chuàng)建了一個(gè) tableView 來(lái)掃描到的周邊設(shè)備的名字拍摇、信號(hào)強(qiáng)度等;
導(dǎo)入CoreBluetooth
#import <CoreBluetooth/CoreBluetooth.h>
創(chuàng)建一個(gè)管理者centralManager馆截,給當(dāng)前類簽訂協(xié)議<cbcentralmanagerdelegate>:</cbcentralmanagerdelegate>
_centralManager=[[CBCentralManager alloc] initWithDelegate:self queue:nil];
實(shí)現(xiàn) centralManagerDidUpdateState: 監(jiān)測(cè)藍(lán)牙狀態(tài)變化:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBCentralManagerStatePoweredOff:
{
NSLog(@"藍(lán)牙功能未開啟");
}
break;
case CBCentralManagerStateUnsupported:
{
NSLog(@"不支持藍(lán)牙功能");
}
break;
case CBCentralManagerStatePoweredOn:
{
NSLog(@"藍(lán)牙功能已開啟");
}
break;
default:
break;
}
}
掃描周邊設(shè)備
[_centralManager scanForPeripheralsWithServices:nil options:nil];
實(shí)現(xiàn)協(xié)議方法獲取掃描到的周邊設(shè)備
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
__block BOOL isContain = NO;
SABLEInfoObject *bleInfo = [[SABLEInfoObject alloc] init];
bleInfo.name = peripheral.name ?: @"--";
bleInfo.rssi = RSSI;
bleInfo.deviceUUID = peripheral.identifier.UUIDString;
bleInfo.servicesCount = peripheral.services.count;
[self.dataArray enumerateObjectsUsingBlock:^(SABLEInfoObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.deviceUUID isEqualToString:bleInfo.deviceUUID]) {
[self.dataArray replaceObjectAtIndex:idx withObject:bleInfo];
isContain = YES;
}
}];
if (!isContain) {
[self.dataArray addObject:bleInfo];
[self.peripheralList addObject:peripheral];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kDiscoverPeripheralRefreshNotificationName object:self.dataArray];
}
在創(chuàng)建的 tableView 中實(shí)時(shí)顯示 peripheral 的信號(hào)強(qiáng)度充活、名稱、服務(wù)個(gè)數(shù)蜡娶,選取某一個(gè)發(fā)起連接:
[_centralManager connectPeripheral:self.peripheralList[index] options:nil];
實(shí)現(xiàn)協(xié)議方法 centralManager:didConnectPeripheral 在連接成功時(shí)去獲取當(dāng)前設(shè)備包含的所有服務(wù):
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"%@ Connect Success", peripheral.name);
peripheral.delegate = self;
[peripheral discoverServices:nil];
}
協(xié)議方法 didDiscoverServices混卵,遍歷服務(wù),查詢服務(wù)下的特征:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
NSLog(@"%@ Discover Services Success", peripheral.name);
[peripheral.services enumerateObjectsUsingBlock:^(CBService * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"Service UUID: %@", obj.UUID.UUIDString);
[peripheral discoverCharacteristics:nil forService:obj];
}];
}
協(xié)議方法 didDiscoverCharacteristicsForService窖张,遍歷當(dāng)前服務(wù)下的特征幕随,讀取特征值或者監(jiān)測(cè)特征值,還可獲取特征的Descriptor:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
[service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"\nservicesUUID: %@\nCharacteristicUUID: %@", service.UUID.UUIDString, obj);
//讀取特征
// if ([obj.UUID.UUIDString isEqualToString:@"2A19"]) {
// [peripheral readValueForCharacteristic:obj];
// }
//
// if ([service.UUID.UUIDString isEqualToString:@"180A"]) {
// [peripheral readValueForCharacteristic:obj];
// }
[peripheral readValueForCharacteristic:obj];
if (obj.properties == CBCharacteristicPropertyWrite) {
NSLog(@"writeValue");
[peripheral writeValue:[@"testValue " dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:obj type:CBCharacteristicWriteWithResponse];
}
//監(jiān)測(cè)特征
// [peripheral setNotifyValue:YES forCharacteristic:obj];
// 外設(shè)發(fā)現(xiàn)特征的描述
[peripheral discoverDescriptorsForCharacteristic:obj];
}];
}
讀取或者監(jiān)測(cè)的特征值可在協(xié)議方法 didUpdateValueForCharacteristic 查看,如下宿接,讀取電量值赘淮,2A19為電量值特征的UUID:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
if ([characteristic.UUID.UUIDString isEqualToString:@"2A19"]) {
NSString *hexStr = [SADataConvertManager convertDataToHexStr:characteristic.value];
NSUInteger batteryLevel = [[SADataConvertManager convertHexStrToDecimalString:hexStr] integerValue];
NSLog(@"\nCharacteristicUUID: %@\nCharacteristicValue: %@", characteristic.UUID, [@(batteryLevel).stringValue stringByAppendingString:@"%"]);
}
}
發(fā)現(xiàn)的 Descriptor 需要實(shí)現(xiàn)協(xié)議方法讀仍肌:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
[characteristic.descriptors enumerateObjectsUsingBlock:^(CBDescriptor * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"\nCharacteristicUUID: %@\nDescriptor: %@", characteristic.UUID, obj.value);
}];
}
周邊設(shè)備讀取信號(hào)強(qiáng)度:
[peripheral readRSSI]; //讀取信號(hào)強(qiáng)度
實(shí)現(xiàn)協(xié)議方法 didReadRSSI 獲取讀取的信號(hào)強(qiáng)度:
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error {
NSLog(@"信號(hào)強(qiáng)度:%@", RSSI.stringValue);
}
寫入特征值:
if (obj.properties == CBCharacteristicPropertyWrite) {
[peripheral writeValue:[@"testValue " dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:obj type:CBCharacteristicWriteWithResponse];
}
Peripheral
作為 Peripheral 模式使用的操作步驟 :
- 創(chuàng)建一個(gè) Peripheral Manager;
- 為外設(shè)添加服務(wù)梢卸;
- 發(fā)送廣播
創(chuàng)建一個(gè)管理者peripheralManager走诞,給當(dāng)前類簽訂協(xié)議<cbperipheralmanagerdelegate>:</cbperipheralmanagerdelegate>
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
實(shí)現(xiàn) centralManagerDidUpdateState: 獲取藍(lán)牙狀態(tài)變化:
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
{
NSLog(@"藍(lán)牙已打開");
[self setupPeripheral];
}
break;
case CBPeripheralManagerStatePoweredOff:
NSLog(@"藍(lán)牙已關(guān)閉");
break;
case CBPeripheralManagerStateUnsupported:
NSLog(@"不支持藍(lán)牙");
break;
default:
break;
}
}
為藍(lán)牙添加服務(wù)
- (void)setupPeripheral {
CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"] properties:CBCharacteristicPropertyRead value:[@"123456789" dataUsingEncoding:NSUTF8StringEncoding] permissions:CBAttributePermissionsReadable];
CBMutableService *myService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"12AB"] primary:YES];
myService.characteristics = @[myCharacteristic];
[_peripheralManager addService:myService];
}
添加服務(wù)成功執(zhí)行代理方法 didAddService :
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
[service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"\nserviceUUID: %@\nCharacteristicUUID: %@\nCharacteristicValue: %@", service.UUID.UUIDString, obj.UUID.UUIDString, [[NSString alloc] initWithData:obj.value encoding:NSUTF8StringEncoding]);
}];
}
打印結(jié)果:
serviceUUID: 12AB
CharacteristicUUID: 71DA3FD1-7E10-41C1-B16F-4430B506CDE7
CharacteristicValue: 123456789
發(fā)送廣播:
- (void)setupPeripheralAdvertising {
if (!_peripheralManager.isAdvertising) {
CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"] properties:CBCharacteristicPropertyRead value:[@"123456789" dataUsingEncoding:NSUTF8StringEncoding] permissions:CBAttributePermissionsReadable];
CBMutableService *myService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"12AB"] primary:YES];
myService.characteristics = @[myCharacteristic];
[_peripheralManager addService:myService];
[_peripheralManager startAdvertising:@{
CBAdvertisementDataLocalNameKey : @"測(cè)試設(shè)備",
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"12AB"]]
}];
[SAAudioSound sa_playPassSound];
}else {
[_peripheralManager stopAdvertising];
[SAAudioSound sa_playWarningSound];
}
}
廣播發(fā)送開啟成功后會(huì)走協(xié)議方法:peripheralManagerDidStartAdvertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error {
NSLog(@"正在廣播");
}
當(dāng)連接到中心設(shè)備時(shí)便可接收讀或?qū)懙恼?qǐng)求,接收到請(qǐng)求時(shí)會(huì)走相應(yīng)的協(xié)議方法:
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
}
附:
GATT服務(wù)UUID: https://www.bluetooth.com/specifications/gatt/services
GATT特征UUID: https://www.bluetooth.com/specifications/gatt/characteristics