一、蓝牙开发引入
情景 : 蓝牙(Center)监听心跳仪(Peripheral)发送数据,发送到服务器。
心跳仪->外设(被连接的设备),蓝牙->中心(发起连接)
类似,客户端和服务器之间的关系(ios设备和非ios设备之间的交互)。
二、关于蓝牙的一些术语
1、Center : 中心 (发起连接的是中心)
2、Peripheral:外设(被链接的设备称之为外设)
3、service and characteristic (服务和特征)
每个设备都会提供服务和特征。每个外设都会提供很多服务,每个服务都会包含很多字段,这些字段都是有权限的,权限大致为:read/write/notify,就是我们链接设备后具体需要操作的内容.
4、Description : 每个characteristic可以对应一个或多个Description,用于描述特征(characteristic)的信息或属性。
5、BLE: blue low energy 蓝牙4.0低耗电
6、4.0 BLE : 开发使用CoreBluetooth 框架 (ios6.0以上)
7、MFL认证: make for ipad /iphone/ itouch (专门为苹果设备制作的设备)
三、蓝牙的中心模式、外设模式、工作状态
1、中心模式流程
主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般来说,外设(蓝牙设备,比如智能手环之类的东西), 会由硬件工程师开发好,并定义好设备提供的服务,每个服务对于的特征,每个特征的属性(只读,只写,通知等等),本文例子的业务场景,就是用一手机app去读写蓝牙设备。
( 1 ) 建立中心角色
( 2 ) 扫描外设(scan)
( 3 ) 链接外设 (connect)
( 4 ) 获取外设中的服务和特征
获取外设的server
获取外设的characteristic,获取characteristic的Description值
( 5 ) 与外设做数据交互
( 6 ) 订阅 characteristic 通知
( 7 ) 断开链接(disconnect)
2、外设模式流程
( 1 ) 启动 Peripheral 管理对象
( 2 ) 设置 Peripheral的 服务、特征、描述、权限
( 3 ) 设置处理订阅、取消订阅、读写characteristic(特征)委托方法
3、工作状态
( 1 ) 准备
( 2 ) 广播
( 3 ) 监听扫描
( 4 ) 发起连接
( 5 ) 已经连接
四、Center模式代码解析
1、准备工作
导入#import <CoreBluetooth/CoreBluetooth.h>
,声明系统蓝牙设备管理者和保存外设动态数组,遵循代理<CBCentralManagerDelegate,CBPeripheralDelegate>
,当连接外设成功后,不仅要保留外设,而且之后要与外设交互,就必须也遵循外设的一些代理方法,并设置外设对象的代理对象。
//系统狼牙设备管理者
CBCentralManager *manager;
// 用于保存被发现的设备(外设)
NSMutableArray *peripherals;
2、建立中心角色,并监听蓝牙设备状态,扫描外设
/*
* 1和2和3、建立角色中心,并确定蓝牙打开状态下,扫描外设
* 注意一个细节: 请特别注意,扫描到目标外设一定要保存!
* 如果不保存,会影响到后面的方法执行,
* 导致无法连接和对外设后续的操作。
*
*/
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"4.0 BLE ";
//初始化,并设置委托和线程队列
manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
}//视图加载
查看作为center 的蓝牙的状态,并当蓝牙打开的时候,扫描外设
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state) {
case CBCentralManagerStateUnknown:
break;
case CBCentralManagerStateResetting:
break;
case CBCentralManagerStateUnsupported:
break;
case CBCentralManagerStateUnauthorized:
break;
case CBCentralManagerStatePoweredOff:
NSLog(@"蓝牙关闭");
break;
case CBCentralManagerStatePoweredOn:
NSLog(@"蓝牙打开");
//扫描外设备(第一个参数为nil就是扫描所有的设备)
[manager scanForPeripheralsWithServices:nil options:nil];
break;
default:
break;
}
}//蓝牙状态
以下方法,分别是扫描外设,并连接外设成功、失败、外设断开连接的代理方法
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(@"发现附近蓝牙:%@---%@",peripheral.name,peripheral.identifier);
if ([peripheral.name hasPrefix:@"C"]) {
/*
找到设备,并持有他,否则CBCentralManager不会保存peripheral,
那么CBPeripheralDelegate中的代理方法也不会被调用
*/
[peripherals addObject:peripheral];
/*
一个主设别最多链接7个外设,每个外设最多只能给一个主设链接
链接成功、失败、断开就会进入各自的委托
*/
//链接设备
[manager connectPeripheral:peripheral options:nil];
}
}//扫描到设备并过滤外设,进而建立连接
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"链接外设%@成功",peripheral.name);
// 设置外设代理
[peripheral setDelegate:self];
// 扫描外设的 services
[peripheral discoverServices:nil];
}// 与外设链接成功
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);
}//链接失败
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@">>>外设连接断开连接 %@: %@\n", [peripheral name], [error localizedDescription]);
}// 外设断开连接
3、当连接成功,扫描外设的服务和特征(也就是遵循外设的几个代理方法)
/*
设备连接成功后,就可以扫描设备的服务了,同样是通过委托形式,扫描到结果后会进入委托方法。但是这个委托已经不再是主设备的委托(CBCentralManagerDelegate),而是外设的委托(CBPeripheralDelegate),这个委托包含了主设备与外设交互的许多 回叫方法,包括获取services,获取characteristics,获取characteristics的值,获取characteristics的Descriptor,和Descriptor的值,写数据,读rssi,用通知的方式订阅数据等等
*/
pragma mark - Peripheral 代理方法
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
NSLog(@">>>扫描到服务:%@",peripheral.services);
if (error) {
NSLog(@"%@",[error localizedDescription]);
return;
}
for (CBService *service in peripheral.services) {
/*
* 扫描每个服务的 service 进而获取 characteristics 调用下面的代理方法
*
* -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
*/
[peripheral discoverCharacteristics:nil forService:service];
}
}//扫描到服务
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error) {
NSLog(@"%@",[error localizedDescription]);
}
/*
* 读取Characteristic 的值
* 进入下面代理方法
* - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
*
*/
for (CBCharacteristic *Characteristic in service.characteristics) {
//NSLog(@"%@----%@",service.UUID,Characteristic.UUID);
[peripheral readValueForCharacteristic:Characteristic];
}
/*
* 读取 Characteristic 的 Descriptors
*
*/
for (CBCharacteristic *Characteristic in service.characteristics) {
[peripheral discoverDescriptorsForCharacteristic:Characteristic];
}
}//每个服务的特征值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
// characteristic 的value值是NSData
NSLog(@"%@",characteristic.value);
}//获取的charateristic的值
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error
{
for (CBDescriptor *des in characteristic.descriptors) {
NSLog(@"%@",des.UUID);
}
}//搜索到Characteristic的Descriptors
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error
{
//这个descriptor都是对于characteristic的描述,一般都是字符串,所以这里我们转换成字符串去解析
NSLog(@"characteristic uuid:%@ value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
}//获取 Descriptors的值
5、与外设进行交互(UE)
-(void)writeCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic
value:(NSData *)value
{
//打印出 characteristic 的权限,可以看到有很多种,这是一个NS_OPTIONS,就是可以同时用于好几个值,常见的有read,write,notify,indicate,知知道这几个基本就够用了,前连个是读写权限,后两个都是通知,两种不同的通知方式。
/*
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的权限才可以写
if(characteristic.properties & CBCharacteristicPropertyWrite){
/*
最好一个type参数可以为CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,区别是是否会有反馈
*/
[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}else{
NSLog(@"该字段不可写!");
}
}//写数据
6、订阅Characteristic通知(订阅和取消)
-(void)notifyCharacteristic:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic
{
//设置通知,数据通知会进入:didUpdateValueForCharacteristic方法
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}//设置通知
-(void)cancelNotifyCharacteristic:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic
{
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
}//取消通知
7、与外设断开连接
-(void)disconnectPeripheral:(CBCentralManager *)centralManager peripheral:(CBPeripheral *)peripheral
{
//停止扫描
[centralManager stopScan];
//断开连接
[centralManager cancelPeripheralConnection:peripheral];
}// 停止扫描和断开连接
提醒 : 关于 4.0BLE,github有人专门进行了二次封装,可通过cocoapods进行下载
pod 'BabyBluetooth','~> 0.6.0'
开发导入 #import "BabyBluetooth.h"