IOS开发:IOS与蓝牙交互开发实战
TIME:2018-09-04
最近刚做了一个蓝牙开发的项目,要求通过蓝牙接收数据和写入数据,下面将蓝牙开发的流程做一个简单的介绍。
概念理解
开发蓝牙之前,通过看看官方文档和网上查看资料了解了蓝牙开发的大体流程,首先得理解关于蓝牙开发中的一些概念。我们都是通过基于蓝牙4.0的低功耗蓝牙进行开发,开发用到的框架是CoreBluetooth。下面解释几个概念:
中心者模式和管理者模式
中心者模式:这个使用的很普遍,就是手机作为主机,蓝牙作为外设。
管理者模式:我们手机自己作为外设,自己创建服务和特征,然后有其他的设备连接我们的手机。
中心CBCentral和外设CBPeripheral
BLE中的交互涉及两个重要角色:中心CBCentral和外围设备(简称外设)CBPeripheral。外设通常具有其他设备所需要的数据,中心通常使用外设提供的信息来实现特定的功能。
服务CBService和特征CBCharacteristic
每个外设CBPeripheral中包含一个或者多个服务CBService以及有关其连接信号的信息。服务是实现一个函数或者功能的设备的数据采集和相关行为的集合。每个服务下面又有很多特征CBCharacteristic,特征提供了这些服务的详细内容。我们在进行BLE蓝牙开发的时候,就需要通过特定服务下面的特定特征来获取想要的数据和内容,特征是与外界交互的最小单位。
UUID
关于UUID,暂时可以理解为蓝牙的唯一标识,每个服务和特征都会有相应的UUID,可以通过UUID来代表特定的服务和特征。
步骤:
- 导入头文件#import <CoreBluetooth/CoreBluetooth.h>
- 遵守协议CBCentralManagerDelegate,CBPeripheralDelegate
- 建立中心管理者CBCentralManager对蓝牙进行管理
- 扫描外设,使用方法scanForPeripheralsWithServices
- 连接扫描到的外设
- 扫描并获取外设的服务CBService
- 扫描并获取外射服务下面的特征CBCharacteristic
- 获取特征的值,即从外围设备读取数据,用didUpdateValueForCharacteristic方法
- 订略特征的通知,实现当连接蓝牙成功之后以通知的方式实现对特征里面数据的读写。需要调用- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic方法
- 写入数据,调用writeValue:forCharacteristic:type:方法
示例代码
- 建立中心管理者和外设
/** 主设备(可以扫描和链接外设备) */ @property (nonatomic, strong) CBCentralManager * centeralManger; /** 外设备 */ @property (nonatomic, strong) CBPeripheral * peripheral; /** 用于保存被发现的设备 */ @property (nonatomic, strong) NSMutableArray * discoverPeripherals;
2.初始化中心管理者并设置委托
// 初始化并设置委托和线程 self.centeralManger = [[CBCentralManager alloc]initWithDelegate:self queue:nil]; self.discoverPeripherals = [[NSMutableArray alloc]init];
将queue的参数设置为nil,默认就是在住线程中执行。
3.初始化中心管理者之后,用代理方法监听主设备状态的改变,当检测到蓝牙打开以后,调用扫描外设的方法:
#pragma mark - CBCentralManagerDelegate /**
* 主设备状态改变
*/ - (void)centralManagerDidUpdateState:(CBCentralManager *)central{ switch (central.state) { case CBManagerStateUnknown: NSLog(@">>>CBManagerStateUnknown"); break; case CBManagerStateResetting: NSLog(@">>>CBManagerStateResetting"); case CBManagerStateUnsupported: NSLog(@">>>CBManagerStateUnsupported"); case CBManagerStateUnauthorized: NSLog(@">>>CBManagerStateUnauthorized"); case CBManagerStatePoweredOff: NSLog(@">>>CBManagerStatePoweredOff"); // [SVProgressHUD setBackgroundColor:[UIColor yellowColor]]; [SVProgressHUD showErrorWithStatus:@"请打开手机蓝牙开关!"]; break; case CBManagerStatePoweredOn: NSLog(@">>>CBManagerStatePoweredOn"); // 开始扫描外围设备 /**
* 第一个参数为nil就是扫描周围所有的外设,扫描后会进入
*/ [self.centeralManger scanForPeripheralsWithServices:nil options:nil]; break; default: break;
}
}
- 在扫描到外设的代理方法里面持有外设
// 扫描到设备会进入方法 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
[SVProgressHUD dismiss]; NSLog(@"扫描到设备名字:%@", peripheral.name); for (CBPeripheral * thePeripheral in self.discoverPeripherals) { if ([thePeripheral.name isEqual:peripheral.name]) return;
} if (peripheral) { //找到设备必须持有他,否者CBCentralManager也不会保存peripheral [self.discoverPeripherals addObject:peripheral]; NSLog(@"discoverPeripherals - %@", self.discoverPeripherals);
[self.BLEtableView reloadData]; // 如果在这停止扫描 那么就每次就只能扫描到一个外设备 // [self.centeralManger stopScan]; }else{
[SVProgressHUD showErrorWithStatus:@"没有扫描到蓝牙设备!"];
}
}
5.用代理方法实现连接蓝牙,在连接成功的方法里面实现读取数据的通知,并设置外设的代理
// 连接成功 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ NSLog(@">>>连接到名称为(%@)的设备 - 成功", peripheral.name);
[SVProgressHUD showSuccessWithStatus:@"连接成功!"];
peripheral.delegate = self; //扫描外设备的services [peripheral discoverServices:nil]; //发送通知 [self notifyCharacteristic:peripheral characteristic:self.characteristic];
[self.centeralManger stopScan];
} // 连接到peripherals - 失败 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);
[SVProgressHUD showErrorWithStatus:@"连接失败!"];
} // peripherals连接断开 - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@">>>外设连接断开连接 %@: %@\n", [peripheral name], [error localizedDescription]);
[SVProgressHUD showErrorWithStatus:@"连接断开!"];
}
6.用外设的代理方法扫描服务和特征
#pragma mark - CBPeripheralDelegate // 扫描到services - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ if (error) { NSLog(@"扫描失败原因 - %@", error.localizedDescription); return;
} NSLog(@"扫描到服务 - %@", peripheral.services); // 扫描每个服务的特征 for (CBService * service in peripheral.services) { NSLog(@"service 的 UUID : %@", service.UUID); // 扫描每个service的characteristics [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.localizedFailureReason); return;
} for (CBCharacteristic * characteristics in service.characteristics) { NSLog(@"services:%@ 的 characteristics:%@", service.UUID, characteristics.UUID);
} // 获取characteristics的值 for (CBCharacteristic * characteristics in service.characteristics) {
[peripheral readValueForCharacteristic:characteristics];
} // 搜索characteristics的description for (CBCharacteristic * characteristics in service.characteristics) {
[peripheral discoverDescriptorsForCharacteristic:characteristics];
[peripheral setNotifyValue:YES forCharacteristic:characteristics];
}
}
7.读取特征的值(即读取数据)
// 获取characteristics的值 - 从外围设备读数据 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ NSLog(@"characteristic 的 uuid:%@--value:%@",characteristic.UUID,characteristic.value); if (error) { NSLog(@"Error changing notification state: %@", [error localizedDescription]);
} self.characteristic = characteristic;
}
8.实现写入数据的方法
// 写数据 - 将数据写入特征 - (void)writeCharactereristic:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic value:(NSData *)value{ 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(@"该字段不可写!");
}
}
8 征订通知和取消通知
// 6.订阅特征的通知 //设置通知 -(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];
}
如果将扫描到的蓝牙用tebleView来显示,就会得到如下效果: