iOS蓝牙架构搭建-2

Posted 宁静暖风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 





以上是关于iOS蓝牙架构搭建-2的主要内容,如果未能解决你的问题,请参考以下文章

iOS 应用架构实现 MVVM、网络和蓝牙,如何实现?

安信可PB-03蓝牙模组专题 第一篇:SDK二次开发入门环境搭建。

Android蓝牙系统框架和代码架构

Qt低功耗蓝牙系列四(搭建低功耗服务端代码)

Qt低功耗蓝牙系列四(搭建低功耗服务端代码)

Qt低功耗蓝牙系列四(搭建低功耗服务端代码)