程序员

CoreBluetooth框架使用

2018-01-19  本文已影响0人  4335151e8554

蓝牙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
上一篇下一篇

猜你喜欢

热点阅读