美文网首页程序员
CoreBluetooth框架使用

CoreBluetooth框架使用

作者: 4335151e8554 | 来源:发表于2018-01-19 17:22 被阅读0次

    蓝牙3.0协议由于高速度,通常用于音视频播放
    蓝牙4.0协议以低功耗著称,传输速度慢,一般用于控制蓝牙设备
    这里介绍iOS的CoreBluetooth框架,该框架支持蓝牙4.0协议,目前使用的非常多

    主要概念:

    中心设备:对应的类是CBCentralManager,中心设备就是手机,用于扫描和连接蓝牙
    周边设备:对应的类是CBPeripheral,周边设备就是蓝牙设备,用于数据的传输处理,当然了没有绝对的中心设备或者周边设备,他们是互通的,根据不同的需求,通常一个App中的中心设备和周边设备的功能是整合在一起的
    服务:对应类CBService,一个周边设备可以包含若干个服务,通过广播的形式供中心设备发现和使用。
    特征:对应类CBCharacteristic,一个服务中可以包含若干个特征,特征是我们发送或接受数据的地方
    UUID:对应类CBUUID,每个周边设备、服务和特征都有一个UUID来唯一标识自己。UUID有16bit、32bit和128bit三种,进行UUID比较时要将低位的UUID转换成高位的UUID再进行比较

    下面是使用这些类的一般步骤:
    1️⃣创建中心设备管理者

    中心设备管理者主要用于扫描和连接蓝牙设备,
    使用[[CBCentralManager alloc] initWithDelegate:self queue:nil]方法创建管理者,
    遵守对应的协议CBCentralManagerDelegate

    2️⃣使用中心管理者扫描蓝牙设备

    使用如下方法扫描蓝牙
    [self.centralManager scanForPeripheralsWithServices:nil options:nil];
    参数为nil表示扫描所有蓝牙,当扫描到蓝牙设备时会调用如下代理方法
    -(void)centralManager: didDiscoverPeripheral: advertisementData: RSSI:
    需要注意的是每扫描到一个蓝牙设备都会调用一次这个方法,RSSI是该蓝牙的信号强度,一般来说距离越远信号越弱,RSSI的值越接近0信号越强。通常会在这个方法中将信号较弱的蓝牙设备屏蔽掉,也可以在这个方法中根据蓝牙名称peripheral.name来过滤不想要的蓝牙设备
    将扫描到的蓝牙设备用数组保存起来,通过tableview展示出来

    3️⃣连接蓝牙

    点击tableview的某一行,连接该行对应的蓝牙设备,使用如下方法连接蓝牙
    [self.centralManager connectPeripheral:peripheral options:nil];
    连接成功后会调用如下代理方法
    -(void)centralManager: didConnectPeripheral:
    在代理方法中设置已连接的蓝牙设备的代理peripheral.delegate = self;
    遵守相关协议CBPeripheralDelegate,该协议和数据传输有关。
    在代理方法中还要做的一件事就是搜索服务,使用如下方法搜索服务
    [peripheral discoverServices:nil];

    4️⃣搜索服务

    在上一步我们已经搜索了服务,服务搜索到之后会调用如下代理方法
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:
    需要注意的是这个方法只调用一次,并且所有的服务通过peripheral.services来获取

    5️⃣搜索特征

    我们需要在上一步的代理方法中逐个的搜索所有服务中的所有特征,通过for循环来遍历吧
    for (CBService *service in peripheral.services) {
    [peripheral discoverCharacteristics:nil forService:service];
    }
    搜索到特征后会调用如下代理方法
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:
    需要注意的是有多少个服务这个方法就会调用多少次,服务中的特征通过如下方法获得
    service.characteristics

    6️⃣处理特征
    我们接收、读取和监听数据的所有操作都是在特征这一层来完成的,常用的特征有下面几个,特征的功能是公司定的,一切以实际为准,下面三个是常用的功能。

    1、用于读取数据的特征:有些特征是负责读取数据的,如果我们要主动读取一些数据,需要通过这个特征来读取数据,对应的读取方法如下
    [self.currentConnectPeripheral readValueForCharacteristic:ReadCharacteristic];
    读取到数据后会调用如下代理方法,
    -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:
    通过characteristic.value获取数据然后进行解析

    2、用于写入数据的特征:有些特征是负责写入操作的,比如我们要同步时间,需要将手机的时间发送到蓝牙设备,这个时候就需要用到这个特征,调用如下方法写入数据
    [self.currentConnectPeripheral writeValue:data forCharacteristic:WriteCharacteristic type:CBCharacteristicWriteWithResponse];
    写入成功后会调用如下代理方法
    -(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
    需要注意的是蓝牙4.0协议,每次发送或接收的最大数据长度为20个字节,如果数据过长则需要分包发送,接收的时候也是一样,如果命令过长则会分包接收命令,然后拼接命令

    3、用于监听数据的特征:有些特征是用于数据监听的,比如蓝牙设备会不定时的发送一些信息,如温度信息,由于不定时,所以读取数据的特征不太现实,只能用监听数据的特征,只要该特征中的数值发生变化就会触发如下代理方法
    -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:
    不过这个特征比较特殊,需要多一步操作,设置监听,使用如下方法设置监听
    [self.currentConnectPeripheral setNotifyValue:YES forCharacteristic:NotifyCharacter];

    以下是完整的蓝牙管理类代码
    .h

    #import <Foundation/Foundation.h>
    #import <CoreBluetooth/CoreBluetooth.h>
    
    #define BLEMANAGER [BLEManager manager]
    @protocol BLEManagerDelegate <NSObject>
    @optional
    //已经发现蓝牙设备
    -(void)BLE_didDiscoverPeripherals:(NSMutableArray<CBPeripheral *>*)peripherals;
    //已经连接到蓝牙设备
    -(void)BLE_didConnectPeripheral:(CBPeripheral*)peripherial;
    //连接错误
    -(void)BLE_connectError;
    //已经断开蓝牙外设
    -(void)BLE_didDisconnectPeripheral:(CBPeripheral*)peripheral;
    //已经扫描完毕
    -(void)BLE_didEndScan;
    @end
    
    @interface BLEManager : NSObject
    @property (nonatomic,weak) id<BLEManagerDelegate> delegate;
    //扫描到的蓝牙设备(通过了名字和信号强度rssi的筛选)
    @property (nonatomic,strong) NSMutableArray<CBPeripheral*> *discoveredPeripherals;
    
    //单粒方法
    + (instancetype)manager;
    //初始化中心管理者
    - (void)CL_initializeCentralManager;
    
    #pragma mark - d
    //蓝牙是否正在连接
    - (BOOL)CL_IsConnecttingPeripheral;
    //开始扫描外围设备 设置超时
    - (void)CL_StartScanDeviceWithTimeout:(NSTimeInterval)timeout;
    //开始连接指定的蓝牙设备
    - (void)CL_StartConnectPeripheral:(CBPeripheral *)peripheral;
    //断开当前连接的蓝牙设备
    - (void)CL_DisconnectPeripheral:(CBPeripheral*)peripheral;
    //写入数据
    -(void)CL_writeValue:(int)serviceUUID characteristicUUID:(int)characteristicUUID data:(NSData *)data
    
    @end
    

    .m

    #import "BLEManager.h"
    #import <CoreBluetooth/CoreBluetooth.h>
    #define RSSI_blueTooth 80    //可接受的外围蓝牙信号强度最低值 rssi通常为负数 只要大于-50 蓝牙信号强度就可接受
    typedef struct _CHAR{
        char buff[1000];
    }CHAR_STRUCT;
    
    @interface BLEManager()<CBCentralManagerDelegate,CBPeripheralDelegate>
    //蓝牙中心管理者
    @property (nonatomic,strong) CBCentralManager *centralManager;
    //扫描状态
    @property (nonatomic,assign) BOOL isScan;
    //扫描超时定时器
    @property (nonatomic,strong) NSTimer *timeoutTimer;
    //准备连接的蓝牙外设
    @property (nonatomic,strong) CBPeripheral *prepareConnectPeripheral;
    //当前已经连接的蓝牙外设
    @property (nonatomic,strong) CBPeripheral *currentConnectPeripheral;
    //蓝牙的连接状态 是否正在连接
    @property (nonatomic,assign) BOOL isConnecttingBluetooth;
    
    //特征值
    @property (nonatomic,strong) CBCharacteristic *writeCharacteristic;  //写入通道
    @property (nonatomic,strong) CBCharacteristic *readCharacteristic;   //读取通道
    @property (nonatomic,strong) CBCharacteristic *notifyCharacteristic; //监听通道
    
    //蓝牙返回数据 或命令
    @property (nonatomic,strong) NSMutableArray *deviceCallBack;
    @end
    
    @implementation BLEManager
    #pragma mark - 初始化管理者
    //单粒方法
    + (instancetype)manager
    {
        static BLEManager *sharedInstance = nil;
        static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            sharedInstance = [[self alloc] init];
        });
        return sharedInstance;
    }
    
    //初始化中心管理者  只需在appdelegate中初始化一次即可
    - (void)CL_initializeCentralManager
    {
        self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
        self.isScan = NO;
        self.isConnecttingBluetooth = NO;
        self.discoveredPeripherals = [NSMutableArray array];
        self.deviceCallBack = [NSMutableArray array];
    }
    
    #pragma mark - 蓝牙的扫描和连接
    //蓝牙是否正在连接
    - (BOOL)CL_IsConnecttingPeripheral{
        return self.isConnecttingBluetooth;
    }
    
    //开始扫描外围设备 设置超时
    - (void)CL_StartScanDeviceWithTimeout:(NSTimeInterval)timeout
    {
        //设置默认超时时间
        if (timeout < 0 || timeout > 60) {
            timeout = 5;
        }
        if (self.centralManager.state != CBManagerStatePoweredOn) {
            NSLog(@"手机蓝牙未开启!");
            return;
        }
        //停止上一次的定时器
        [self destroyTimeoutTimer];
        //扫描前清空之前的设备记录
        [self.discoveredPeripherals removeAllObjects];
        //再重新开启一个定时器
        if (self.timeoutTimer == nil) {
            self.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:@selector(timeout) userInfo:nil repeats:NO];
        }
        [self.centralManager stopScan];
        //开始扫描之前一定要判断centralManager的state是否为CBManagerStatePoweredOn 否则将不会扫描
        [self.centralManager scanForPeripheralsWithServices:nil options:nil];
        self.isScan = YES;
    }
    
    //扫描超时回调
    -(void)timeout{
        [self destroyTimeoutTimer];
        [self.centralManager stopScan];
        self.isScan = NO;
        if ([self.delegate respondsToSelector:@selector(BLE_didEndScan)])
            [self.delegate BLE_didEndScan];
    }
    
    //销毁定时器
    -(void)destroyTimeoutTimer
    {
        [self.timeoutTimer invalidate];
        self.timeoutTimer = nil;
    }
    
    //开始连接指定的蓝牙外设
    -(void)CL_StartConnectPeripheral:(CBPeripheral *)peripheral
    {
        if (self.centralManager.state != CBManagerStatePoweredOn) {
            NSLog(@"手机蓝牙未开启!");
            return;
        }
        peripheral.delegate = self;   //设置外设代理
        if (peripheral) {
            self.prepareConnectPeripheral = peripheral;
            peripheral.delegate = self;
            [self.centralManager connectPeripheral:peripheral options:nil];
        }
    }
    
    //断开当前连接的蓝牙
    -(void)CL_DisconnectPeripheral:(CBPeripheral*)peripheral{
        [self.centralManager cancelPeripheralConnection:peripheral];
    }
    
    #pragma mark - 数据写入和读取
    /**
     *  通用写入数据,发送给外设
     */
    -(void)CL_writeValue:(int)serviceUUID characteristicUUID:(int)characteristicUUID data:(NSData *)data{
        
        CBUUID *su = [self UUIDWithNumber:serviceUUID];      //获得serviceUUID         服务uuid
        CBUUID *cu = [self UUIDWithNumber:characteristicUUID];      //获得characteristicUUID  特征uuid
        CBService *service = [self searchServiceWithUUID:su];        //搜索对应的服务
        if (service) {
            CBCharacteristic *characteristic = [self searchCharacteristicWithUUID:cu andService:service];
            if (characteristic) {
                [self.currentConnectPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
            }
        }
    }
    
    // 通用从外设读取数据
    -(void)CL_readValue:(int)serviceUUID characteristicUUID:(int)characteristicUUID{
        
        CBUUID *su = [self UUIDWithNumber:serviceUUID];             //获得serviceUUID         服务uuid
        CBUUID *cu = [self UUIDWithNumber:characteristicUUID];      //获得characteristicUUID  特征uuid
        CBService *service = [self searchServiceWithUUID:su];        //搜索对应的服务
        if (service) {
            CBCharacteristic *characteristic = [self searchCharacteristicWithUUID:cu andService:service];
            if (characteristic) {
                [self.currentConnectPeripheral readValueForCharacteristic:characteristic];    
            }
        }
    }
    
    // 通用该外设注册通知状态是否活跃   根据isActive值 开启或者关闭对某个特征值的监听
    -(void)CL_notification:(int)serviceUUID characteristicUUID:(int)characteristicUUID isActive:(BOOL)isActive{
     
        CBUUID *su = [self UUIDWithNumber:serviceUUID];      //获得serviceUUID         服务uuid
        CBUUID *cu = [self UUIDWithNumber:characteristicUUID];      //获得characteristicUUID  特征uuid
        CBService *service = [self searchServiceWithUUID:su];
        if (service) {
            CBCharacteristic *characteristic = [self searchCharacteristicWithUUID:cu andService:service];
            if (characteristic) {
                //设置特征值变化的通知
                [self.currentConnectPeripheral setNotifyValue:isActive forCharacteristic:characteristic];
            }
        }
    }
    
    //在服务中搜索特征
    -(CBCharacteristic *)searchCharacteristicWithUUID:(CBUUID *)cu andService:(CBService *)service
    {
        CBCharacteristic *characteristic = nil;   //在服务中搜索特征
        for(int i=0; i < service.characteristics.count; i++) {
            CBCharacteristic *c = [service.characteristics objectAtIndex:i];
            if ([c.UUID isEqual:cu]) {
                characteristic = c;
                break;
            }
        }
        return characteristic;
    }
    
    //搜索服务
    -(CBService *)searchServiceWithUUID:(CBUUID *)su
    {
        CBService *service = nil;   //搜索对应的服务
        for(int i = 0; i < self.currentConnectPeripheral.services.count; i++) {
            CBService *s = [self.currentConnectPeripheral.services objectAtIndex:i];
            if ([s.UUID isEqual:su]) {
                service = s;
                break;
            }
        }
        return service;
    }
    
    -(CBUUID *)UUIDWithNumber:(int)number
    {
        UInt16 s = [self _swap:number];
        NSData *sd = [[NSData alloc] initWithBytes:(char *)&s length:2];
        return [CBUUID UUIDWithData:sd];
    }
    
    - (UInt16)_swap:(UInt16) s{
        UInt16 temp = s << 8;
        temp |= (s >> 8);      //表示 temp = temp | (s >> 8)  按位或
        return temp;
    }
    
    //十六进制的字符串To String
    - (NSString *)stringFromHexString:(NSString *)hexString {
        if (([hexString length] % 2) != 0)
            return nil;
        
        NSMutableString *string = [NSMutableString string];
        
        for (NSInteger i = 0; i < [hexString length]; i += 2) {
            
            NSString *hex = [hexString substringWithRange:NSMakeRange(i, 2)];
            NSInteger decimalValue = 0;
            sscanf([hex UTF8String], "%lx", &decimalValue);
            [string appendFormat:@"%ld", (long)decimalValue];
        }
        return string;
    }
    
    #pragma mark - CBCentralManagerDelegate 中心设备代理方法
    /**
     *  中心管理者更新状态
     */
    - (void)centralManagerDidUpdateState:(CBCentralManager *)central
    {
        if (central.state == CBManagerStatePoweredOff) {  
            self.isConnecttingBluetooth = NO;
            self.currentConnectPeripheral = nil;   
            if ([self.delegate respondsToSelector:@selector(BLE_didDisconnectPeripheral:)])
                [self.delegate BLE_didDisconnectPeripheral:self.currentConnectPeripheral];
            NSLog(@"断开连接");
        }
    }
    
    //中心设备发现外围设备的回调
    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI
    {
        if (peripheral.name == nil) {
            return;
        }
        
        //按名字和信号强度筛选蓝牙设备
    if ([peripheral.name containsString:@"BLE"]) {
            if (RSSI.intValue > -RSSI_blueTooth) {
    //            NSLog(@"扫描到的设备 = %@",peripheral);
                BOOL isRepetitive = NO;   //重复标记
                for (CBPeripheral *peripher in self.discoveredPeripherals){    //查重
                    if ([peripheral.identifier.UUIDString isEqualToString:peripher.identifier.UUIDString])
                        isRepetitive = YES;   //有重复的
                }
                if (!isRepetitive)
                    [self.discoveredPeripherals addObject:peripheral];
            }
    }
        //通知代理
        if ([self.delegate respondsToSelector:@selector(BLE_didDiscoverPeripherals:)])
            [self.delegate BLE_didDiscoverPeripherals:self.discoveredPeripherals];
    }
    
    //中心设备成功连接外围设备
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
    {
        self.isConnecttingBluetooth = YES;
        self.currentConnectPeripheral = peripheral;
        //搜索服务
        [self.currentConnectPeripheral discoverServices:nil];
        //通知代理
        if ([self.delegate respondsToSelector:@selector(BLE_didConnectPeripheral:)])
            [self.delegate BLE_didConnectPeripheral:peripheral];
        NSLog(@"已连接的设备%@",peripheral); 
    }
    
    //连接失败
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
        if ([self.delegate respondsToSelector:@selector(BLE_connectError)])
            [self.delegate BLE_connectError];
    }
    
    //设备断开连接
    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
    {
        self.isConnecttingBluetooth = NO;
        self.currentConnectPeripheral = nil;
        //通知代理
        if ([self.delegate respondsToSelector:@selector(BLE_didDisconnectPeripheral:)])
            [self.delegate BLE_didDisconnectPeripheral:peripheral];
        NSLog(@"断开的设备%@",peripheral);
    }
    
    
    #pragma mark - CBPeripheralDelegate 蓝牙外设代理
    //发现蓝牙的服务
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error
    {
        if (error) {
            return;
        }
        NSLog(@"发现服务 = %@ count = %lu",peripheral.services,peripheral.services.count);
        //去发现所有服务中的所有特征
        for (CBService *service in peripheral.services) {
            [peripheral discoverCharacteristics:nil forService:service];
        }
    }
    
    
    //发现服务中的特征
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
    {
        if (error) {
            return;
        }
    //公司不同 规定的特征的UUID可能也不同,
        for (CBCharacteristic *character in service.characteristics) {
            NSLog(@"发现服务中的特征%@",character);
            // 写入数据的特征值 0xXXXX
            if ([character.UUID isEqual:[CBUUID UUIDWithString:@"0000XXXX-0000-1000-8000-00805F9B34FB"]]) {
                self.writeCharacteristic = character;
                continue;
            }
            // 读取数据的特征值 0xXXXX
            if ([character.UUID isEqual:[CBUUID UUIDWithString:@"0000XXXX-0000-1000-8000-00805F9B34FB"]] ) {
                self.readCharacteristic = character;
                [self CL_readValue:0xXXXX characteristicUUID:0xXXXX];
                continue;
            }
            // 注册监听通道的特征值 0xXXXX
            if ([character.UUID isEqual:[CBUUID UUIDWithString:@"0000XXXX-0000-1000-8000-00805F9B34FB"]] ) {
                self.notifyCharacteristic = character;
                [self.currentConnectPeripheral setNotifyValue:YES forCharacteristic:character];    //监听通道开启监听
                continue;
            }
        }
    }
    
    //特征值更新
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
    {
        if (error) {
            NSLog(@"特征值更新错误%@",error);
            return;
        }
        //处理特征值,解析数据,公司不同解析规则也不同
        NSData *receiveData = characteristic.value;
        Byte *byte = (Byte *)[receiveData bytes];         
        [self.deviceCallBack removeAllObjects];
        [self.deviceCallBack addObject:receiveData];
            
        if (self.delegate && [self.delegaterespondsToSelector:@selector(getValueForPeripheral)]) {
            [self.delegate getValueForPeripheral];
        }
    }
    
    //特征值写入(修改)成功
    - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
    {
        if (error) {
            NSLog(@"写入失败%@",error);
        }else{
            NSLog(@"写入成功");
        }
    }
    
    //特征值监听状态改变
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
    {
        NSLog(@"特征监听状态改变 = %@",characteristic);
    }
    
    @end
    

    相关文章

      网友评论

        本文标题:CoreBluetooth框架使用

        本文链接:https://www.haomeiwen.com/subject/kxgooxtx.html