• 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
  • 相关阅读:
    每日站立会议08
    MAVEN常用命令
    android开发环境搭建
    阻止默认事件和阻止事件冒泡
    C#ActiveX安装项目
    System&Software
    poj 1386 Play on Words 有向欧拉回路
    poj 1033
    120.Triangle
    pandas中 transform 函数和 apply 函数的区别
  • 原文地址:https://www.cnblogs.com/dujiahong/p/7269696.html
Copyright © 2020-2023  润新知