iOS蓝牙架构搭建-2

蓝牙架构的搭建

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

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

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

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

React Native -IOS 开发环境搭建 web架构(基础) 安装依赖 * 必须安装的依赖有:Node.Watchman 和 React Native 命令行工具以及 Xcode. npm 镜像 npm config set registry https://registry.npm.taobao.org --global npm config set disturl https://npm.taobao.org/dist --global Yarn.React Native 的命令行工

ios系统架构及常用框架

1.iOS基于UNIX系统,因此从系统的稳定性上来说它要比其他操作系统的产品好很多 2.iOS的系统架构分为四层,由上到下一次为:可触摸层(Cocoa Touch layer).媒体层(Media layer).核心服务层(Core Services layer).核心操作系统层(Core OS layer)如图: (1) 触摸层:为应用程序开发提供了各种常用的框架并且大部分框架与界面有关,本质上来说它负责用户在iOS设备上的触摸交互操作.它包括以下这些组件: Multi-Touch Event

iOS的架构

根据多年的iOS开发经验,常用的iOS开发架构有:MVC.MVVM.CDD等,在这里我就不一一列举了. 做一个项目一般首先要搭建主流框架界面:常见的有TabBar控制器可以切换子控制器,上面又有Navigation导航条这个主流框架,还有一个就是用左右侧滑来控制自控制器的主流框架. 接下来就是搭建项目主框架 1.先搭建tabBarController(下面有一条) 2.再搭建NavigationController(上面有一条,并且每个子控制器的不一样) 思考开发方式 1.storyboard搭

iOS移动架构设计

---恢复内容开始--- 1.项目架构搭建 2.多媒体框架 iOS音频框架  iOS视频框架 聊天架构设计 3.推送架构设计 (推送原理实现服务器端.推送原理实现客户端) 4.DI架构 5.IOC架构 6.ORM数据库架构 7.Http网络请求框架 8.列表缓存架构 9.图片缓存架构 10.聊天架构设计 (客户端  服务器端) 11.推送架构设计  推送原理实现服务器端 推送原理实现客户端 ---恢复内容结束---

iOS移动架构设计FWZ

1.项目架构搭建 2.多媒体框架 iOS音频框架  iOS视频框架 聊天架构设计 3.推送架构设计 (推送原理实现服务器端.推送原理实现客户端) 4.DI架构 5.IOC架构 6.ORM数据库架构 7.Http网络请求框架 8.列表缓存架构 9.图片缓存架构 10.聊天架构设计 (客户端  服务器端) 11.推送架构设计  推送原理实现服务器端 推送原理实现客户端

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

前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二篇出来了. 当我们开始设计View层的架构时,往往是这个App还没有开始开发,或者这个App已经发过几个版本了,然后此时需要做非常彻底的重构. 一般也就是这两种时机会去做View层架构,基于这个时机的特殊性,我们在这时候必须清楚认识到:View层的架构一旦实现或定型,在App发版后可修改的余地就已经非常之小了

iOS应用架构谈-part2 view层的组织和调用方案

前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二篇出来了. 当我们开始设计View层的架构时,往往是这个App还没有开始开发,或者这个App已经发过几个版本了,然后此时需要做非常彻底的重构. 一般也就是这两种时机会去做View层架构,基于这个时机的特殊性,我们在这时候必须清楚认识到:View层的架构一旦实现或定型,在App发版后可修改的余地就已经非常之小了

iOS应用架构现状分析

iOS从2007年诞生至今已有近10年的历史,10年的时间对iOS技术圈来说足够产生相当可观的沉淀,尤其这几年的技术分享氛围无论国内国外都显得异常活跃.本文就iOS架构这一主题,结合开发圈里讨论较多的几种主流方式,再配以博主自己的理解,做下现状分析.给自己做下知识梳理的同时,也期望能引入新的思考. 架构的定义 过去6年多几乎绝大部分时间都浸淫在iOS平台,翻阅过不少关于架构的文章,发现众人对架构的理解颇有些差异,总体来说可分为四类: 第一类:精简型应用架构 这类架构的文章分析主要还是围绕MVC展

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

前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二篇出来了. 当我们开始设计View层的架构时,往往是这个App还没有开始开发,或者这个App已经发过几个版本了,然后此时需要做非常彻底的重构. 一般也就是这两种时机会去做View层架构,基于这个时机的特殊性,我们在这时候必须清楚认识到:View层的架构一旦实现或定型,在App发版后可修改的余地就已经非常之小了