蓝牙架构的搭建

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

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

    • 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

最新文章

  1. 修改Firebug字体
  2. 数据库SQL语句练习题
  3. 如何创建vss2005的数据库
  4. iOS平台XML解析类库对比和安装说明
  5. Cassandra安装及其简单试用
  6. IT职场生存法则
  7. GDB 多进程调试
  8. UNICODE与ANSI的区别
  9. 王者荣耀_KEY
  10. 【YFMemoryLeakDetector】人人都能理解的 iOS 内存泄露检测工具类
  11. JSP/JSF从web.xml中取出context-param的配置信息
  12. Unity读Excel 输出PC端(Windows)后不能读取的问题
  13. Spring boot 入门配置
  14. Linux内核Inotify机制学习笔记
  15. yum 安装指定 kernel 版本源码
  16. logger示例
  17. Android:不同drawable文件夹的区别
  18. LA 4015 树形背包
  19. json字符串转为json对象-jQuery.parseJSON()
  20. {ubuntu}乱七八糟重命名为1 2 3.....png

热门文章

  1. PPTP&amp;L2TP&amp;PPPOE client and server configure
  2. PAT 天梯赛 L2-008. 最长对称子串 【字符串】
  3. php不使用递归实现无限极分类
  4. HDU2512 一卡通大冒险 —— 第二类斯特林数
  5. codeforces 467B Fedor and New Game 解题报告
  6. 提高scroll性能
  7. 使用media来加载css
  8. C++类定义 常量定义
  9. 将linux系统用户导入mysql表
  10. 前端开发 --- CSS参考手册