RDSBabyBluetooth.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. //
  2. // RDSBabyBluetooth.m
  3. // RuiZhi
  4. //
  5. // Created by Rayson on 2020/6/13.
  6. // Copyright © 2020 RDIOT. All rights reserved.
  7. //
  8. #import "RDSBabyBluetooth.h"
  9. #import "NSString+convert.h"
  10. #import "NSString+safe.h"
  11. #import "RDSDeallocWatcher.h"
  12. #import <objc/runtime.h>
  13. #define Server_00FF @"00FF"
  14. #define Characteristic_FF01 @"FF01"
  15. @interface RDSBabyBluetooth ()<CBCentralManagerDelegate,CBPeripheralDelegate>
  16. @property (nonatomic, copy) NSString *deviceName;// 要连接的设备名
  17. @property (nonatomic, strong) CBCentralManager *centralManager;
  18. @property (nonatomic,strong) CBCharacteristic *characteristic;
  19. @property (nonatomic,strong) CBPeripheral *peripheral;
  20. @property (nonatomic, strong) NSMapTable *blockTable;
  21. @property (nonatomic, strong) NSMapTable *blockTableDisconnect;
  22. @property (nonatomic, assign) BOOL isBinding;
  23. @end
  24. @implementation RDSBabyBluetooth
  25. //DEF_SINGLETON(RDSBabyBluetooth)
  26. // 初始化单例方法
  27. + (instancetype)sharedInstance
  28. {
  29. static RDSBabyBluetooth *manager = nil;
  30. static dispatch_once_t oneToken;
  31. // dispatch_once 是线程安全的,能够做到在多线程的环境下Block中的代码只会被执行一次
  32. dispatch_once(&oneToken, ^{
  33. manager = [[RDSBabyBluetooth alloc] init];
  34. [manager p_setup];
  35. });
  36. return manager;
  37. }
  38. // 初始化
  39. - (void)p_setup{
  40. //初始化 蓝牙库
  41. dispatch_queue_t queue = dispatch_queue_create("yxlj.com", DISPATCH_QUEUE_SERIAL);
  42. _centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:queue options:@{@"CBCentralManagerOptionShowPowerAlertKey":@YES}];
  43. // 创建 NSMapTable 映射表
  44. self.blockTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory capacity:1];
  45. self.blockTableDisconnect = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory capacity:1];
  46. }
  47. - (void)rds_beginScan{
  48. if(_centralManager == nil){
  49. dispatch_queue_t queue = dispatch_queue_create("yxlj.com", DISPATCH_QUEUE_SERIAL);
  50. _centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:queue options:@{@"CBCentralManagerOptionShowPowerAlertKey":@YES}];
  51. }
  52. else{
  53. [_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:Server_00FF]] options:nil];
  54. }
  55. }
  56. - (void)rds_cancelScan{
  57. //停止扫描
  58. [self.centralManager stopScan];
  59. }
  60. - (void)rds_cancelConnection{
  61. //停止之前的连接
  62. if(self.centralManager && self.peripheral){
  63. [self.centralManager cancelPeripheralConnection:self.peripheral];
  64. }
  65. _isBinding = NO;
  66. }
  67. - (void)rds_refreshScanDeviceName:(NSString *)name{
  68. _isBinding = YES;
  69. self.deviceName = name;
  70. [self rds_refreshScan];
  71. }
  72. - (void)rds_refreshScan{
  73. DDLog(@"重新扫描设备");
  74. [self rds_beginScan];
  75. }
  76. #pragma mark - 写一个值
  77. //写一个值
  78. -(void)rds_writeValue:(NSData *)data{
  79. if (data == nil){
  80. DDLog(@"数据为空,写入失败");
  81. return;
  82. }
  83. if (self.peripheral == nil || self.characteristic == nil) {
  84. DDLog(@"代理为空,写入失败");
  85. return;
  86. }
  87. [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
  88. }
  89. #pragma mark 解析设备回复
  90. - (void)p_receiveData:(NSData *)data{
  91. RDS_WEAKSELF(weakSelf)
  92. [RDSDeviceCmd rds_receiveData:data finish:^(RDSDeviceReply *reply) {
  93. [weakSelf p_cmdResponce:reply];
  94. }];
  95. }
  96. #pragma mark - 蓝牙配置和操作
  97. #pragma mark - CBCentralManagerDelegate
  98. /** 手机蓝牙状态 */
  99. -(void)centralManagerDidUpdateState:(CBCentralManager *)central{
  100. if (central.state == CBManagerStateUnknown) {
  101. NSLog(@"系统蓝牙当前状态不明确");
  102. }else if (central.state == CBManagerStateResetting){
  103. NSLog(@"系统蓝牙设备正在被重置");
  104. }else if (central.state == CBManagerStateUnsupported){
  105. NSLog(@"系统蓝牙设备不支持");
  106. }else if (central.state == CBManagerStateUnauthorized){
  107. NSLog(@"系统蓝未被授权");
  108. }else if (central.state == CBManagerStatePoweredOff){
  109. NSLog(@"系统蓝牙关闭了,请先打开蓝牙");
  110. }else if (central.state == CBManagerStatePoweredOn){
  111. NSLog(@"系统蓝牙开启");
  112. //扫描设备
  113. if(_isBinding){
  114. [_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:Server_00FF]] options:nil];
  115. }
  116. }
  117. }
  118. /** 扫描结果 */
  119. -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
  120. NSLog(@"%@",advertisementData);
  121. // C049EFC91660 807D3AE5E5D4
  122. if ([peripheral.name hasPrefix:self.deviceName]) {
  123. self.peripheral = peripheral;
  124. //连接设备
  125. [central connectPeripheral:peripheral options:nil];
  126. //停止扫描
  127. [self.centralManager stopScan];
  128. }
  129. }
  130. /** 连接成功 */
  131. -(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
  132. NSLog(@"连接成功");
  133. // 设置代理
  134. self.peripheral.delegate = self;
  135. // 根据UUID来寻找服务
  136. [self.peripheral discoverServices:@[[CBUUID UUIDWithString:Server_00FF]]];
  137. }
  138. /** 连接失败 */
  139. -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(nonnull CBPeripheral *)peripheral error:(nullable NSError *)error{
  140. NSLog(@"连接失败");
  141. // 断开连接可以设置重新连接
  142. if(_isBinding){
  143. [central connectPeripheral:peripheral options:nil];
  144. }
  145. }
  146. /** 断开连接 */
  147. - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
  148. NSLog(@"断开连接");
  149. // 断开连接可以设置重新连接
  150. if(_isBinding){
  151. [central connectPeripheral:peripheral options:nil];
  152. }
  153. }
  154. #pragma mark - CBPeripheralDelegate
  155. /** 发现服务 */
  156. -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
  157. CBService * __nullable findService = nil;
  158. // 遍历服务
  159. for (CBService *service in peripheral.services) {
  160. NSLog(@"遍历服务 UUID:%@",service.UUID);
  161. if ([[service UUID] isEqual:[CBUUID UUIDWithString:Server_00FF]]) {
  162. findService = service;
  163. }
  164. }
  165. if (findService)
  166. [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:Characteristic_FF01]] forService:findService];
  167. }
  168. /** 发现特征回调 */
  169. - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
  170. // 遍历出所需要的特征
  171. for (CBCharacteristic *characteristic in service.characteristics) {
  172. NSLog(@"所有特征:%@", characteristic);
  173. // 从外设开发人员那里拿到不同特征的UUID,不同特征做不同事情,比如有读取数据的特征,也有写入数据的特征
  174. if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:Characteristic_FF01]]) {
  175. self.characteristic = characteristic;
  176. // 订阅通知
  177. [self.peripheral setNotifyValue:YES forCharacteristic:self.characteristic];
  178. }
  179. }
  180. }
  181. /** 订阅状态的改变 */
  182. -(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
  183. if (error) {
  184. NSLog(@"订阅失败");
  185. NSLog(@"%@",error);
  186. }
  187. if (characteristic.isNotifying) {
  188. NSLog(@"订阅成功");
  189. BLOCK_SAFE_RUN(_onConnectDeviceSuccessAndCanWrite);
  190. } else {
  191. NSLog(@"取消订阅");
  192. }
  193. }
  194. /** 接收到数据回调 */
  195. - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
  196. // 拿到外设发送过来的数据
  197. // NSData *data = characteristic.value;
  198. // NSString *msg = [[ NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  199. // //所有控制符都会被替换成空字符。
  200. // NSString *jsonString =[msg stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
  201. //
  202. // NSLog(@"收到信息 %@",jsonString);
  203. [self p_receiveData:characteristic.value];
  204. }
  205. /** 写入数据回调 */
  206. - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
  207. NSLog(@"写入成功");
  208. }
  209. #pragma mark - block 一对多监听回调
  210. - (void)addObserver:(id)observer callback:(RDSCmdTouchResponce)callback {
  211. // 这里要打破循环引用,因为关联代码中 watch 被 observer 持有,而 watch 中的 callback 去调用了 observer
  212. __weak typeof (observer) weakObserver = observer;
  213. RDSDeallocWatcher *watch = [[RDSDeallocWatcher alloc] initWithDeallocCallback:^{
  214. __strong typeof (observer) strongObserver = weakObserver;
  215. [self removeObserver:strongObserver];
  216. }];
  217. [self.blockTable setObject:callback forKey:observer];
  218. // 将 observer 与 watch 进行绑定关联,key 则使用 observer 的打印地址
  219. objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], watch, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  220. }
  221. - (void)addObserver:(id)observer block:(RDSDisconnectDeviceBlock)block {
  222. // 这里要打破循环引用,因为关联代码中 watch 被 observer 持有,而 watch 中的 callback 去调用了 observer
  223. __weak typeof (observer) weakObserver = observer;
  224. RDSDeallocWatcher *watch = [[RDSDeallocWatcher alloc] initWithDeallocCallback:^{
  225. __strong typeof (observer) strongObserver = weakObserver;
  226. [self removeObserver:strongObserver];
  227. }];
  228. [self.blockTableDisconnect setObject:block forKey:observer];
  229. // 将 observer 与 watch 进行绑定关联,key 则使用 observer 的打印地址
  230. objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], watch, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  231. }
  232. - (void)removeObserver:(id)observer {
  233. [self.blockTable removeObjectForKey:observer];
  234. objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  235. [self.blockTableDisconnect removeObjectForKey:observer];
  236. objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  237. }
  238. - (void)p_cmdResponce:(RDSDeviceReply *)reply{
  239. // 回调
  240. [[[self.blockTable objectEnumerator] allObjects] enumerateObjectsUsingBlock:^(RDSCmdTouchResponce callback, NSUInteger idx, BOOL * _Nonnull stop) {
  241. callback(reply);
  242. }];
  243. }
  244. // block 一对多监听设备信息回调
  245. - (void)rds_cmdResponceObserve:(id)observe responce:(RDSCmdTouchResponce)responceBlock{
  246. if (observe == nil) {
  247. return;
  248. }
  249. [self addObserver:observe callback:responceBlock];
  250. }
  251. - (void)rds_disconnectDeviceObserve:(id)observe block:(RDSDisconnectDeviceBlock)block{
  252. if (observe == nil) {
  253. return;
  254. }
  255. [self addObserver:observe block:block];
  256. }
  257. - (void)p_disconnectDevice{
  258. [[[self.blockTableDisconnect objectEnumerator] allObjects] enumerateObjectsUsingBlock:^(RDSDisconnectDeviceBlock block, NSUInteger idx, BOOL * _Nonnull stop) {
  259. block();
  260. }];
  261. }
  262. @end