美文网首页
蓝牙开发-Central开发

蓝牙开发-Central开发

作者: 涛涛涛涛涛涛涛涛涛 | 来源:发表于2018-11-08 11:38 被阅读0次

必须要了解的

1、CBCentralManager 蓝牙中心管理器(作为中心开发使用)
CBPeripheral 蓝牙外设
CBService 蓝牙服务(一个外设有多个服务)
CBCharacteristic 蓝牙服务特性(一个服务有多个特性)
CBUUID 唯一标识符(服务和特性都有)

2、中心和外设是一对多的关系,中心可以连接多个外设,但外设只能被一个中心连接。

3、每个服务和特性都有一个CBUUID,如果项目需要监听或写入多个特性,那么开发中需要对服务和特性的CBUUID进行记录,用于判断消息是从哪个特性传过来的。反之则不太需要关注CBUUID参数。

4、如果不想搜索出无关的蓝牙设备,则需要在搜索外设时传入服务和特性的CBUUID数组进行筛选。

1、导入头文件和协议

#import <CoreBluetooth/CoreBluetooth.h>
@interface WWTBlueToothModule()<CBCentralManagerDelegate,CBPeripheralDelegate>

2、实例化CentralManager

-(instancetype)init
{
    instance = [super init];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        
        NSDictionary * options = @{CBCentralManagerOptionShowPowerAlertKey:@YES};
        _BT_CenterManager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue() options:options];
        _maxLength = MAXLENGTH;//设置数据写入的默认最大长度
        
    });
    
    return instance;
}

3、判断蓝牙状态

/*
typedef NS_ENUM(NSInteger, CBManagerState) {
    CBManagerStateUnknown = 0,//未知状态
    CBManagerStateResetting,//蓝牙正在重置
    CBManagerStateUnsupported,//不支持蓝牙
    CBManagerStateUnauthorized,//未授权
    CBManagerStatePoweredOff,//蓝牙未开启
    CBManagerStatePoweredOn,//蓝牙已开启
} NS_ENUM_AVAILABLE(10_13, 10_0);
 */

/*
 *获取Central状态
 */
-(CBManagerState)GetCentralManagerState
{
    return _BT_CenterManager.state;
}

/*
 *Central状态更新时触发
 *在CentralManager创建时会自动调用该方法
 */
-(void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    [[NSNotificationCenter defaultCenter] postNotificationName:kCentralManagerStateUpdateNoticiation object:@{@"central":central}];
}

4、若蓝牙状态为CBManagerStatePoweredOn,则开始搜索外设。UUIDs为服务UUID的集合,传入后只会搜索出带有这些服务的外设,传nil则返回全部外设。

//这里只给出了关键代码,需要根据自身的代码选择调用时机
[_BT_CenterManager scanForPeripheralsWithServices:uuids options:options];

5、搜索到外设时会进入CBCentralManagerDelegate的代理方法didDiscoverPeripheral

每当搜索到一个外设时会进入didDiscoverPeripheral方法,要注意会重复搜索出同一个外设,所以在加入外设数组时,需要先判断是否已经存在数组中了。

//RSSI--信号量
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    
}

6、连接外设

//这里只给出了关键代码,需要根据自身的代码选择调用时机
[_BT_CenterManager connectPeripheral:peripheral options:connectOptions];

7、连接成功后会进入代理方法didConnectPeripheral

//改变当前蓝牙连接状态 _connectStatus = BETConnecting;
//停止搜索[_BT_CenterManager stopScan];
//查看外设的全部服务peripheral.services
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
   
}

8、若连接失败,则进入didFailToConnectPeripheral

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
   
}

9、连接成功后设置外设peripheral代理

peripheral.delegate = self;

10、根据serviceUUIDs搜索服务

//serviceUUIDs为nil时搜索全部服务
[_BT_Peripheral discoverServices:_serviceUUIDs];

11、搜索到服务会触发peripheral的代理方法didDiscoverServices

/*
 *发现服务触发
 *服务在peripheral.services里面
 */
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
   
}

12、根据搜索到的服务继续搜索服务的特性

[_BT_Peripheral discoverCharacteristics:_characteristicUUIDs forService:service];

13、发现特性后会触发peripheral的代理方法didDiscoverCharacteristicsForService

//特性在service.characteristics里面
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
{
    
}

14、接下来有几个分支

(1)搜索特性描述

 [_BT_Peripheral discoverDescriptorsForCharacteristic:character];

搜索到后会进入didDiscoverDescriptorsForCharacteristic方法

/*
 *描述在characteristic.descriptors里面
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{

}

读取特性描述

[_BT_Peripheral readValueForDescriptor:descriptor];

从didUpdateValueForDescriptor获取描述的值

/*
 *获取描述会触发
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error
{
    if (error) {
        if (_readValueForDescriptorBlock) {
            _readValueForDescriptorBlock(descriptor,nil,error);
        }
        return;
    }
    
    NSData *data = descriptor.value;
    if (_readValueForDescriptorBlock) {
        _readValueForDescriptorBlock(descriptor,data,nil);
    }
}

(2)获取特性的值

[_BT_Peripheral readValueForCharacteristic:character];

触发didUpdateValueForCharacteristic读取特性的值,这里注意需要注意订阅读取特性值都是从这个方法返回的

// 读取特性中的值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error) {
        if (_readValueForCharacteristicBlock) {
            _readValueForCharacteristicBlock(characteristic,nil,error);
        }
        return;
    }
    
    
    NSData *data = characteristic.value;
    
    if (_readValueForCharacteristicBlock) {
        _readValueForCharacteristicBlock(characteristic,data,nil);
    }
}

(3)订阅特性

[_BT_Peripheral setNotifyValue:YES forCharacteristic:character];

需要在didUpdateNotificationStateForCharacteristic中判断订阅是否成功

/*
 *特性订阅状态回调
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    if (error) {
        if (_notifyCharacteristicBlock) {
            _notifyCharacteristicBlock(peripheral,characteristic,error);
        }
        return;
    }
    if (_notifyCharacteristicBlock) {
        _notifyCharacteristicBlock(peripheral,characteristic,nil);
    }
}

最后当订阅的特征的值更新时触发didUpdateValueForCharacteristic方法

(4)向特性写值

[_BT_Peripheral writeValue:Data forCharacteristic:characteristic type:type];

这里要注意特性允许单次写入的最大长度!!
iOS 9.0以上可以使用maximumWriteValueLengthForType方法进行查询最大写入长度

//往特性写入数据(传输数据)
-(void)WriteValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic andType:(CBCharacteristicWriteType)type
{
    _writeCount = 0;
    _responseCount = 0;
    if ([_BT_Peripheral respondsToSelector:@selector(maximumWriteValueLengthForType:)]) {
        _maxLength = [_BT_Peripheral maximumWriteValueLengthForType:type];
    }
    if (_maxLength <= 0) {
        [_BT_Peripheral writeValue:data forCharacteristic:characteristic type:type];
        _writeCount ++;
        return;
    }
    
    if (data.length <= _maxLength) {
        [_BT_Peripheral writeValue:data forCharacteristic:characteristic type:type];
        _writeCount ++;
    }
    else
    {
        NSInteger Dataindex = 0;
        for (Dataindex = 0; Dataindex < data.length - _maxLength; Dataindex += _maxLength) {
            NSData * pieceOfData = [data subdataWithRange:NSMakeRange(Dataindex, _maxLength)];
            [_BT_Peripheral writeValue:pieceOfData forCharacteristic:characteristic type:type];
            _writeCount ++;
        }
        NSData * leftData = [data subdataWithRange:NSMakeRange(Dataindex, data.length - Dataindex)];
        if (leftData) {
            [_BT_Peripheral writeValue:leftData forCharacteristic:characteristic type:type];
            _writeCount ++;
        }
    }
}

写入后会触发didWriteValueForCharacteristic,在这里判断数据是否写入成功

/*
 *向特性写入值后触发
 *根据error判断是否成功
 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    if (!_writeValueForCharacteristicBlock) {
        return;
    }
    
    _responseCount ++;
    if (_writeCount != _responseCount) {
        return;
    }
    
    _writeValueForCharacteristicBlock(characteristic,error);
}

(5)向特性的描述写值

[_BT_Peripheral writeValue:value forDescriptor:descriptor];

写入后会触发didWriteValueForCharacteristic,在这里判断数据是否写入成功

/*
 *写入描述后触发
 *根据error判断是否成功
 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error
{
    if (error) {
        if (_writeValueForDescriptorBlock) {
            _writeValueForDescriptorBlock(descriptor, error);
        }
        return;
    }
    
    if (_writeValueForDescriptorBlock) {
        _writeValueForDescriptorBlock(descriptor, nil);
    }
}

15、断开连接

[_BT_CenterManager cancelPeripheralConnection:_BT_Peripheral];

主动或被动断开连接都会触发didDisconnectPeripheral

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    _BT_Peripheral = nil;
    _connectStatus = BETDisconnect;
    
    NSLog(@"断开连接");
}

最后说一些问题

1、上面代码也有说到,当蓝牙连接成功后就停止搜索外设,但是如果用户点击进入蓝牙列表界面,但是没连接蓝牙又POP出去了,那么蓝牙是一直处于搜索状态的。所以需要在列表界面viewWillDisappear的时候StopScan。

-(void)viewWillDisappear:(BOOL)animated
{
    [[WWTBlueToothModule ShareInstance]StopScan];
    [super viewWillDisappear:animated];
}

2、似乎第一次搜索外设是必须要在centralManagerDidUpdateState方法中进行的,这个不太确定,反正我这么写了。另外把搜索方法写在了这里还是不够的,因为centralManagerDidUpdateState只会在蓝牙状态改变时和创建CentralManager时调用,将该模块写成单例模式时,第二次进入外设列表界面需要手动Scan。

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear: animated];
    //获取当前设备蓝牙状态
    if ([BET_ShareInstance GetCentralManagerState] == CBManagerStatePoweredOn) {
        //若设备蓝牙状态为可用,则开始扫描
        [BET_ShareInstance ScanForPeripheralWithServiceUUIDs:_ServiceUUIDs options:nil];
    }
    else
    {
        //提示检查设备蓝牙
    }
}

相关文章

网友评论

      本文标题:蓝牙开发-Central开发

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