蓝牙架构的搭建

  • 前言:笔者认为,如果只是单纯的传授大家代码怎么敲,那么大家很有可能在实际开发中难以运用。刚好本人曾经参与过多款智能硬件开发的架构搭建,本小节本人就现场带领大家开发出一个通用的蓝牙工具类

    • 既然是工具类,虽然大家以后可以在开发中直接拿去用,但是我的目的是想要传授给大家架构的思想,而不是教大家如何偷懒
    • 为了能够让大家对蓝牙通讯理解的更加的透彻,本人专门买了一个小米手环,并且经过大量的测试,破解了部分小米的蓝牙协议(小米手环蓝牙数据是没有加密的) 
      • 只有对技术执着的追求,才能造就更高的品质
  • 目前该工具类由于时间原因,主要是想让大家熟悉蓝牙开发的流程,并没有对更深层次的架构做研究

    • 1.只支持蓝牙与外设一对一连接,一对多未做复杂处理

      • 一般开发中,一对一够用了
    • 2.对API接口的架构没有做深入的探讨研究 
      • 关于类的接口设计,将会在下一个课程阶段实用技术项目阶段再给大家重点介绍,目前大家也很难吸收太难的知识点
    • 3.本人将会在后期继续优化我们的框架,并且放入github供全球的iOS开发朋友们使用 
      • 中国软件的下一步发展就是要走向国际一流
  • HMBluetoothManager.h文件

#import <CoreBluetooth/CoreBluetooth.h>

#define kHMBluetoothManager [HMBluetoothManager shareInstance]

@interface HMBluetoothManager : NSObject

//单利类实现
+(HMBluetoothManager*)shareInstance; //蓝牙中心
@property(nonatomic,strong)CBCentralManager *CB_central;
//蓝牙外设数组,蓝牙支持一对多连接(一个中心 多个外设)
//扫描到的外设数组
@property(nonatomic,strong)NSMutableArray <CBPeripheral *>*scanArr; //已经连接的外设数组
@property(nonatomic,strong)NSMutableArray <CBPeripheral *>*connectArr; //扫描外设成功回调
@property(nonatomic,copy)void(^scanPeripheralUpdate)(CBPeripheral *peripheral); //连接外设成功回调
@property(nonatomic,copy)void(^connectedPeripheral)(CBPeripheral *peripheral,NSString *connectState); //当前激活的外设(目前的架构暂时只支持一对一连接)
@property(nonatomic,strong)CBPeripheral *currentPeripheral;
//当前蓝牙特征(发送数据)
@property (strong ,nonatomic) CBCharacteristic *currentCharacteristic;
//当前发送数据的UUID
@property(nonatomic,strong)NSString *UUID; /**
检测蓝牙是否可用 @param completion 错误表示 可用标签
@return 可用标签
*/
- (BOOL)isDeviceBluetoothAvaliable:(void(^)(NSError *error,BOOL flag))completion; /**
开始扫描
*/
- (void)BeginScanPeripheral:(void(^)(CBPeripheral *peripheral))scanPeripheralUpdate; /**
连接外设 @param peripheral 外设
@param connectedPeripheral 连接回调
*/
- (void)connectPeripheral:(CBPeripheral *)peripheral Completion:(void(^)(CBPeripheral *peripheral,NSString *connectState))connectedPeripheral; /**
发送数据 @param value 数据
@param peripheral 外设
@param characteristic 特征
*/
- (void)writeValue:(NSData *)value toPeripheral:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic; @end
  • HMBluetoothManager.m文件
#import "HMBluetoothManager.h"

//蓝牙框架
#import <CoreBluetooth/CoreBluetooth.h> #define kDisconnectPeripheralNotification @"kDisconnectPeripheralNotification" const NSString *connectStateSuccess = @"连接成功";
const NSString *connectStateRepeat = @"外设已经在连接列表中";
const NSString *connectStateFaild = @"连接失败"; @interface HMBluetoothManager ()<CBCentralManagerDelegate,CBPeripheralDelegate> @end @implementation HMBluetoothManager //单利类实现
+(HMBluetoothManager*)shareInstance
{
static HMBluetoothManager *manager = nil; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[HMBluetoothManager alloc] init];
}); return manager;
} - (instancetype)init
{
self = [super init]; //初始化数组
self.scanArr = [NSMutableArray array];
self.connectArr = [NSMutableArray array]; return self;
} #pragma mark - 检测蓝牙是否可用 /**
检测蓝牙是否可用 @param completion 错误表示 可用标签
@return 可用标签
*/
- (BOOL)isDeviceBluetoothAvaliable:(void(^)(NSError *error,BOOL flag))completion
{ NSString * state = nil;
BOOL flag = NO; switch ([self.CB_central state])
{
case CBCentralManagerStateUnsupported:
state = @"系统不支持蓝牙.";
break;
case CBCentralManagerStateUnauthorized:
state = @"手机蓝牙未开启";
break;
case CBCentralManagerStatePoweredOff:
state = @"Bluetooth is currently powered off.";
break;
case CBCentralManagerStatePoweredOn:
state = @"蓝牙可用";
flag = YES;
break;
case CBCentralManagerStateUnknown:
flag = YES;
state = @"未知错误"; break;
default: break;
}
if ([state isEqualToString:@"未知错误"]) {
//第一次开启扫描可能会出现未知错误,只需要再次调用扫描即可
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.CB_central scanForPeripheralsWithServices:nil options:nil];
});
} /**创建error
*domain:作用域,表示error报错的位置
* code:错误码
* userInfo:错误描述键值对,key一般使用系统默认NSLocalizedDescriptionKey
*/
NSError *error = [NSError errorWithDomain:@"HMBluetoothManager" code:- userInfo:@{NSLocalizedDescriptionKey:state}];
//回调block,非空判断不要忘记
if (completion) {
completion(error,flag);
} return flag;
} #pragma mark - 1.开始扫描 - (void)BeginScanPeripheral:(void(^)(CBPeripheral *peripheral))scanPeripheralUpdate
{
//1.创建蓝牙中心
if (!self.CB_central) {
//参数是代理 和线程)
self.CB_central = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
} //2.判断当前设备是否支持蓝牙
//NSAssert:第一个参数condition表示判断条件 第二个参数desc表示描述。当判断条件不成立时程序会崩溃并且打印描述
NSAssert([self isDeviceBluetoothAvaliable:nil], @"手机不支持蓝牙"); self.scanPeripheralUpdate = scanPeripheralUpdate; //3.开始扫描
/**
Services:查找指定的外设,不设置表示查找所有的外设
options:查找方式,一般设为nil使用系统默认
*/
[self.CB_central scanForPeripheralsWithServices:nil options:nil];
} #pragma mark -3.连接外设 - (void)connectPeripheral:(CBPeripheral *)peripheral Completion:(void(^)(CBPeripheral *peripheral,NSString *connectState))connectedPeripheral
{
self.connectedPeripheral = connectedPeripheral;
//如果是已经连接,则直接返回
if (peripheral.state == CBPeripheralStateConnected) {
connectedPeripheral(peripheral,connectStateRepeat);
}
else
{
[self.CB_central connectPeripheral:peripheral options:nil];
}
} #pragma mark -蓝牙中心代理 //蓝牙中心有更新状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{ } #pragma mark - 2.扫描到外设
//查到外设后,停止扫描,连接设备 -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
//外设相关数据
NSLog(@"%@",advertisementData);
//外设唯一标识符
NSLog(@"%@",peripheral.identifier);
//外设的名称
NSLog(@"%@",peripheral.name);
//与外设的信号强度
NSLog(@"%@",RSSI); self.scanPeripheralUpdate(peripheral);
//添加到扫描数组(工具类的封装,不应该在内部处理与自身无关的业务逻辑,所以这里不要连接设备,应该封装连接方法让外部调用)
//添加之前做一个重复判断,避免同一外设被多次添加
if (![self.scanArr containsObject:peripheral]) {
[self.scanArr addObject:peripheral];
} } #pragma mark - 4.连接外设成功,开始寻找服务
//连接外设成功,开始发现服务
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { self.connectedPeripheral(peripheral,connectStateSuccess); //设为当前连接的外设
self.currentPeripheral = peripheral; //添加到已经连接的数组
[self.connectArr addObject:peripheral];
//设置代理
[peripheral setDelegate:self];
//发现服务
[peripheral discoverServices:nil]; } //连接外设失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{ self.connectedPeripheral(peripheral,connectStateFaild);
} //连接断开
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
//这里应该主动发送通知告知外部
[[NSNotificationCenter defaultCenter] postNotificationName:kDisconnectPeripheralNotification object:nil userInfo:@{@"key":peripheral}]; //断开连接之后,应当从连接列表中移除外设
[self.connectArr removeObject:peripheral]; // We're disconnected, so start scanning again } #pragma mark CBPeripheralDelegate 外设代理 #pragma mark- 5.发现服务,搜索特征
-(void) peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ if (error) {
NSLog(@"Error discovering services: %@", [error localizedDescription]);
return;
} int i=;
// for (CBService *s in peripheral.services) {
// [self.nServices addObject:s];
// }
//遍历服务,发现特征
for (CBService *s in peripheral.services) {
NSLog(@"%@",[NSString stringWithFormat:@"%d :服务 UUID: %@(%@)",i,s.UUID.data,s.UUID]);
i++;
[peripheral discoverCharacteristics:nil forService:s];
}
} #pragma mark- 6.已搜索到某个服务的特征Characteristics
-(void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{ // Deal with errors (if any)
if (error) {
NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
return;
} //从服务中遍历特征
for (CBCharacteristic *c in service.characteristics) { NSLog(@"%@",[NSString stringWithFormat:@"发现特征的服务UUID:%@ 该特征UUID:%@",service.UUID ,c.UUID]); //如果是当前发送数据的UUID,则保存该特征
if ([[c.UUID UUIDString] isEqual:self.UUID]) {
self.currentCharacteristic = c; }
//开启与特征之间的通知(中心与外设长连接,当特征发送数据过来时,能够及时收到)
[peripheral setNotifyValue:YES forCharacteristic:c];
//读取特征服务,一次性
// [peripheral readValueForCharacteristic:c];
} } #pragma mark- 获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSLog(@"外设发送过来的数据:%@",characteristic.value.description );
} #pragma mark- 中心读取外设实时数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) { }
// Notification has started
if (characteristic.isNotifying) {
//读取外设数据
[peripheral readValueForCharacteristic:characteristic];
NSLog(@"%@",characteristic.value); } else { // Notification has stopped
// so disconnect from the peripheral
// NSLog(@"Notification stopped on %@. Disconnecting", characteristic); }
} #pragma mark - 7.给特征发送数据 //把数据写到哪个外设的哪个特征里面
- (void)writeValue:(NSData *)value toPeripheral:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic { //写入数据
[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
} @end

iOS蓝牙架构搭建-2的更多相关文章

  1. React Native IOS ---基础环境搭建(前端架构师)

    React Native -IOS 开发环境搭建 web架构(基础) 安装依赖 * 必须安装的依赖有:Node.Watchman 和 React Native 命令行工具以及 Xcode. npm 镜 ...

  2. iOS开发之谈谈App应用的架构搭建(推荐给大家看)

    1.iOS应用架构谈 开篇: 2.iOS应用架构谈 view层的组织和调用方案: 3.iOS应用架构谈 网络层设计方案: 4.iOS应用架构谈 本地持久化方案及动态部署: 5.iOS应用架构谈 组件化 ...

  3. iOS的架构

    根据多年的iOS开发经验,常用的iOS开发架构有:MVC.MVVM.CDD等,在这里我就不一一列举了. 做一个项目一般首先要搭建主流框架界面:常见的有TabBar控制器可以切换子控制器,上面又有Nav ...

  4. iOS应用架构谈(三):View层的组织和调用方案(下)

    iOS客户端应用架构看似简单,但实际上要考虑的事情不少.本文作者将以系列文章的形式来回答iOS应用架构中的种种问题,本文是其中的第二篇,主要讲View层的组织和调用方案.下篇主要讨论做View层架构的 ...

  5. iOS应用架构谈 view层的组织和调用方案

    当我们开始设计View层的架构时,往往是这个App还没有开始开发,或者这个App已经发过几个版本了,然后此时需要做非常彻底的重构. 一般也就是这两种时机会去做View层架构,基于这个时机的特殊性,我们 ...

  6. iOS应用架构现状分析

    iOS从2007年诞生至今已有近10年的历史,10年的时间对iOS技术圈来说足够产生相当可观的沉淀,尤其这几年的技术分享氛围无论国内国外都显得异常活跃.本文就iOS架构这一主题,结合开发圈里讨论较多的 ...

  7. (转)iOS应用架构谈 view层的组织和调用方案

    前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二 ...

  8. 用 VIPER 构建 iOS 应用架构(2)

    [编者按]本篇文章由 Jeff Gilbert 和 Conrad Stoll 共同编写,通过构建一个基础示例应用,深入了解 VIPER,并从视图.交互器等多个部件理清 VIPER 的整体布局及思路.通 ...

  9. 精华阅读第 9 期 |滴滴出行 iOS 客户端架构演进之路

    「架构都是演变出来的,没有最好的架构,只有最合适的架构!」最近,滴滴出行平台产品中心 iOS 技术负责人李贤辉接受了 infoQ 的采访,阐述了滴滴的 iOS 客户端架构模式与演变过程.李贤辉也是移动 ...

随机推荐

  1. SOAP Only Authentication Using C#

                Jason Lattimer's Blog                 Monday, February 9, 2015 SOAP Only Authentication ...

  2. 最大流EK算法

    给定一个有向图G=(V,E),把图中的边看作 管道,每条边上有一个权值,表示该管道 的流量上限.给定源点s和汇点t,现在假设 在s处有一个水源,t处有一个蓄水池,问从 s到t的最大水流量是多少? 网络 ...

  3. 管中窥Vue

    博客文章链接:管中窥Vue Vue和Angular.React.js的相同点和不同点? 与React的相同: 都使用了Virtual DOM 提供了响应式和组件化的视图组件 将注意力集中保持在核心库, ...

  4. java中设计模式详解

    一.设计模式的分类 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代理模式.外 ...

  5. POJ2406 Power Strings —— KMP or 后缀数组 最小循环节

    题目链接:https://vjudge.net/problem/POJ-2406 Power Strings Time Limit: 3000MS   Memory Limit: 65536K Tot ...

  6. Linux各类压宿包的解压方法

    01-.tar格式解包:[*******]$ tar xvf FileName.tar 打包:[*******]$ tar cvf FileName.tar DirName(注:tar是打包,不是压缩 ...

  7. TCP连接过程

    TCP建立连接与释放连接  最近复习准备<计算机网络>考试,感觉TCP协议建立连接与释放连接这两个过程比较重要,所以把自己理解的部分写下来. 1.建立连接:(三次握手)   (1)客户端发 ...

  8. swoole异步redis安装前置条件和流程

    ---恢复内容开始--- 1.redis服务 确认redis在服务器中已经安装了 2.hiredis库 第二步安装hiredis 下载位置 2.1获取 安装包https://github.com/re ...

  9. hdu-2157 How many ways??(矩阵快速幂)

    题目链接: How many ways?? Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 32768/32768 K (Java/O ...

  10. ZIP伪加密(deprecated)

    ZIP伪加密 经过伪加密的apk,改成zip格式打开会发现里面的文件都经过了加密. APK实际上是Zip压缩文件,但是Android系统在解析APK文件时,和传统的解压压缩软件在解析Zip文件时存在差 ...