蓝牙架构的搭建

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

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

    • 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. s:text

    <s:text>是Struts2用来显示资源文件中信息或格式化数据时使用的,一般要配合<s:i18n>标签.

  2. 使用eclipse的SVN连接码云

    码云配置: 码云的项目上,启用SVN访问 eclipse的配置,不配置这个会报错

  3. zkui部署

    1.拉取代码 #git clone https://github.com/DeemOpen/zkui.git 2.构建并安装程序 #cd zkui/ #yum install -y maven #mv ...

  4. 为 Android 平台开发一个输入法

    学习目标: 实现新的输入法 学习目的: 掌握Android输入法框架 学习收获: Android 1.5 新特色之一就是输入法框架(Input Method Framework,IMF),正是它的出现 ...

  5. hdu-5754 Life Winner Bo(博弈)

    题目链接: Life Winner Bo Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 131072/131072 K (Java/ ...

  6. Linux下C语音实现socket发送和接收的小程序

    1.什么是socket套接字 socket其实就是计算机通信的端口,可以实现两个计算机之间的通信的一个接口,应用程序在网络上传输就是通过这个借口实现. socket分为三种类型: 字节流套接字(Str ...

  7. Swift引用计数器

    ARC概述 和4.2+版本的Xcode对OC的支持一样,Swift也是使用ARC来管理内存,文档是这么描述的: Swift uses Automatic Reference Counting(ARC) ...

  8. HDU2844(多重部分和)

    Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  9. POJ3624(01背包:滚动 实现)

    Charm Bracelet Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 30417   Accepted: 13576 ...

  10. POJ2752(next原理理解)

    Seek the Name, Seek the Fame Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 15536   Ac ...