IOS CoreBluetooth系列二:实战之本地 Central 和远程 Peripheral

前言:上文中我们主要是对CoreBluetooth一些术语和概念的介绍,接下来主要讲的是本地 Central 和远程 Peripheral。

大致步骤如下:

1、首先需要导入<CoreBluetooth/CoreBluetooth.h>这个框架,并在info配置Privacy - Bluetooth Peripheral Usage Description权限;

2、其次需要遵循CBCentralManagerDelegate,CBPeripheralDelegate这两个代理;

3、扫描外设;

3.1、懒加载创建中央设备;

- (CBCentralManager *)manager
{
    if (!_manager) {
        //1.创建中央设备
        _manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    }
    return _manager;
}

3.2、利用中央设备扫描外设

#pragma mark 利用中央设备扫描外设设备
- (void)scanForPeripheral
{
    NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
    //当没有指定对应的Services的时候,nil为全部
    [self.manager scanForPeripheralsWithServices:nil options:options];
}

3.3、如果发现外设会执行centralManager:didDiscoverPeripheral:advertisementData:RSSI:这个方法;

#pragma mark 发现外设
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSLog(@"查找设备peripheral is %@ name is %@",peripheral,peripheral.name);
    //1.保存扫描得到设备,判断如果数组中不包含当前扫描到得外部设置才保存
    NSMutableArray *uuidArray = [NSMutableArray array];
    for (CBPeripheral *p in self.peripherals) {
        [uuidArray addObject:p.identifier.UUIDString];
    }
    if (![uuidArray containsObject:peripheral.identifier.UUIDString]) {
        [self.peripherals addObject:peripheral];
        [self.tableView reloadData];
    }
}

4、连接外设;

4.1、这个时候我们可以选择一个外设,进行连接,需要执行connectPeripheral:options:这个方法;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.peripheral = self.peripherals[indexPath.row];
    self.peripheral.delegate = self;
    NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey];
    [self.manager connectPeripheral:self.peripheral options:options];
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

4.2、连接外设会执行CBCentralManagerDelegate这两个方法;

#pragma mark 连接外设成功调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"连接成功");
    //扫描外设中的服务,nil表示查找全部
    [peripheral discoverServices:nil];
}

#pragma mark 连接外设失败调用
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"连接失败");
}

5、连接上外设后,获取指定外设的服务(Service);

#pragma mark 只要扫描到就会调用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    NSLog(@"扫描服务");
    //获取外设中所有扫描到的服务
    if (!error) {
        NSArray *services = peripheral.services;
        for (CBService *service in services) {
            NSLog(@"service.UUID is %@",service.UUID.UUIDString);
            //判断service.UUID是否是我们需要的
            if ([service.UUID.UUIDString isEqualToString:KServiceUUID]) {
                //nil表示查找所有
                [peripheral discoverCharacteristics:nil forService:service];
            }
        }
    }else {
        NSLog(@"Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
    }
}
6、获取服务后,遍历服务的特征,得到可读、可写等特征;

#pragma mark 只要扫描到特征就会调用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (!error) {
        NSArray *characteristics = service.characteristics;
        for (CBCharacteristic *characteristic in characteristics) {
            NSLog(@"characteristic.UUID is %@",characteristic.UUID.UUIDString);
            if ([characteristic.UUID.UUIDString isEqualToString:KCharacteristicUUID]) {
                NSString *valueStr = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
                NSLog(@"valueStr is %@",valueStr);
                //保存特征
                self.characteristic = characteristic;
                //开启监听,为了保持连接
                [self.peripheral setNotifyValue:YES forCharacteristic:characteristic];
            }
        }
    }else {
        NSLog(@"Discovered read characteristics:%@ for service: %@", service.UUID, service.UUID);
    }
}
7、与中心管理者进行数据交互;

7.1、写入数据

#pragma mark 写入数据
- (void)writeString:(NSString *)string
{
    NSData *data = [NSData dataWithBytes:string.UTF8String length:string.length];
    self.peripheral.delegate = self;
    [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithoutResponse];
}
7.2、回调方法

#pragma mark 发送消息触发的方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    NSLog(@"发送消息回调 is %@",error);
}

#pragma mark 写描述信息时触发的方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error
{
    NSLog(@"描述信息回调 peripheral:%@,descriptor:%@,error:%@",peripheral,descriptor,error);
}

#pragma mark 有更新资料就会触发
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    NSLog(@"更新资料 peripheral:%@,characteristic:%@,error:%@",peripheral,characteristic,error);
    NSMutableData *recvData;
    [recvData appendData:characteristic.value];
    if (recvData.length >= 5) {
        unsigned char *buffer = (unsigned char *)[recvData bytes];
        int nLen = buffer[3]*256 + buffer[4];
        if (recvData.length == nLen + 3 + 2 + 2) {
            NSLog(@"recvData.length");
        }
    }
}
8、停止连接

- (void)stopScanForPeripheral
{
    //1.取消扫描
    [self.manager stopScan];
    
    //2.取消外设设备
    if (self.peripheral != nil) {
        [self.manager cancelPeripheralConnection:self.peripheral];
    }
    self.peripheral = nil;
    
    //3.刷新表格
    [self.tableView reloadData];
    
    //4.设置标题
    self.navigationItem.rightBarButtonItem.title = @"扫描";
}


demo详细代码如下:

#import <UIKit/UIKit.h>

@interface ViewController : UITableViewController

@end

#import "ViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>

static NSString *const KServiceUUID = @"9830BC16-5CE7-43CD-996F-74E9110C4CEA";
static NSString *const KCharacteristicUUID = @"F6F6B7E0-897D-4F51-959F-387A94BF440E";

@interface ViewController ()<UITableViewDelegate,UITableViewDataSource,CBCentralManagerDelegate,CBPeripheralDelegate>

//外设数组
@property (nonatomic,strong) NSMutableArray *peripherals;

//中心管理者
@property (nonatomic,strong) CBCentralManager *manager;

//外设
@property (nonatomic,strong) CBPeripheral *peripheral;

//特征
@property (nonatomic,strong) CBCharacteristic *characteristic;

@end

@implementation ViewController

//懒加载重写getter方法
- (NSMutableArray *)peripherals
{
    if (!_peripherals) {
        _peripherals = [NSMutableArray array];
    }
    return _peripherals;
}

- (CBCentralManager *)manager
{
    if (!_manager) {
        //1.创建中央设备
        _manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
    }
    return _manager;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1.设置导航Item
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"扫描" style:UIBarButtonItemStylePlain target:self action:@selector(openOrClosed)];
    
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"open" style:UIBarButtonItemStylePlain target:self action:@selector(openTheDoor)];
}

#pragma mark 扫描外设
- (void)openOrClosed
{
    if ([self.navigationItem.rightBarButtonItem.title isEqualToString:@"扫描"]) {
        self.navigationItem.rightBarButtonItem.title = @"断开";
        //开始扫描
        [self scanForPeripheral];
    } else {
        //停止扫描
        [self stopScanForPeripheral];
    }
}

#pragma mark 利用中央设备扫描外设设备
- (void)scanForPeripheral
{
    //当没有指定对应的Services的时候,nil为全部
    [self.manager scanForPeripheralsWithServices:nil options:nil];
}

#pragma mark 停止扫描
- (void)stopScanForPeripheral
{
    //1.取消扫描
    [self.manager stopScan];
    
    //2.取消外设设备
    if (self.peripheral != nil) {
        [self.manager cancelPeripheralConnection:self.peripheral];
    }
    self.peripheral = nil;
    
    //3.刷新表格
    [self.tableView reloadData];
    
    //4.设置标题
    self.navigationItem.rightBarButtonItem.title = @"扫描";
}

- (void)openTheDoor
{
    //拿到可读可写的特征
    [self writeString:@"OLWANDA_IL12345678"];
}

#pragma mark 写入数据
- (void)writeString:(NSString *)string
{
    NSData *data = [NSData dataWithBytes:string.UTF8String length:string.length];
    self.peripheral.delegate = self;
    [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithoutResponse];
}

#pragma mark - CBCentralManagerDelegate代理
#pragma mark 更新状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    NSLog(@"更新状态 is %ld",(long)central.state);
    switch (central.state) {
        case CBCentralManagerStatePoweredOn:{
            [self scanForPeripheral];
        }
            break;
        default:
            NSLog(@"Central Manager did change state");
            break;
    }
}

#pragma mark 发现外设
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSLog(@"查找设备peripheral is %@ name is %@",peripheral,peripheral.name);
    //1.保存扫描得到设备,判断如果数组中不包含当前扫描到得外部设置才保存
    NSMutableArray *uuidArray = [NSMutableArray array];
    for (CBPeripheral *p in self.peripherals) {
        [uuidArray addObject:p.identifier.UUIDString];
    }
    if (![uuidArray containsObject:peripheral.identifier.UUIDString]) {
        [self.peripherals addObject:peripheral];
        [self.tableView reloadData];
    }
}

#pragma mark 连接外设成功调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"连接成功");
    [self showTheAlertViewWithMessage:@"连接成功"];
    //扫描外设中的服务
    [peripheral discoverServices:nil];
}

#pragma mark 连接外设失败调用
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"连接失败");
}

- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)dict
{
    NSLog(@"dict is %@",dict);
}

- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals
{
    NSLog(@"peripherals is %@",peripherals);
}

#pragma mark - CBPeripheralDelegate代理
#pragma mark 只要扫描到就会调用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    NSLog(@"扫描服务");
    //获取外设中所有扫描到的服务
    if (!error) {
        NSArray *services = peripheral.services;
        for (CBService *service in services) {
            NSLog(@"service.UUID is %@",service.UUID.UUIDString);
            //判断service.UUID是否是我们需要的
            if ([service.UUID.UUIDString isEqualToString:KServiceUUID]) {
                //nil表示查找所有
                [peripheral discoverCharacteristics:nil forService:service];
            }
        }
    }else {
        NSLog(@"Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
    }
}

#pragma mark 只要扫描到特征就会调用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (!error) {
        NSArray *characteristics = service.characteristics;
        for (CBCharacteristic *characteristic in characteristics) {
            NSLog(@"characteristic.UUID is %@",characteristic.UUID.UUIDString);
            if ([characteristic.UUID.UUIDString isEqualToString:KCharacteristicUUID]) {
                NSString *valueStr = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
                NSLog(@"valueStr is %@",valueStr);
                //保存特征
                self.characteristic = characteristic;
                //开启监听,为了保持连接
                [self.peripheral setNotifyValue:YES forCharacteristic:characteristic];
            }
        }
    }else {
        NSLog(@"Discovered read characteristics:%@ for service: %@", service.UUID, service.UUID);
    }
}

#pragma mark 发送消息触发的方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    NSLog(@"发送消息回调 is %@",error);
}

#pragma mark 写描述信息时触发的方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error
{
    NSLog(@"描述信息回调 peripheral:%@,descriptor:%@,error:%@",peripheral,descriptor,error);
}

#pragma mark 有更新资料就会触发
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    NSLog(@"更新资料 peripheral:%@,characteristic:%@,error:%@",peripheral,characteristic,error);
    NSMutableData *recvData;
    [recvData appendData:characteristic.value];
    if (recvData.length >= 5) {
        unsigned char *buffer = (unsigned char *)[recvData bytes];
        int nLen = buffer[3]*256 + buffer[4];
        if (recvData.length == nLen + 3 + 2 + 2) {
            NSLog(@"recvData.length");
        }
    }
}

#pragma mark - TableView的代理和数据源
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _peripherals.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cell_id = @"cell_id";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cell_id];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cell_id];
    }
    
    CBPeripheral *peripheral = self.peripherals[indexPath.row];
    cell.textLabel.text = peripheral.name;
    cell.detailTextLabel.text = peripheral.identifier.UUIDString;
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.peripheral = self.peripherals[indexPath.row];
    self.peripheral.delegate = self;
    NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey];
    [self.manager connectPeripheral:self.peripheral options:options];
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

#pragma mark 弹窗
- (void)showTheAlertViewWithMessage:(NSString *)message
{
    UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"温馨提示" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
    [alertC addAction:okAction];
    [self presentViewController:alertC animated:YES completion:nil];
}

@end





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值