// // RDSBabyBluetooth.m // RuiZhi // // Created by Rayson on 2020/6/13. // Copyright © 2020 RDIOT. All rights reserved. // #import "RDSBabyBluetooth.h" #import "NSString+convert.h" #import "NSString+safe.h" #import "RDSDeallocWatcher.h" #import #define Server_00FF @"00FF" #define Characteristic_FF01 @"FF01" @interface RDSBabyBluetooth () @property (nonatomic, copy) NSString *deviceName;// 要连接的设备名 @property (nonatomic, strong) CBCentralManager *centralManager; @property (nonatomic,strong) CBCharacteristic *characteristic; @property (nonatomic,strong) CBPeripheral *peripheral; @property (nonatomic, strong) NSMapTable *blockTable; @property (nonatomic, strong) NSMapTable *blockTableDisconnect; @property (nonatomic, assign) BOOL isBinding; @end @implementation RDSBabyBluetooth //DEF_SINGLETON(RDSBabyBluetooth) // 初始化单例方法 + (instancetype)sharedInstance { static RDSBabyBluetooth *manager = nil; static dispatch_once_t oneToken; // dispatch_once 是线程安全的,能够做到在多线程的环境下Block中的代码只会被执行一次 dispatch_once(&oneToken, ^{ manager = [[RDSBabyBluetooth alloc] init]; [manager p_setup]; }); return manager; } // 初始化 - (void)p_setup{ //初始化 蓝牙库 dispatch_queue_t queue = dispatch_queue_create("yxlj.com", DISPATCH_QUEUE_SERIAL); _centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:queue options:@{@"CBCentralManagerOptionShowPowerAlertKey":@YES}]; // 创建 NSMapTable 映射表 self.blockTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory capacity:1]; self.blockTableDisconnect = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory capacity:1]; } - (void)rds_beginScan{ if(_centralManager == nil){ dispatch_queue_t queue = dispatch_queue_create("yxlj.com", DISPATCH_QUEUE_SERIAL); _centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:queue options:@{@"CBCentralManagerOptionShowPowerAlertKey":@YES}]; } else{ [_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:Server_00FF]] options:nil]; } } - (void)rds_cancelScan{ //停止扫描 [self.centralManager stopScan]; } - (void)rds_cancelConnection{ //停止之前的连接 if(self.centralManager && self.peripheral){ [self.centralManager cancelPeripheralConnection:self.peripheral]; } _isBinding = NO; } - (void)rds_refreshScanDeviceName:(NSString *)name{ _isBinding = YES; self.deviceName = name; [self rds_refreshScan]; } - (void)rds_refreshScan{ DDLog(@"重新扫描设备"); [self rds_beginScan]; } #pragma mark - 写一个值 //写一个值 -(void)rds_writeValue:(NSData *)data{ if (data == nil){ DDLog(@"数据为空,写入失败"); return; } if (self.peripheral == nil || self.characteristic == nil) { DDLog(@"代理为空,写入失败"); return; } [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse]; } #pragma mark 解析设备回复 - (void)p_receiveData:(NSData *)data{ RDS_WEAKSELF(weakSelf) [RDSDeviceCmd rds_receiveData:data finish:^(RDSDeviceReply *reply) { [weakSelf p_cmdResponce:reply]; }]; } #pragma mark - 蓝牙配置和操作 #pragma mark - CBCentralManagerDelegate /** 手机蓝牙状态 */ -(void)centralManagerDidUpdateState:(CBCentralManager *)central{ if (central.state == CBManagerStateUnknown) { NSLog(@"系统蓝牙当前状态不明确"); }else if (central.state == CBManagerStateResetting){ NSLog(@"系统蓝牙设备正在被重置"); }else if (central.state == CBManagerStateUnsupported){ NSLog(@"系统蓝牙设备不支持"); }else if (central.state == CBManagerStateUnauthorized){ NSLog(@"系统蓝未被授权"); }else if (central.state == CBManagerStatePoweredOff){ NSLog(@"系统蓝牙关闭了,请先打开蓝牙"); }else if (central.state == CBManagerStatePoweredOn){ NSLog(@"系统蓝牙开启"); //扫描设备 if(_isBinding){ [_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:Server_00FF]] options:nil]; } } } /** 扫描结果 */ -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ NSLog(@"%@",advertisementData); // C049EFC91660 807D3AE5E5D4 if ([peripheral.name hasPrefix:self.deviceName]) { self.peripheral = peripheral; //连接设备 [central connectPeripheral:peripheral options:nil]; //停止扫描 [self.centralManager stopScan]; } } /** 连接成功 */ -(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ NSLog(@"连接成功"); // 设置代理 self.peripheral.delegate = self; // 根据UUID来寻找服务 [self.peripheral discoverServices:@[[CBUUID UUIDWithString:Server_00FF]]]; } /** 连接失败 */ -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(nonnull CBPeripheral *)peripheral error:(nullable NSError *)error{ NSLog(@"连接失败"); // 断开连接可以设置重新连接 if(_isBinding){ [central connectPeripheral:peripheral options:nil]; } } /** 断开连接 */ - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error { NSLog(@"断开连接"); // 断开连接可以设置重新连接 if(_isBinding){ [central connectPeripheral:peripheral options:nil]; } } #pragma mark - CBPeripheralDelegate /** 发现服务 */ -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ CBService * __nullable findService = nil; // 遍历服务 for (CBService *service in peripheral.services) { NSLog(@"遍历服务 UUID:%@",service.UUID); if ([[service UUID] isEqual:[CBUUID UUIDWithString:Server_00FF]]) { findService = service; } } if (findService) [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:Characteristic_FF01]] forService:findService]; } /** 发现特征回调 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { // 遍历出所需要的特征 for (CBCharacteristic *characteristic in service.characteristics) { NSLog(@"所有特征:%@", characteristic); // 从外设开发人员那里拿到不同特征的UUID,不同特征做不同事情,比如有读取数据的特征,也有写入数据的特征 if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:Characteristic_FF01]]) { self.characteristic = characteristic; // 订阅通知 [self.peripheral setNotifyValue:YES forCharacteristic:self.characteristic]; } } } /** 订阅状态的改变 */ -(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (error) { NSLog(@"订阅失败"); NSLog(@"%@",error); } if (characteristic.isNotifying) { NSLog(@"订阅成功"); BLOCK_SAFE_RUN(_onConnectDeviceSuccessAndCanWrite); } else { NSLog(@"取消订阅"); } } /** 接收到数据回调 */ - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { // 拿到外设发送过来的数据 // NSData *data = characteristic.value; // NSString *msg = [[ NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // //所有控制符都会被替换成空字符。 // NSString *jsonString =[msg stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]]; // // NSLog(@"收到信息 %@",jsonString); [self p_receiveData:characteristic.value]; } /** 写入数据回调 */ - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error { NSLog(@"写入成功"); } #pragma mark - block 一对多监听回调 - (void)addObserver:(id)observer callback:(RDSCmdTouchResponce)callback { // 这里要打破循环引用,因为关联代码中 watch 被 observer 持有,而 watch 中的 callback 去调用了 observer __weak typeof (observer) weakObserver = observer; RDSDeallocWatcher *watch = [[RDSDeallocWatcher alloc] initWithDeallocCallback:^{ __strong typeof (observer) strongObserver = weakObserver; [self removeObserver:strongObserver]; }]; [self.blockTable setObject:callback forKey:observer]; // 将 observer 与 watch 进行绑定关联,key 则使用 observer 的打印地址 objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], watch, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)addObserver:(id)observer block:(RDSDisconnectDeviceBlock)block { // 这里要打破循环引用,因为关联代码中 watch 被 observer 持有,而 watch 中的 callback 去调用了 observer __weak typeof (observer) weakObserver = observer; RDSDeallocWatcher *watch = [[RDSDeallocWatcher alloc] initWithDeallocCallback:^{ __strong typeof (observer) strongObserver = weakObserver; [self removeObserver:strongObserver]; }]; [self.blockTableDisconnect setObject:block forKey:observer]; // 将 observer 与 watch 进行绑定关联,key 则使用 observer 的打印地址 objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], watch, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)removeObserver:(id)observer { [self.blockTable removeObjectForKey:observer]; objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self.blockTableDisconnect removeObjectForKey:observer]; objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)p_cmdResponce:(RDSDeviceReply *)reply{ // 回调 [[[self.blockTable objectEnumerator] allObjects] enumerateObjectsUsingBlock:^(RDSCmdTouchResponce callback, NSUInteger idx, BOOL * _Nonnull stop) { callback(reply); }]; } // block 一对多监听设备信息回调 - (void)rds_cmdResponceObserve:(id)observe responce:(RDSCmdTouchResponce)responceBlock{ if (observe == nil) { return; } [self addObserver:observe callback:responceBlock]; } - (void)rds_disconnectDeviceObserve:(id)observe block:(RDSDisconnectDeviceBlock)block{ if (observe == nil) { return; } [self addObserver:observe block:block]; } - (void)p_disconnectDevice{ [[[self.blockTableDisconnect objectEnumerator] allObjects] enumerateObjectsUsingBlock:^(RDSDisconnectDeviceBlock block, NSUInteger idx, BOOL * _Nonnull stop) { block(); }]; } @end