转载请注明本文地址:
http://www.jianshu.com/p/38a4c6451d93
最近公司在做一个iOS蓝牙项目,在开发的过程中简单整理了一些与之相关的基础知识,在这里分享一下。整理包括以下内容:
1、iOS蓝牙开发的关键词
2、蓝牙的简单介绍
3、CoreBluetooth框架
4、实现iOS蓝牙外设(
Demo
)
5、实现iOS蓝牙中心设备(
Demo
)
点击查看
Demo的运行gif图
,中心设备可以从外设读取数据,也可以向外设写入数据。外设也可以向中心设备发送数据。
PS:需要使用真机测试。
iOS的蓝牙开发是围绕着CoreBluetooth框架来实现的。
下面先从iOS蓝牙开发的基本概念说起。
中心设备:就是用来扫描周围蓝牙硬件的设备,比如通过你手机的蓝牙来扫描并连接智能手环,这时候你的手机就是中心设备。
外设:被扫描的设备。比如当你用手机的蓝牙扫描连接智能手环的时候,智能手环就是外设。
中心设备和外设
广播:就是外设不停的散播蓝牙信号,让中心设备可以扫描到。
外设广播
服务(services):外设广播和运行的时候会有服务,可以理解成一个功能模块,中心设备可以读取服务。外设可以有多个服务。
特征(characteristic):在服务中的一个单位,一个服务可以有多个特征,特征会有一个value,一般读写的数据就是这个value。
服务和特征.png
UUID:区分不同的服务和特征,可以理解为服务和特征的身份证。我们可以用UUID来挑选需要的服务和特征。
偷个懒:
蓝牙百科
蓝牙( Bluetooth® ):是一种短距离无线通信技术 ,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。蓝牙4.2发布于2014年12月2日,本文发布的时候,蓝牙的最新版本为4.2。
蓝牙开发层次图
如上图所示,iOS中的蓝牙开发框架CoreBluetooth处在蓝牙低功耗协议栈的上面,我们开发的时候只是使用CoreBluetooth这个框架,通过CoreBluetooth可以轻松实现外设或中心设备的开发。
CoreBluetooth可以分为两大模块,中心设备central,外设peripheral,它们俩各有自己的一套API供我们使用。
中心设备和外设使用.png
上图左边的就是中心设备的开发类,我们平时是使用CBCentralManager来进行相关操作。
CBCentralManager: 蓝牙中心设备管理类,用来统一调度中心设备的开发
CBPeripheral :蓝牙外设,例如蓝牙手环、心率监测仪。
CBService :蓝牙外设的服务,可以有0个或者多个服务。
CBCharacteristic :服务中的特征,每一个蓝牙服务中可以有0个或多个特征,特征中包含数据信息。
CBUUID:可以理解为服务或特征的身份证,可以用来选择需要的服务和特征。
右边是外设开发相关类,一般是围绕着CBPeripheralManager来进行编码。
CBPeripheralManager: 蓝牙外设开发时使用,用来开发蓝牙外设的中心管理类。
CBCentral:蓝牙中心设备,例如用来连接蓝牙手环的手机。
CBMutableService:外设开发的时候可以添加多个服务,所有这里用CBMutableService来创建添加服务。
CBMutableCharacteristic:每个服务中可以有多个特征,外设开发给服务添加特征的时候使用这个类。
CBATTRequest:读或者写请求。它的实例对象有一个value属性,用来装载外设进行蓝牙读取或写入请求时的数据。一般在外设写入或读取的回调方法中有这一个参数。
外设添加服务和特征
外设管理器
1、首先导入CoreBluetooth框架,并遵守协议
#import
// 遵守CBPeripheralManagerDelegate协议
@interface ViewController ()
2、创建外设管理对象,用一个属性来强引用这个对象。并且在创建的时候设置代理,声明放到哪个线程。
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
// 创建外设管理器,会回调peripheralManagerDidUpdateState方法
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
3、当创建CBPeripheralManager的时候,会回调判断蓝牙状态的方法。当蓝牙状态没问题的时候创建外设的Service(服务)和Characteristics(特征)。
/*
设备的蓝牙状态
CBManagerStateUnknown = 0, 未知
CBManagerStateResetting, 重置中
CBManagerStateUnsupported, 不支持
CBManagerStateUnauthorized, 未验证
CBManagerStatePoweredOff, 未启动
CBManagerStatePoweredOn, 可用
*/
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state == CBManagerStatePoweredOn) {
// 创建Service(服务)和Characteristics(特征)
[self setupServiceAndCharacteristics];
// 根据服务的UUID开始广播
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:SERVICE_UUID]]}];
}
}
可以先用宏来做两个标识字符串,用来创建服务和特征的UUID。
最终把创建好的特征放进服务,把服务放入中心管理器。
#define SERVICE_UUID @"CDD1"
#define CHARACTERISTIC_UUID @"CDD2"
/** 创建服务和特征 */
- (void)setupServiceAndCharacteristics {
// 创建服务
CBUUID *serviceID = [CBUUID UUIDWithString:SERVICE_UUID];
CBMutableService *service = [[CBMutableService alloc] initWithType:serviceID primary:YES];
// 创建服务中的特征
CBUUID *characteristicID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID];
CBMutableCharacteristic *characteristic = [
[CBMutableCharacteristic alloc]
initWithType:characteristicID
properties:
CBCharacteristicPropertyRead |
CBCharacteristicPropertyWrite |
CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable |
CBAttributePermissionsWriteable
];
// 特征添加进服务
service.characteristics = @[characteristic];
// 服务加入管理
[self.peripheralManager addService:service];
// 为了手动给中心设备发送数据
self.characteristic = characteristic;
}
注意CBCharacteristicPropertyNotify这个参数,只有设置了这个参数,在中心设备中才能订阅这个特征。
一般开发中可以设置两个特征,一个用来发送数据,一个用来接收中心设备写过来的数据,我们这里为了方便就只设置了一个特征。
最后用一个属性拿到这个特征,是为了后面单独发送数据的时候使用,数据的写入和读取最终还是要通过特征来完成。
4、当中心设备读取这个外设的数据的时候会回调这个方法。
/** 中心设备读取数据的时候回调 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
// 请求中的数据,这里把文本框中的数据发给中心设备
request.value = [self.textField.text dataUsingEncoding:NSUTF8StringEncoding];
// 成功响应请求
[peripheral respondToRequest:request withResult:CBATTErrorSuccess];
}
5、当中心设备写入数据的时候,外设会调用下面这个方法。
/** 中心设备写入数据的时候回调 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray
*)requests {
// 写入数据的请求
CBATTRequest *request = requests.lastObject;
// 把写入的数据显示在文本框中
self.textField.text = [[NSString alloc] initWithData:request.value encoding:NSUTF8StringEncoding];
}
6、还有一个主动给中心设备发送数据的方法。
/** 通过固定的特征发送数据到中心设备 */
- (IBAction)didClickPost:(id)sender {
BOOL sendSuccess = [self.peripheralManager updateValue:[self.textField.text dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.characteristic onSubscribedCentrals:nil];
if (sendSuccess) {
NSLog(@"数据发送成功");
}else {
NSLog(@"数据发送失败");
}
}
7、中心设备订阅成功的时候回调。
/** 订阅成功回调 */
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"%s",__FUNCTION__);
}
8、中心设备取消订阅的时候回调。
/** 取消订阅回调 */
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {