LEONBLManager.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. //
  2. // LEONBLManager.m
  3. // Unity-iPhone
  4. //
  5. // Created by Leon on 2021/4/23.
  6. #import "LEONBLManager.h"
  7. #import "AlgorithmTool.h"
  8. //CBCentralManagerDelegate 蓝牙外设回调代理
  9. //CBPeripheralDelegate 蓝牙链接后的数据回调代理
  10. @interface LEONBLManager()<CBCentralManagerDelegate,CBPeripheralDelegate>
  11. @end
  12. @implementation LEONBLManager
  13. #pragma mark --------------------------------------- public methon ==>> 单例
  14. static LEONBLManager * instance;
  15. //[[self alloc] init] 类方法使用self时,self代表类本身即Class
  16. //类方法中不能访问“属性”和“实例变量”,只能访问“类”对象
  17. +(instancetype)sharedInstance{
  18. // NSLog(@"LEONBLManager === 1");
  19. return [[self alloc] init];
  20. }
  21. //初始化一个对象的时候,[[Class alloc] init],其实是做了两件事。
  22. //alloc 给对象分配内存空间,init是对对象的初始化,包括设置成员变量初值这些工作。
  23. //而给对象分配空间,除了alloc方法之外,还有另一个方法: allocWithZone.
  24. //使用alloc方法初始化一个类的实例的时候,默认是调用了allocWithZone的方法。为了保持单例类实例的唯一性,需要覆盖所有会生成新的实例的方法,如果初始化这个单例类的时候不走[[Class alloc] init] ,而是直接 allocWithZone, 那么这个单例就不再是单例了,所以必须把这个方法也堵上。
  25. + (instancetype)allocWithZone:(struct _NSZone *)zone{
  26. // NSLog(@"LEONBLManager === 2");
  27. static dispatch_once_t onceToken;
  28. dispatch_once(&onceToken, ^{
  29. instance = [super allocWithZone:zone];
  30. });
  31. return instance;
  32. }
  33. //实例对象能访问“属性”和“实例变量”
  34. - (instancetype)init{
  35. // NSLog(@"LEONBLManager === 3");
  36. static dispatch_once_t onceToken;
  37. dispatch_once(&onceToken, ^{
  38. instance = [super init];
  39. [self initCBCentralManager];
  40. });
  41. return instance;
  42. }
  43. /**
  44. 初始外设管理类
  45. */
  46. -(void)initCBCentralManager{
  47. //蓝牙没打开时alert提示框
  48. // NSDictionary *options = @{CBCentralManagerOptionShowPowerAlertKey:@(YES)};
  49. NSDictionary *options = @{CBCentralManagerScanOptionAllowDuplicatesKey:@(YES)};
  50. self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:options];
  51. NSLog(@"LEONBLManager ===>> initCBCentralManager");
  52. }
  53. #pragma mark ---------------- public methon ==>> 外部 操作蓝牙外设
  54. /**
  55. 开始扫描蓝牙外设
  56. @param serviceUUIDs 一个CBUUID对象表示要扫描的服务。
  57. @param options 一个可选的字典,指定扫描选项
  58. */
  59. - (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options{
  60. [self.centralManager scanForPeripheralsWithServices:serviceUUIDs options:options];
  61. NSLog(@"开始扫描外设");
  62. }
  63. /**
  64. 停止扫描蓝牙外设
  65. */
  66. - (void)stopScan{
  67. [self.centralManager stopScan];
  68. NSLog(@"停止扫描外设");
  69. }
  70. /**
  71. 外部主动链接蓝牙
  72. @param peripheral 待链接的CBPeripheral对象
  73. @param options 一个可选的字典,指定连接行为选项
  74. */
  75. - (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options{
  76. if (peripheral==self.peripheral){
  77. [self.centralManager connectPeripheral:self.peripheral options:options];
  78. NSLog(@"主设备 主动连接蓝牙外设 = %@ %p %p",self.peripheral.name,self.peripheral,peripheral);
  79. }else if (peripheral==self.vicePeripheral){
  80. [self.centralManager connectPeripheral:self.vicePeripheral options:options];
  81. NSLog(@"副设备 主动连接蓝牙外设 = %@ %p %p",self.vicePeripheral.name,self.vicePeripheral,peripheral);
  82. }
  83. }
  84. /**
  85. 外部主动断开蓝牙链接
  86. @param peripheral 待断开的CBPeripheral对象
  87. */
  88. - (void)cancelPeripheralConnection:(int)type{
  89. if (self.peripheral!=nil&&type==0){
  90. [self.centralManager cancelPeripheralConnection:self.peripheral];
  91. NSLog(@"主设备 主动断开蓝牙链接 = %@",self.peripheral.name);
  92. // self.peripheral = nil;
  93. }else if (self.vicePeripheral!=nil&&type==1){
  94. [self.centralManager cancelPeripheralConnection:self.vicePeripheral];
  95. NSLog(@"副设备 主动断开蓝牙链接 = %@",self.vicePeripheral.name);
  96. // self.vicePeripheral = nil;
  97. }
  98. }
  99. #pragma mark -------------- public methon ==>> 外部 操作蓝牙数据
  100. /**
  101. 主动向蓝牙写入数据
  102. @param data 数据流
  103. @param characteristic 可以写入的特征
  104. @param type 写入数据的类型
  105. */
  106. - (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic writeType:(CBCharacteristicWriteType)writeType deviceType:(DEVICE_TYPE)deviceType{
  107. if (deviceType==DEVICETYPE_MAIN){
  108. [self.peripheral writeValue:data forCharacteristic:characteristic type:writeType];
  109. }else if (deviceType==DEVICETYPE_VICE){
  110. [self.vicePeripheral writeValue:data forCharacteristic:characteristic type:writeType];
  111. }
  112. }
  113. #pragma mark =================================== CentralManagerDelegate 代理回调
  114. //返回蓝牙状态
  115. - (void)centralManagerDidUpdateState:(CBCentralManager *)central{
  116. // NSLog(@"蓝牙状态更新 = %ld",(long)central.state);
  117. if (central!=nil){
  118. if (_stateUpdateBlock){
  119. _stateUpdateBlock(central);
  120. }
  121. }
  122. }
  123. //扫描发现蓝牙设备
  124. - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
  125. if (_discoverPeripheralBlock){
  126. //过滤无效外设
  127. if (peripheral.name.length>=1){
  128. // NSLog(@"扫描发现蓝牙外设 = %@",peripheral.name);
  129. _discoverPeripheralBlock(central, peripheral, advertisementData, RSSI);
  130. }
  131. }
  132. }
  133. //蓝牙链接成功
  134. - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
  135. if (_successfulBlock){
  136. _successfulBlock(peripheral);
  137. }
  138. if (peripheral == self.peripheral){
  139. NSLog(@"主设备 查找 %@ 的服务",peripheral.name);
  140. //声明蓝牙delegate
  141. self.peripheral.delegate = self;
  142. //serviceUUIDs传nil表示去获取连接到的蓝牙的所有服务
  143. [self.peripheral discoverServices:nil];
  144. }else if (peripheral == self.vicePeripheral){
  145. NSLog(@"副设备 查找 %@ 的服务",peripheral.name);
  146. //声明蓝牙delegate
  147. self.vicePeripheral.delegate = self;
  148. //serviceUUIDs传nil表示去获取连接到的蓝牙的所有服务
  149. [self.vicePeripheral discoverServices:nil];
  150. }
  151. }
  152. //蓝牙链接失败
  153. - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
  154. if (_connectFailureBlock){
  155. _connectFailureBlock(error);
  156. }
  157. if (peripheral == self.peripheral){
  158. NSLog(@"主设备 蓝牙外设链接失败 = %@",peripheral.name);
  159. }else if (peripheral == self.vicePeripheral){
  160. NSLog(@"副设备 蓝牙外设链接失败 = %@",peripheral.name);
  161. }
  162. }
  163. //蓝牙链接断开
  164. - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
  165. if (_disConnectBlock){
  166. _disConnectBlock(peripheral,error);
  167. }
  168. if (peripheral == self.peripheral){
  169. NSLog(@"主设备 蓝牙外设丢失链接 = %@",peripheral.name);
  170. }else if (peripheral == self.vicePeripheral){
  171. NSLog(@"副设备 蓝牙外设丢失链接 = %@",peripheral.name);
  172. }
  173. }
  174. #pragma mark =================================== PeripheralDelegate 代理回调
  175. //发现服务的回调
  176. - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error{
  177. if (_discoveredServicesBlock){
  178. _discoveredServicesBlock(peripheral,peripheral.services,error);
  179. }
  180. if (peripheral == self.peripheral){
  181. NSLog(@"主设备发现 %@ 的服务值:%@ 开始查找它的特征",peripheral.name,peripheral.services);
  182. }else if (peripheral == self.vicePeripheral){
  183. NSLog(@"副设备发现 %@ 的服务值:%@ 开始查找它的特征",peripheral.name,peripheral.services);
  184. }
  185. //扫描所有的特征
  186. for (CBService *service in peripheral.services){
  187. // NSLog(@"扫描所有的特征 %@",service);
  188. // peripheral.delegate = self;
  189. [peripheral discoverCharacteristics:nil forService:service];
  190. }
  191. }
  192. //发现特征的回调
  193. - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error{
  194. // NSLog(@"发现所有服务+特征的值 %p %p %p",self.peripheral,self.vicePeripheral,peripheral);
  195. __weak __typeof(self)weakself = self;
  196. if (_discoverCharacteristicsBlock){
  197. if (peripheral == weakself.peripheral){
  198. _discoverCharacteristicsBlock(peripheral,service,service.characteristics,error);
  199. for (CBCharacteristic * cha in service.characteristics){
  200. if (cha.properties == 12){//写
  201. NSLog(@"主设备发现 %@ 写的特征值:%@",peripheral.name,cha.UUID);
  202. }else if (cha.properties == 16){//读
  203. NSLog(@"主设备发现 %@ 读的特征值:%@",peripheral.name,cha.UUID);
  204. }
  205. }
  206. }else if (peripheral == weakself.vicePeripheral){
  207. _discoverCharacteristicsBlock(peripheral,service,service.characteristics,error);
  208. NSLog(@"副设备 发现 %@ 服务+特征",peripheral.name);
  209. }
  210. }
  211. }
  212. //收到数据的回调
  213. - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
  214. __weak __typeof(self)weakself = self;
  215. if (_readValueForCharacteristicBlock){
  216. NSData *data = characteristic.value;
  217. if (peripheral == weakself.peripheral){
  218. if (characteristic.value.length >60){
  219. weakself.main_read = 0;
  220. int newTs = [AlgorithmTool dataToChar:[characteristic.value subdataWithRange:NSMakeRange(54, 1)]];;//ts 时间戳
  221. self.totalPackages++;
  222. int total_loss = self.tempTs - abs(newTs);
  223. int abs_loss = abs(total_loss);
  224. // NSLog(@"收到主设备 推送的报文 时间戳 ================ >>%d %d 【%d - %d = %d】",self.totalPackages,newTs,self.tempTs,abs(newTs),abs_loss);
  225. if (abs_loss>2){
  226. self.totalLoss = self.totalLoss+(abs_loss/2-1);
  227. // NSLog(@"系统api丢包 ===================================================================== >> %d %d/%d",abs_loss/2-1,self.totalLoss,self.totalPackages);
  228. }
  229. self.tempTs = abs(newTs);
  230. }
  231. _readValueForCharacteristicBlock(weakself.peripheral,characteristic,data,error);
  232. // NSLog(@"收到主设备 %@ 推送的报文 %@",peripheral.name,data);
  233. }else if (peripheral == weakself.vicePeripheral){
  234. if (characteristic.value.length >60){
  235. // int ts = [AlgorithmTool dataToChar:[characteristic.value subdataWithRange:NSMakeRange(54, 1)]];;//ts 时间戳
  236. // NSLog(@"收到副设备 推送的报文 时间戳 ==============>> %d",ts);
  237. weakself.vice_read = 0;
  238. }
  239. _readValueForCharacteristicBlock(weakself.vicePeripheral,characteristic,data,error);
  240. // NSLog(@"收到副设备 %@ 推送的报文 %@",peripheral.name,data);
  241. }
  242. }
  243. }
  244. //写入数据的回调
  245. - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
  246. __weak __typeof(self)weakself = self;
  247. if (_writeToCharacteristicBlock){
  248. if (peripheral==weakself.peripheral){
  249. _writeToCharacteristicBlock(weakself.peripheral,characteristic,error,DEVICETYPE_MAIN);
  250. // NSLog(@"收到主设备 %@ 的response",peripheral.name);
  251. }else if (peripheral == weakself.vicePeripheral){
  252. _writeToCharacteristicBlock(weakself.vicePeripheral,characteristic,error,DEVICETYPE_VICE);
  253. // NSLog(@"收到附设备 %@ 的response",peripheral.name);
  254. }
  255. }
  256. }
  257. /*!
  258. * @method peripheral:didReadRSSI:error:
  259. *
  260. * @param peripheral The peripheral providing this update.
  261. * @param RSSI The current RSSI of the link.
  262. * @param error If an error occurred, the cause of the failure.
  263. *
  264. * @discussion This method returns the result of a @link readRSSI: @/link call.
  265. */
  266. - (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error{
  267. if (_rssiBlock){
  268. _rssiBlock(RSSI);
  269. }
  270. }
  271. @end