// // Bluetooth.m // tongxin // // Created by ecell on 2022/11/3. // Copyright © 2022 xTT. All rights reserved. // #import "Bluetooth.h" #import #import "BLEModel.h" #import "BLEConst.h" #import "GCDTimerManager.h" #import #import // 超时间隔 #define TimeOutNumber 5 NSString *const sDeviceKey = @"sDeviceModel"; @interface Bluetooth () @property (strong, nonatomic) CBPeripheral *bleCurrentPeripheral; @property (strong, nonatomic) NSString *selectedOtaFilePath; @property (strong, nonatomic) CBCentralManager *bleManager; @property (strong, nonatomic) NSMutableArray *blePeripheralArr; /// 计时器 @property (nonatomic ,assign) NSInteger timeNumber; /// 自动重连次数 @property (nonatomic ,assign) NSInteger ljNumber; @property (nonatomic, strong) AVAudioPlayer *audioPlayer; @end @implementation Bluetooth + (instancetype)shareInstance { static Bluetooth *share; static dispatch_once_t oneToken; dispatch_once(&oneToken, ^{ share = [[self alloc] init]; share.cache = [YYCache cacheWithName:@"lksh_cache_data"]; if ([share.cache containsObjectForKey:sDeviceKey]) { id object = [share.cache objectForKey:sDeviceKey]; if ([object isKindOfClass:BLEModel.class]) { share.currenModel = object; } } }); return share; } - (instancetype)init { self = [super init]; if (self) { self.timeNumber = 0; self.ljNumber = 0; _bleManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; } return self; } #pragma mark - 扫描设备相关 #pragma mark 开始扫描 - (void)startScanBLE { NSLog(@"BLE ---> startScanBLE."); _blePeripheralArr = [NSMutableArray new]; if (_bleManager) { NSArray *uuids = @[[CBUUID UUIDWithString:BraceletServicesUUID]]; if (_bleManager.state == CBManagerStatePoweredOn) { [_bleManager scanForPeripheralsWithServices:uuids options:nil]; } else { __weak typeof(self) weakSelf = self; dispatch_after(0.5, dispatch_get_main_queue(), ^{ if (weakSelf.bleManager.state == CBManagerStatePoweredOn) { [weakSelf.bleManager scanForPeripheralsWithServices:uuids options:nil]; } }); } } } #pragma mark 停止扫描 - (void)stopScanBLE { if (_bleManager) [_bleManager stopScan]; } #pragma mark - 蓝牙设备连接相关 #pragma mark 断开当前蓝牙设备连接 - (void)disconnectBLE { if (_bleCurrentPeripheral) { NSLog(@"BLE --->To disconnectBLE."); [_bleManager cancelPeripheralConnection:_bleCurrentPeripheral]; [GCDTimerManager.sharedInstance cancelTimerWithName:@"Zhendong"]; AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate); [GCDTimerManager.sharedInstance cancelTimerWithName:@"BLELJ"]; } } #pragma mark 连接蓝牙设备 - (void)connectBLE:(BLEModel*)bleModel { _bleCurrentPeripheral = bleModel.peripheral; self.currenModel = bleModel; [_bleCurrentPeripheral setDelegate:self]; [_bleManager connectPeripheral:_bleCurrentPeripheral options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}]; [_bleManager stopScan]; NSLog(@"BLE Connecting... Name ---> %@", bleModel.peripheral.name); //[self jisjiqi:0 bleModel:bleModel]; } - (void)jisjiqi:(NSInteger)type bleModel:(BLEModel*)bleModel { self.timeNumber = 0; [GCDTimerManager.sharedInstance cancelTimerWithName:@"BLELJ"]; [GCDTimerManager.sharedInstance scheduleGCDTimerWithName:@"BLELJ" interval:1 queue:dispatch_get_main_queue() repeats:YES option:CancelPreviousTimerAction action:^{ self.timeNumber++; NSLog(@"%ld",self.timeNumber); [[NSNotificationCenter defaultCenter] postNotificationName:@"LIANJIEZ" object:self.timeNumber%2 == 0 ? @"连接中..." : @"连接中.."]; if (self.timeNumber == 15) { self.ljNumber ++; [GCDTimerManager.sharedInstance cancelTimerWithName:@"BLELJ"]; if (self.ljNumber < 2) { if (type == 1) [self connectPeripheralWithUUID]; else [self connectBLE:bleModel]; } else { [[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtConnectFail object:nil]; self.isConnected = NO; self.ljNumber = 0; NSString *switchNum = [[NSUserDefaults standardUserDefaults] objectForKey:@"WARNINGSWITCH"]; if ([switchNum boolValue]) { NSURL *fileURL = [[NSBundle mainBundle]URLForResource:@"alarm_sound" withExtension:@".caf"]; self.audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:fileURL error:nil]; self.audioPlayer.delegate = self; self.audioPlayer.numberOfLoops = 10; [self.audioPlayer play]; [GCDTimerManager.sharedInstance scheduleGCDTimerWithName:@"Zhendong" interval:1 queue:dispatch_get_main_queue() repeats:YES option:CancelPreviousTimerAction action:^{ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); }]; } UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"防丢警告" message:@"您已经和您的设备远离,请确认设备是否还在您附近!!!" preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { [self.audioPlayer stop]; [GCDTimerManager.sharedInstance cancelTimerWithName:@"Zhendong"]; AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate); }]]; [[Bluetooth currentVC] presentViewController:alert animated:YES completion:nil]; } } }]; } #pragma mark AVAudioPlayerDelegate /// 音频播放完成时 - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { [GCDTimerManager.sharedInstance cancelTimerWithName:@"Zhendong"]; AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate); } #pragma mark 通过UDID重连 - (void)connectPeripheralWithUUID { NSString *uuid = [[NSUserDefaults standardUserDefaults] objectForKey:AutomaticReconnectionDevice]; if (!uuid) return; NSArray *uuidArr = @[[[NSUUID alloc] initWithUUIDString:uuid]]; NSArray *phArr = [_bleManager retrievePeripheralsWithIdentifiers:uuidArr];//serviceUUID就是你首次连接配对的蓝牙 if (phArr.count == 0) { return; } CBPeripheral* peripheral = phArr[0]; if (phArr.firstObject // && [phArr.firstObject state] != CBPeripheralStateConnected // && [phArr.firstObject state] != CBPeripheralStateConnecting ) { NSString *ble_name = peripheral.name; NSString *ble_uuid = peripheral.identifier.UUIDString; NSLog(@"FLT Connecting(Last)... Name ---> %@ UUID:%@",ble_name,ble_uuid); _bleCurrentPeripheral = peripheral; [_bleCurrentPeripheral setDelegate:self]; [_bleManager connectPeripheral:_bleCurrentPeripheral options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}]; [self jisjiqi:1 bleModel:nil]; } } /// 移除设备 - (void)disConnectedCurrenDevice { [self disconnectBLE]; [Bluetooth.shareInstance.cache containsObjectForKey:sDeviceKey withBlock:^(NSString * _Nonnull key, BOOL contains) { [self.cache removeObjectForKey:sDeviceKey withBlock:^(NSString * _Nonnull key) { }]; }]; [self.audioPlayer stop]; [[NSNotificationCenter defaultCenter] postNotificationName:DISConnectedCurrenDevice object:nil]; [GCDTimerManager.sharedInstance cancelTimerWithName:@"Zhendong"]; AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate); [GCDTimerManager.sharedInstance cancelTimerWithName:@"BLELJ"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:AutomaticReconnectionDevice]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"WARNINGSWITCH"]; } #pragma mark - coreBlueTooth --系统代理 - (void)centralManagerDidUpdateState:(CBCentralManager *)central { _mBleManagerState = central.state; if (central.state == CBManagerStatePoweredOn) { // 蓝牙开始重连 [self connectPeripheralWithUUID]; } else if (central.state == CBManagerStatePoweredOff) { [[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtPowerOff object:@"PowerOff"]; CGFloat systemVision = DEVICEVERSION; if (systemVision <10 || systemVision >= 11) { if (self.isConnected) { // 连接断开发出通知 self.isConnected = NO; [[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtDisconnect object:nil]; // 清空所有 self.writecharacteristic = nil; } } [self connectPeripheralWithUUID]; } } //搜索蓝牙设备 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { NSLog(@"搜索到了设备:%@ 标示:%@ advertisementData:%@",peripheral.name,peripheral.identifier.UUIDString,advertisementData); NSLog(@"搜索到的设备UUID:%@",peripheral.identifier.UUIDString); NSLog(@"%@",[advertisementData objectForKey:@"kCBAdvDataServiceData"]); NSString *ble_name = advertisementData[@"kCBAdvDataLocalName"]; NSString *MACString = [[NSString alloc] init]; NSData *manufacturerData = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"]; if (manufacturerData && manufacturerData.length >= 8) { NSData *MACData = [[advertisementData objectForKey:@"kCBAdvDataManufacturerData"] subdataWithRange:NSMakeRange(2, 6)]; MACString = [BluetoothTool getPositiveSequenceMacWithData:MACData]; } BLEModel *bleModel = [BLEModel new]; bleModel.central = central; bleModel.peripheral = peripheral; bleModel.advertisementData = advertisementData; bleModel.RSSI = RSSI; bleModel.UUIDString = peripheral.identifier.UUIDString; bleModel.MACString = MACString; bleModel.DeName = peripheral.name; if (ble_name.length > 0) [self.blePeripheralArr addObject:bleModel]; [[NSNotificationCenter defaultCenter] postNotificationName:@"BLEUPDATA" object:self.blePeripheralArr]; } #pragma mark 连接外设--成功 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { NSLog(@"BLE Connected ---> Device %@", peripheral.name); [GCDTimerManager.sharedInstance cancelTimerWithName:@"BLELJ"]; [peripheral discoverServices:nil]; [self stopScanBLE]; NSLog(@"连接成功!!!!!!"); // 连接成功保存UUID,做重连使用 [[NSUserDefaults standardUserDefaults] setObject:peripheral.identifier.UUIDString forKey:AutomaticReconnectionDevice]; // 订阅了读数据才发出连接成功通知 self.isConnected = YES; self.currenModel.peripheral = peripheral; self.bleCurrentPeripheral = peripheral; [self.bleCurrentPeripheral setDelegate:self]; if (self.currenModel != nil) { NSMutableDictionary *dic = [NSMutableDictionary dictionary]; //[dic setObject:self.currenModel.central forKey:@"central"]; // [dic setObject:peripheral forKey:@"peripheral"]; // [dic setObject:self.currenModel.advertisementData forKey:@"advertisementData"]; [dic setObject:self.currenModel.RSSI forKey:@"RSSI"]; [dic setObject:self.currenModel.UUIDString forKey:@"UUIDString"]; [dic setObject:self.currenModel.MACString forKey:@"MACString"]; [dic setObject:peripheral.name forKey:@"DeName"]; BLEModel *model = [BLEModel yy_modelWithJSON:[self dictToJsonStr:dic]]; self.currenModel = model; [self.cache setObject:model forKey:sDeviceKey]; } [[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtConnectSuccess object:self.currenModel]; } #pragma mark 连接外设——失败 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"设备:%@--连接失败",peripheral.name); [SVProgressHUD showInfoWithStatus:NSLocalizedString(@"连接失败", nil)]; // 连接失败发出通知 [[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtConnectFail object:peripheral.name]; self.isConnected = NO; } #pragma mark 取消或已经断开连接回调 - (void) centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"设备:%@--断开连接",peripheral.name); // 连接断开发出通知 self.isConnected = NO; [[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtDisconnect object:peripheral.name]; // 清空所有 self.writecharacteristic = nil; [self connectPeripheralWithUUID]; } #pragma mark - 中心读取外设实时数据 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error { if (error){NSLog(@"订阅失败");} if (characteristic.isNotifying) {NSLog(@"订阅成功");} else {NSLog(@"取消订阅");} // 判断是否监听设备广播通知成功 -->成功才发送连接成功通知 if (characteristic.isNotifying && [characteristic.UUID isEqual:[CBUUID UUIDWithString:NotifycharacteristicUUIDString]]) { NSLog(@"连接成功!!!!!!"); // 连接成功保存UUID,做重连使用 [[NSUserDefaults standardUserDefaults] setObject:peripheral.identifier.UUIDString forKey:AutomaticReconnectionDevice]; // 订阅了读数据才发出连接成功通知 self.isConnected = YES; self.currenModel.peripheral = peripheral; self.bleCurrentPeripheral = peripheral; [self.bleCurrentPeripheral setDelegate:self]; if (self.currenModel != nil) { NSMutableDictionary *dic = [NSMutableDictionary dictionary]; //[dic setObject:self.currenModel.central forKey:@"central"]; // [dic setObject:peripheral forKey:@"peripheral"]; // [dic setObject:self.currenModel.advertisementData forKey:@"advertisementData"]; [dic setObject:self.currenModel.RSSI forKey:@"RSSI"]; [dic setObject:self.currenModel.UUIDString forKey:@"UUIDString"]; [dic setObject:self.currenModel.MACString forKey:@"MACString"]; [dic setObject:peripheral.name forKey:@"DeName"]; BLEModel *model = [BLEModel yy_modelWithJSON:[self dictToJsonStr:dic]]; self.currenModel = model; [self.cache setObject:model forKey:sDeviceKey]; } [[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtConnectSuccess object:self.currenModel]; } } #pragma mark - 从外围设备读取数据 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { NSLog(@" 设备发过来的 characteristic name:%@ value is:%@",characteristic.UUID,characteristic.value); } #pragma mark 发现服务回调 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { for (CBService *service in peripheral.services) { NSLog(@"BLE Service ---> %@", service); [peripheral discoverCharacteristics:nil forService:service]; } } #pragma mark 发现特征回调 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { if ([service.UUID.UUIDString isEqualToString:@"1802"] || [service.UUID.UUIDString isEqualToString:@"1803"]) { NSLog(@"服务-->%@",service.UUID.UUIDString); for (CBCharacteristic *characteristic in service.characteristics ) { NSLog(@"特征---->%@",characteristic.UUID.UUIDString); if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"] && [service.UUID.UUIDString isEqualToString:@"1802"]) { [peripheral setNotifyValue:true forCharacteristic:characteristic]; self.writecharacteristic = characteristic; } if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"] && [service.UUID.UUIDString isEqualToString:@"1803"]) { [peripheral setNotifyValue:true forCharacteristic:characteristic]; self.writecharacteristicq = characteristic; } } } } //写入值的监听 - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error { NSLog(@"写入成功"); } /// 发送指令 /// - Parameter cmd: 查找设备:0x00,取消查找:0x01,断开后报警:0x02/0xFF - (void)writeCheckBleWithBle:(NSString *)cmd { NSData *data = [cmd dataUsingEncoding:NSUTF8StringEncoding]; [self.bleCurrentPeripheral writeValue:data forCharacteristic:self.writecharacteristic type:CBCharacteristicWriteWithResponse]; } - (NSString *)dictToJsonStr:(NSMutableDictionary *)dict { NSError *error = nil; NSData *jsonData = nil; if (!self) { return nil; } [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { NSString *keyString = nil; NSString *valueString = nil; if ([key isKindOfClass:[NSString class]]) { keyString = key; }else{ keyString = [NSString stringWithFormat:@"%@",key]; } if ([obj isKindOfClass:[NSString class]]) { valueString = obj; }else{ valueString = [NSString stringWithFormat:@"%@",obj]; } [dict setObject:valueString forKey:keyString]; }]; jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error]; if ([jsonData length] == 0 || error != nil) { return nil; } NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; return jsonString; } + (UIViewController *)currentVC { UIWindow * window = [[UIApplication sharedApplication] keyWindow]; if (window.windowLevel != UIWindowLevelNormal) { NSArray *windows = [[UIApplication sharedApplication] windows]; for(UIWindow * tmpWin in windows) { if (tmpWin.windowLevel == UIWindowLevelNormal) { window = tmpWin; break; } } } UIViewController *result = window.rootViewController; while (result.presentedViewController) { result = result.presentedViewController; } if ([result isKindOfClass:[UITabBarController class]]) { result = [(UITabBarController *)result selectedViewController]; } if ([result isKindOfClass:[UINavigationController class]]) { result = [(UINavigationController *)result topViewController]; } return result; } @end