多蓝牙设备连接
2017-03-23 本文已影响0人
___吉
今天生日,先祝自己生日快乐!嗯,留在公司加班了,这两天接手的一个项目中需要支持多个蓝牙设备同时连接进行数据交互,就写写多设备支持吧。主要模型如下:
模型.png
上图中,管理中心为单例对象,将系统提供的CBCentralManager封装到其中去,并实现对应的代理方法
大致实现思路是,在找到自己需要的外设后:
1.创建外设模型,将外设名,信号值,广播数据等传进去,创建出对象,添加进管理中的设备数组中,并将代理设置成管理中心。
2.连接刚刚找到的那个外设,连接成功,则将外设的代理设为上面创建的设备对象。
3.实现外设的对应代理方法,找打需要使用的属性,如:读、写、等
4.属性找完后,调用代理方法,回到管理中心,告诉管理中心,自己已OK。
5.管理中心继续扫描,寻找下一个需要连接的对象
6.一直到连接完所有设备。此时,设备数组中已保存好所有连接的外设,需要使用时,只需要将其找出来即可。
代码没有最终确定,还在完善...
static JCMultiBLEManager *_manager;
static CBCentralManager *_myCentralManager;
@interface JCMultiBLEManager ()<CBCentralManagerDelegate, JCDeviceDelegate>
@property (nonatomic, weak) JCBLEDataManager *bluetoothDataManager; //数据模型管理
@property (nonatomic, copy) getBluetoothDataComplete getDataComplete;
@property (nonatomic, copy) connectingCallBack connectingCompletion;
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation JCMultiBLEManager
#pragma mark 【1】创建单例蓝牙管理中心
+ (JCMultiBLEManager *)shareCBCentralManager{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_manager = [[JCMultiBLEManager alloc]init];
_myCentralManager = [[CBCentralManager alloc] initWithDelegate:_manager queue:nil];
_manager.bluetoothDataManager = [JCBLEDataManager shareBluetoothData];
_manager.uartArray = [NSMutableArray array];
});
return _manager;
}
#pragma mark 【2】监测蓝牙状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state)
{
case CBCentralManagerStateUnknown:
break;
case CBCentralManagerStateUnsupported:
break;
case CBCentralManagerStateUnauthorized:
break;
case CBCentralManagerStatePoweredOff:{
self.bluetoothState = BluetoothOpenStateIsClosed;
if ([self.delegate respondsToSelector:@selector(bluetoothStateChange:state:)]) {
[self.delegate bluetoothStateChange:self state:BluetoothOpenStateIsClosed];
}
}
break;
case CBCentralManagerStateResetting:
break;
case CBCentralManagerStatePoweredOn:
{
self.bluetoothState = BluetoothOpenStateIsOpen;
if ([self.delegate respondsToSelector:@selector(bluetoothStateChange:state:)]) {
[self.delegate bluetoothStateChange:self state:BluetoothOpenStateIsOpen];
}
}
break;
}
}
#pragma mark 【3】发现外部设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
if (![peripheral.name isEqual:[NSNull null]]) {
if ([peripheral.name containsString:DEVICE_NAME_DT2] || [peripheral.name containsString:DEVICE_NAME_DT3]) {
//这里可以设置自己的过滤条件,比如信号值,设备名字等等
JCLog(@"发现 --> %@",peripheral);
JCBLEDevice *device = nil;
BOOL isContains = NO;
for (JCBLEDevice *model in _uartArray) {
if (model.peripheral == peripheral) {
isContains = YES;
device = model;
break;
}
}
if (isContains == NO) {
device = [JCBLEDevice UARTWith:peripheral advertisementData:advertisementData andRSSI:RSSI];
[_uartArray addObject:device];
device.delegate = self;
}
if (device.peripheral.state == CBPeripheralStateDisconnected) {
[self connectToPeripheral:device.peripheral];//这里因为项目需要,我做成了自动连接,更靠谱的过滤设备方法,到后期再做,目前只是测试
[_myCentralManager stopScan];
JCLog(@"< -- > %@",peripheral.name);
}
}
}
}
#pragma mark 【4】连接外部蓝牙设备
- (void)connectToPeripheral:(CBPeripheral *)peripheral{
if (!peripheral) {
return;
}
[_myCentralManager connectPeripheral:peripheral options:nil];//连接蓝牙
}
#pragma mark 【5】连接外部蓝牙设备成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
for (JCBLEDevice *needDevice in _uartArray) {
if (needDevice.peripheral == peripheral) {
peripheral.delegate = needDevice;
}
}
[peripheral discoverServices:nil];
if ([self.delegate respondsToSelector:@selector(bluetoothManager:didSucceedConectPeripheral:)]) {
[self.delegate bluetoothManager:self didSucceedConectPeripheral:peripheral];
}
JCLog(@"\n\n成功连接上:%@",peripheral.name);
}
//这个方法是设备找到对应属性后 反向代理回调告诉控制器,刚刚连接的外设已经找打对应属性。项目中用于判断是否已将全部设备连接成功
- (void)foundCharacterSuccess:(JCBLEDevice *)device{
[_myCentralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]]
options:@{CBCentralManagerScanOptionAllowDuplicatesKey:[NSNumber numberWithBool:NO]}];
if ([self getConnectDevice].count == kDeviceCount) {
if (_timer) {
[_timer invalidate];
_timer = nil;
}
if (_connectingCompletion) {
_connectingCompletion(YES);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kAllDeviceIsConnect object:nil];
[_myCentralManager stopScan];//停止扫描
}
}
#pragma mark 【6】连接外部蓝牙设备失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
if ([self.delegate respondsToSelector:@selector(bluetoothManager:didFailConectPeripheral:)]) {
[self.delegate bluetoothManager:self didFailConectPeripheral:peripheral];
}
}
#pragma mark 【7】蓝牙外设连接断开,自动重连
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
if ([self.delegate respondsToSelector:@selector(bluetoothManager:didDisconnectPeripheral:error:)]) {
[self.delegate bluetoothManager:self didDisconnectPeripheral:peripheral error:error];
}
if (peripheral) {
JCLog(@"\n\n断开与%@的连接,正在重连...\n\n",peripheral.name);
[_manager connectToPeripheral:peripheral];
}
}
#pragma mark 【8】重新扫描外设
- (void)reScan{
for (JCBLEDevice *device in _uartArray) {
[self disConnectToPeripheral:device.peripheral];
}
[_uartArray removeAllObjects];
[_myCentralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]]
options:@{CBCentralManagerScanOptionAllowDuplicatesKey:[NSNumber numberWithBool:NO]}];
}
#pragma mark 停止扫描外设
- (void)stopScan{
for (JCBLEDevice *device in _uartArray) {
[self disConnectToPeripheral:device.peripheral];
}
[_uartArray removeAllObjects];
[_myCentralManager stopScan];
}
#pragma mark 断开外设连接
- (void)disConnectToPeripheral:(CBPeripheral *)peripheral{
[_myCentralManager cancelPeripheralConnection:peripheral];
}
/*!
* 获取设备
* @param deviceType -[in] 需要获取的设备类型
* return 获取到的设备
*/
- (JCBLEDevice *)getDeviceForDeviceType:(DeviceType)deviceType{
JCBLEDevice *device = nil;
switch (deviceType) {
case DeviceTypeDT2:
{
for (JCBLEDevice *bleDevice in _manager.uartArray) {
if ([bleDevice.name isEqualToString:DEVICE_NAME_DT2]) {
device = bleDevice;
}
}
}
break;
case DeviceTypeDT3:
{
for (JCBLEDevice *bleDevice in _manager.uartArray) {
if ([bleDevice.name isEqualToString:DEVICE_NAME_DT3]) {
device = bleDevice;
}
}
}
break;
default:
break;
}
return device;
}
- (NSArray *)getConnectDevice{
return [_myCentralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]]];
}
//连接设备 加超时判断
- (void)connectSensorsWithTimeout:(NSTimeInterval)timeout
onCompletion:(connectingCallBack)completion {
[self reScan];
self.timer = [NSTimer scheduledTimerWithTimeInterval:timeout
target:self
selector:@selector(stopConnecting:)
userInfo:nil
repeats:NO];
self.connectingCompletion = completion;
}
//停止连接
- (void)stopConnecting:(NSTimer *)timer{
[self stopScan];
self.connectingCompletion(NO);
}
设备类中
@interface JCBLEDevice ()<CBPeripheralDelegate>
@property (nonatomic, copy) getBluetoothDataComplete complete;
@property (nonatomic, strong) CBCharacteristic *readCharacteristic; //读取数据特性
@property (nonatomic, strong) CBCharacteristic *writeCharacteristic; //写数据特性
@property (nonatomic, strong) CBCharacteristic *readMACCharacteristic; //读取mac地址特性
@end
@implementation JCBLEDevice
#pragma mark 【1】寻找蓝牙服务
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if(error){
JCLog(@"外围设备寻找服务过程中发生错误,错误信息:%@",error.localizedDescription);
}
CBUUID *serviceUUID=[CBUUID UUIDWithString:kServiceUUID];
for (CBService *service in peripheral.services) {
if([service.UUID isEqual:serviceUUID]){
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kNotifyUUID],[CBUUID UUIDWithString:kWriteUUID],[CBUUID UUIDWithString:kReadMacUUID]] forService:service];
}
}
}
#pragma mark 【2】寻找蓝牙服务中的特性
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if (error) {//报错直接返回退出
JCLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
return;
}
for (CBCharacteristic *characteristic in service.characteristics)
{
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyUUID]]){
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
self.readCharacteristic = characteristic;
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kWriteUUID]]) {
self.writeCharacteristic = characteristic;
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kReadMacUUID]]) {
self.readMACCharacteristic = characteristic;
}
}
self.name = peripheral.name;
if (_readCharacteristic && _writeCharacteristic) {
if ([self.delegate respondsToSelector:@selector(foundCharacterSuccess:)]) {
[self.delegate foundCharacterSuccess:self];
}
}
[self initDevice];
}
#pragma mark 【8】直接读取特征值被更新后
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
JCLog(@"更新特征值时发生错误,错误信息:%@",error.localizedDescription);
return;
}
if (characteristic.value) {
JCLog(@" \n --> 读取到%@的数据:%@",peripheral.name,characteristic.value);
//数据
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyUUID]]) {
JCLog(@" \n --> 读取到%@的数据:%@",peripheral.name,characteristic.value);
[JCBLEDataManager dealRecieveData:characteristic.value device:self complete:self.complete];
}
//mac地址
else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kReadMacUUID]]) {
self.macAddr = [JCDataConvert convertHexToString:characteristic.value];
JCLog(@" \n%@的mac地址:%@",peripheral.name,characteristic.value);
}
}
}
//初始化传感器
- (void)initDevice{
//读取蓝牙mac地址
[self readBLEMacAddress];
//读取电量
__weak typeof(self)weakSlef = self;
[self sendDataUseCommand:APP_COMMAND_READ_BATTERY validData:nil complete:^(JCBLEDevice *device, RespondType respondType, id obj) {
if (respondType == RespondTypeBattery) {
JCLog(@"%@ == > %@",weakSlef,obj);
}
}];
}
- (void)sendInitCommandToDevice:(JCBLEDevice *)device{
//发送电池电量获取
// [JCMultiBLEManager getDataUseCommand:APP_COMMAND_READ_BATTERY validData:nil device:device
// complete:nil];
/*
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//发送时间校准
Byte bytes[4];
NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
bytes[0] = (Byte)(timeInterval/(256*256*256));
bytes[1] = (Byte)(timeInterval/(256*256));
bytes[2] = (Byte)(timeInterval/256);
bytes[3] = ((Byte)timeInterval)%256;
NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)];
NSString *valid = [JCDataConvert convertHexToString:data];
[JCMultiBLEManager getDataUseCommand:APP_COMMAND_TIMESTAMP_VERIFY validData:valid device:device
complete:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//发送主页数据获取
[JCMultiBLEManager getDataUseCommand:APP_COMMAND_READ_MAIN_DATA validData:nil device:device
complete:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//发送电池电量获取
[JCMultiBLEManager getDataUseCommand:APP_COMMAND_READ_BATTERY validData:nil device:device
complete:nil];
//读取蓝牙mac地址
[self readBLEMacAddressForDevice:device];
});
});
});
*/
}
// 获得对256求模的值
- (Byte)modulusValue:(Byte *)bytes countOfBytes:(NSInteger)size{
NSUInteger total = 0;
for (NSInteger index = 0; index < size - 1; index++) {
total += bytes[index];
}
return total % 256;
}
+(instancetype)UARTWith:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
andRSSI:(NSNumber *)RSSI{
JCBLEDevice *uart = [JCBLEDevice new];
uart.peripheral = peripheral;
uart.RSSI = RSSI;
uart.advertisementData = advertisementData;
return uart;
}
/*!
* 通过蓝牙发送 指令+数据 到外设
*
* @param command -[in] 要发送的指令
* @param validData -[in] 要发送的数据
*/
#pragma APP发送指令数据
- (void)sendDataUseCommand:(nullable NSString *)command
validData:(nullable NSString *)validData
complete:(getBluetoothDataComplete)complete{
NSString *valid = @"";
if ([command isEqualToString:APP_COMMAND_EDIT_DEVICE_NAME]) {
valid = [JCDataConvert convertHexToString:[JCDataConvert hexDataFromString:validData]];
}
else{
valid = validData;
}
NSData *commandData = [self creatSendDataCommand:command valid:valid];
[self sendData:commandData];
self.complete = complete;
}
#pragma mark 发送数据
/*!
* 通过蓝牙发送data数据到外设
*
* @param data -[in] 要发送的data
*/
- (void)sendData:(nullable NSData *)data{
if (self.peripheral.state == CBPeripheralStateConnected) {
[self.peripheral writeValue:data
forCharacteristic:self.writeCharacteristic
type:CBCharacteristicWriteWithResponse];
JCLog(@" \n --> 给 %@ 发送的数据:%@",self.name,data);
}
}
/*!
* 创建数据
* @param command -[in] 指令
* @param validData -[in] 有效数据
*/
- (NSData *)creatSendDataCommand:(NSString *)command valid:(NSString *)validData{
//拼接帧头+指令+有效数据
NSString *sendStr = [FH stringByAppendingString:command];
if (validData != nil) {
sendStr = [sendStr stringByAppendingString:validData];
}
//计算checkSum
NSUInteger checkSum = 168;
for (NSInteger i = 0; i < 2; i++) {
switch (i) {
case 0:
checkSum += strtoul([command UTF8String], 0, 16);
break;
case 1:
{
NSInteger subCheckNum = 0;
for (NSInteger calcuChekNum = 0; calcuChekNum < validData.length/2.0; calcuChekNum++) {
NSString *cutStr = [validData substringWithRange:NSMakeRange(calcuChekNum*2, 2)];
subCheckNum += strtoul([cutStr UTF8String], 0, 16);
}
checkSum += subCheckNum;
}
break;
default:
break;
}
}
checkSum %= 256;
NSInteger missNum = 19 - sendStr.length/2.0;
NSString *checkSumStr = [JCDataConvert toHex:(int)checkSum];
for (NSInteger i = 0; i < missNum; i++) {
sendStr = [sendStr stringByAppendingString:@"00"];
}
sendStr = [sendStr stringByAppendingFormat:@"%@",checkSumStr];
return [JCDataConvert hexToBytes:sendStr];
}
/*!
* 读取蓝牙mac地址
*/
- (void)readBLEMacAddress{
if (self.peripheral != nil && self.readMACCharacteristic != nil) {
[self.peripheral readValueForCharacteristic:self.readMACCharacteristic];
}
}
@end