工作模式:
-
当 central 和 peripheral 通信时,绝大部分操作都在 central 这边。此时,central 被描述为 CBCentralManager,这个类提供了扫描、寻找、连接 peripheral(被描述为 CBPeripheral)的方法。
- 下图标示了 central 和 peripheral 在 Core Bluetooth 中的表示方式:
- 当你操作 peripheral 的时候,实际上是在和它的 service 和 characteristic 打交道,这两个分别由 CBService 和 CBCharacteristic 表示。
服务、特征和特征的属性
-
一个 peripheral 包含一个或多个 service,或提供关于信号强度的信息。service 是数据和相关行为的集合。例如,一个心率监测仪的数据就可能是心率数据。
- service 本身又是由 characteristic 或者其他 service 组成的。characteristic 又提供了更为详细的 service 信息。还是以心率监测仪为例,service 可能会包含两个 characteristic,一个描述当前心率带的位置,一个描述当前心率的数据。
- 每个 characteristic 属性分为这么几种:读,写,通知这么几种方式
步骤简述:
- 1.导入CoreBluetooth.framework,遵循代理< CBCentralManagerDelegate , CBPeripheralDelegate >;
- 2.建立中心角色;
- 3.实现“蓝牙状态”的delegate方法,判断蓝牙状态,如成功则扫描指定UUID设备(如不指定UUID,则无法后台持续连接);
- 4.实现“扫描成功”的delegate方法,连接外围设备;
- 5.实现“连接成功”的delegate方法,断开连接,获取外围设备服务;
- 6.实现“获取服务”的delegate方法,获取外围设备服务的特征;
- 7.实现“获取服务特征”的delegate方法,订阅特征值的通知;
- 8.实现“获取服务特征属性和特征属性值”的delegate方法,读取外围设备服务特征属性和特征属性值,进行数据交互;
- 9.断开连接。
1.导入 CoreBluetooth.framework
2.设置蓝牙权限
3.导入头文件,遵循CBCentralManager和CBPeripheral代理
#import <CoreBluetooth/CoreBluetooth.h>
//让ViewController实现相应的delegate
@interface ViewController () <CBCentralManagerDelegate, CBPeripheralDelegate>
@property (nonatomic , strong) CBCentralManager *centrelManager;
@end
4.判断蓝牙状态
#pragma mark -- CBCentralManagerDelegate Delegate(监控蓝牙状态,扫描外设)
/**
* 得到蓝牙的目前状态 判断当前设备是否只拆BLE
*
* @param central central description
* 当central管理器更新状态时调用。这个方法是必须实现的,为了确保当前的central设备是否支持BLE以及当前是否可以被利用,当检测到central蓝牙已经打开时,需要做一些操作,比如开始寻找Peripheral。
* 当状态改变为CBCentralManagerStatePoweredOff时,会结束当前的寻找以及断开当前连接的peripheral。当检测到PoweredOff这个状态是所有的APP必须重新开始检索以及寻找。
*/
- (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");
//开始扫描周围的外设
[centrelManager scanForPeripheralsWithServices:nil options:nil];
/*
1、 如果要扫描周围指定的外设,则第一个参数为
CBUUID *uuid = [CBUUID UUIDWithString:@"0xf2a4"];
其中f2a4是我连接到iPad mini2的LightBlue app模拟的BLE外围设备,你要换成你设备的UUID。
2、第一个参数nil就是扫描周围所有的外设,扫描到外设后会进入didDiscoverPeripheral
*/
break;
default:
break;
}
}
5.扫描外围设备
#pragma mark -- 扫描外围设备
/**
* 如果发现周围有可联系的peripheral,该方法会不断地执行
*
* @param central 中心设备
* @param peripheral 外围设备
* @param advertisementData 外围设备广播内容
* @param RSSI 信号强度
*
* 当central管理器发现一个peripheral时调用,广播的数据被以AdvertisementDataRetrievalKeys键值的形式接受。
* 你必须建立一个本地的副本,当需要对他进行操作时。
* 在一般的情况下,你的APP要保证能够在一定的范围内自动连接还是上peripheral,你能够使用RSSI的值来判断与发现的peripheral的距离。
*/
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI{
/*
advertisementData:(NSDictionary *)advertisementData:
CBAdverisementDataLocalNameKey: 一个包含了peripheral设备名称的字符串。
CBAdvertisementDataManudfactureDataKey: 一个包含了制造商信息的NSdata对象。
CBAdvertisementDataServiceDataKey: 这个keys是一个CBUUID的对象代表了CBServicesUUID,这个NSData代表了服务指定的Data。
CBAdvertisementDataServiceUUIDsKey: 这是一包含了服务的UUID的数组。
CBAdvertisementDataOverflowServiceUUIDsKey: 这个数组包含了一个或者更多的CBUUID对象,这些对象代表了被广播在溢出区域的数据,关于这个keys的详细介绍请看我的上一篇译文《CBPeripheralManager 类介绍》。
CBAdvertisementDataTxPowerLeverlKey: 这个NSnumber对象包含了peripheral传输功率的强弱。如果peripheral广播的数据中包含了传输的功率是这个key值将可以被使用,使用RSSI和传输功率可以计算之间的距离。
CBAdvertisementDataIsconnectable: 这个BOOL值指明当前广播的事件是否可连接。
CBAdvertisementDataSolicitedServiceUUIDsKEy:一个包含了CBServices UUIDs的数组。
*/
//取得 local name,唯一标识设备
NSString *localName = peripheral.name;
if([localName hasPrefix:@“LL”]) {
self.peripheral = [peripheral copy];
//连接外设,有时候会连接不上,可以尝试用timer多连接几次
[self.centrelManager connectPeripheral:peripheral options:nil];
}
}
}
一个主设备最多能连7个外设,每个外设最多只能给一个主设备连接,连接成功,失败,断开会进入各自的代理方法。
6.连接外围设备成功:
#pragma mark -- 连接成功
/**
* 连接成功
*
* @param central 中心设备
* @param peripheral 外围设备
*/
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral{
//中央设备停止扫描外围设备
[self.centrelManager stopScan];
// 设置外围设备代理
peripheral.delegate = self;
//寻找服务 查找设备中所包含的服务了 执行完后会执行CBPeripheral delegate didDiscoverServices
[peripheral discoverServices:nil];
/*
可根据所设定的参数搜寻指定的服务
NSArray *serviceUUIDs = @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]];
[peripheral discoverServices:serviceUUIDs];
*/
}
7.外围设备连接失败:
/*
* 连接失败
*
* @param central 中心设备
* @param peripheral 外围设备
* @param error 错误参数
*
* 这个方法在方法connectPeripheral:options建立的连接断开时调用,应为建立连接的动作是不能超时的,通常在失败连接时你需要再次试图连接peripheral。
*/
- (void)centralManager:(CBCentralManager *)central
didFailToConnectPeripheral:(CBPeripheral *)peripheral
error:(NSError *)error{
if (error) {
NSLog(@"Failed to connect to %@. (%@)", peripheral, [error localizedDescription]);
}
}
8.断开外围设备的连接和连接超时:
/*
* 连接超时 断开连接
*
* @param central 中心设备
* @param peripheral 外围设备
* @param error 错误参数
*
* 当已经建立的连接被断开时调用。
* 这个方法在connectPeripheral:options方法建立的连接断开时调用。
* 如果断开连接不是有cancelPeripheralConnection方法发起的,那么断开连接的详细信息就在error参数中。
* 当这个方法被调用只有peripheral代理中的方法不在被调用。
* 注意:当peripheral断开连接时,peripheral所有的service、characteristic、descriptors都无效。
*/
- (void)centralManager:(CBCentralManager *)central
didDisconnectPeripheral:(CBPeripheral *)peripheral
error:(NSError *)error{
if (error) {
NSLog(@"Disconnect to %@. (%@)", peripheral, [error localizedDescription]);
}
// 重连机制,可设置重新连接次数,结合连接成功的代理累计
if (self.peripheral) {
[self.centrelManager connectPeripheral:self.peripheral options:nil];
}
}
9.搜寻外围设备中服务
/**
* 查找外围设备中服务
*
* @param peripheral 外围设备
* @param error 错误参数
*/
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error{
if (error) {
NSLog(@"Service discover was unsuccessfull! error:%@ ",[error localizedDescription]);
}else{
for (int i = 0; i < peripheral.services.count; i++) {
CBService *service = [peripheral.services objectAtIndex:i];
NSLog(@"service UUID:%@", service.UUID);
self.serviceUUID = service.UUID;
//知道每个service下有多少个Characteristics特性,有服务的话扫描特征
[peripheral discoverCharacteristics:nil forService:service];
/*
可根据所设定的参数搜寻指定服务的特征
for (CBService *service in peripheral.services) {
//serviceId筛选
if ([service.UUID.UUIDString isEqualToString:TRANSFER_SERVICE_UUID1]) {
[peripheral discoverCharacteristics:nil forService:service];
// 可根据指定的特征UUID搜寻
// NSArray *characteristicUUIDs = @[[CBUUID UUIDWithString:TRANSFER_CHRARCTERISTIC_UUID]];
// [peripheral discoverCharacteristics:charateristicUUIDs forService:service];
}
}
*/
}
}
}
在做该类项目时,外设需求往往有一个UUID来确定需要连接的服务,对应这边service的UUID,而不是peripheral的UUID
(在使用lightblue模拟测试时,可以添加service并设置其UUID来模拟测试)
10.设置读取和订阅外围设备特征值的通知
/**
* 设置读取和订阅外围设备特征值的通知
* 知道什么样的Services服务 什么样的Characteristics特性之后再进行处理, 并且注册通知 当BLE周边发信息过来时就会收到通知并得到资料
*
* @param peripheral 中心设备
* @param service 服务
* @param error 错误参数
*/
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error{
if (error) {
NSLog(@"Characteristic discover was unsuccessfull! error:%@ ",[error localizedDescription]);
}else{
// 获取service的characteristics
NSArray *characteristics = [service characteristics];
for (CBCharacteristic *characteristic in characteristics) {
if ([characteristic.UUID.UUIDString isEqualToString:TRANSFER_CHRARCTERISTIC_UUID]) {
self.characteristic = characteristic;
[peripheral readValueForCharacteristic:self.characteristic];
//注册监听
[peripheral setNotifyValue:YES forCharacteristic:self.characteristic];
//我们可以使用readValueForCharacteristic:来读取数据。
//如果数据是不断更新的,则可以使用setNotifyValue:forCharacteristic:来实现只要有新数据,就获取。
//外围设备支持监听模式,特征值发生变化时广播。
}
}
}
11.数据处理:读取特征值和所监听的特征的特征值发生变化时所调用代理
/**
* 数据处理
* 连上完成后 取得数据非常重要 当外设发送新值时候,此方法自动被动用
*
* @param peripheral 外围设备
* @param characteristic 特征
* @param error 错误参数
*/
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error{
if (error) {
NSLog(@"updateValueForCharacteristic failed! error:%@ ",[error localizedDescription]);
return;
}else{
//读取数据,当周边发送新的值,此方法的第二个参数包含特征。可以用value属性读取他的值,这是一个包含特征的值的NSData。
unsigned char characteristicValueData[1024] = {0};
[characteristic.value getBytes:characteristicValueData];
NSLog(@"characteristicValue:%@ - Length:%lu",characteristicValueData,(unsigned long)characteristicValueData.length);
// 与外围设备进行交互
/*
*可以通过特征值来校验是否为正确的数据,做相对应的操作
* if (characteristicValueData[0] == 0x02) {
* //此时为第一位为2才是正确数据,做相对应的操作
*
* }else if (characteristicValueData[0] == 0x04){
*
* //此时为第一位为4不是正确数据或者是其他操作的数据,做相对应的操作
* (如设置监听后特征值发生变化为正确的数据)
* }
*/
}
}
中心设备单次发送的数据最大不能超过20个字节,蓝牙模块内部接收缓冲区只有20个字节,如果超过20个字节,需要分包发送。
/**
* 数据处理
*
* 传输数据大于20个字节时需要分包处理
*
* @param data 传输的数据
*
* TranslateDataLenght为最大传输数据值20
*/
- (void)transmissionWithData:(NSData *)data{
if(data.length > TranslateDataLenght){
int i = 0;
while ((i + 1) * TranslateDataLenght <= data.length) {
NSData *dataSend = [data subdataWithRange:NSMakeRange(i * TranslateDataLenght, TranslateDataLenght)];
[self write: self.peripheral data:dataSend];
i++;
//根据接收模块的处理能力做相应延时
usleep(20 * 1000);
}
i = data.length % TranslateDataLenght;
if(i >0){
NSData *dataSend = [data subdataWithRange:NSMakeRange(data.length - i, i)];
[self write:self.peripheral data:dataSend];
}
//根据接收模块的处理能力做相应延时
usleep(20 * 1000);
}else{
[self write:self.peripheral data:data];
}
}
/*
* 向外围设备传输数据
* @param peripheral 外围设备
* @param data 传输的数据
*/
- (void) write:(CBPeripheral *)peripheral
data:(NSData *)data{
/*
* 如果设置为CBCharacteristicWriteWithResponse,
* 则可以写成功一次,只可读,写入数据有反馈
*
* 如果为CBCharacteristicPropertyWriteWithoutResponse,
* 则一次也不能写成功,可以读,也可以通知。
*/
if(characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse){
[peripheral writeValue:data
forCharacteristic:characteristic
type:CBCharacteristicWriteWithoutResponse];
}else{
[peripheral writeValue:data
forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
}
}
12.数据写入成功后反馈
/*
* 数据写入成功后反馈
*
* 外围设备写入数据后的类型为CBCharacteristicWriteWithResponse,会调用该方法
* @param peripheral 外围设备
* @param data 传输的数据
*/
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error{
if (error) {
NSLog(@"Write value for characteristic failed : %@",error.userInfo);
}else{
NSLog(@"Write value for characteristic successfull!! ");
// 如果写入数据成功,外围设备被中心设备所监听的设备的服务特征发生改变,会调用- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error,做相对应的处理
}
}
各位大神,有不足之处,请多多指教,勿喷。