OC 原生蓝牙框架CoreBlueTooth

今天心血来潮,想学一下蓝牙开发,然后搜了一下,找了相关的一些文章看了看,大概有了一些了解,在这里做一个记录.

蓝牙模式分为两类,一种是中心模式,一种是外设模式;

基本概念:

  • BLE:(蓝牙低能量)蓝牙4.0设备因为低耗电,也叫BLE
  • 周边,中央:外设和中心设备,发起链接的是中央(一般是指手机),被链接的设备是外围设备(运动手环)
  • service and characteristic:(服务和特征)每个设备会提供服务和特征,类似于服务端的API,但是结构不同。每个设备会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为读(read),写(write),通知(notify)几种,就是我们连接设备后具体需要操作的内容
  • Description:每个characteristic可以对应一个或者多个Description用于描述characteristic的信息或属性(eg.范围,计量单位)

蓝牙的基本流程

BLE中心模式流程

  1. 建立中心角色
  2. 扫描外设(Discover Peripheral)
  3. 连接外设(Connect Peripheral)
  4. 扫描外设中的服务和特征(Discover Services And Characteristics)
    4.1 获取外设的services
    4.2 获取外设的Characteristics,获取characteristics的值,,获取Characteristics的Descriptor和Descriptor的值
  5. 利用特征与外设做数据交互(Explore And Interact)
  6. 订阅Characteristic的通知
  7. 断开连接(Disconnect)

BLE外设模式流程

  1. 启动一个Peripheral管理对象
  2. 本地peripheral设置服务,特征,描述,权限等等
  3. peripheral发送广告
  4. 设置处理订阅,取消订阅,读characteristic,写characteristic的代理方法

蓝牙设备的状态

  1. 待机状态(standby):设备没有传输和发送数据,并且没有连接到任何外设
  2. 广播状态(Advertiser):周期性广播状态
  3. 扫描状态(Scanner):主动搜索正在广播的设备
  4. 发起链接状态(Initiator):主动向扫描设备发起连接
  5. 主设备(Master):作为主设备连接到其它设备.
  6. 从设备(Slave):作为从设备链接到其它设备

蓝牙设备的五种工作状态

  • 准备(Standby)
  • 广播(Advertising)
  • 监听扫描(Scanning)
  • 发起连接(Initiating)
  • 已连接(Connected)

蓝牙和版本使用限制

  • 蓝牙2.0:越狱设备
  • BLE:iOS6以上
  • MFI认证设备:无限制

BLE测试

  1. 两台BLE设备
  2. 如何让iOS模拟器也能测试BLE?
  3. 买一个CSR蓝牙4.0 USB适配器,插在Mac上
  4. 在终端输入sudo nvram bluetoothHostControllerSwitchBehavior="never"
  5. 重启Mac
  6. 用Xcode4.6调试代码,将程序跑在iOS6.1模拟器上
  7. 苹果把iOS7.0模拟器对BLE的支持移除了

(以上内容来自博客CoreBluetooth:baseK,中心模式,外设模式流程,iBeacon)

在iOS中,现在提供了一个CoreBlueTooth库,来操作蓝牙。相应代码如下

@interface ViewController ()

@property (strong, nonatomic) UIButton *scanButton;

@property (strong, nonatomic) CBCentralManager *cbCentralManager;
@property (strong, nonatomic) CBPeripheral *peripheral;

@end

@implementation ViewController

#pragma mark ====================    懒加载    ====================
- (UIButton *)scanButton
{
    if (!_scanButton)
    {
        _scanButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [_scanButton addTarget:self action:@selector(scanButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
        [_scanButton setTitle:@"立即扫描" forState:UIControlStateNormal];
        [_scanButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    }
    return _scanButton;
}



#pragma mark ====================    viewDidLoad    ====================
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self setupUI];
}


- (void)setupUI
{
    [self.view addSubview:self.scanButton];
    [self.scanButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(20);
        make.centerX.equalTo(self.view.mas_centerX);
        make.height.mas_equalTo(40);
        make.width.mas_equalTo(200);
    }];
}




#pragma mark ====================    点击事件    ====================
- (void)scanButtonClickAction:(UIButton *)button
{
    _cbCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}




#pragma mark ====================    CBCentralManagerDelegate    ====================
//当蓝牙状态发生变化时调用
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    switch (central.state)
    {
        case CBCentralManagerStateUnknown:
            NSLog(@"CBCentralManagerStateUnknown");
            break;
        case CBCentralManagerStateResetting:
            NSLog(@"CBCentralManagerStateResetting");
            break;
        case CBCentralManagerStateUnsupported:
            NSLog(@"CBCentralManagerStateUnsupported");
            break;
        case CBCentralManagerStateUnauthorized:
            NSLog(@"CBCentralManagerStateUnauthorized");
            break;
        case CBCentralManagerStatePoweredOff:
        {
            UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"前往打开蓝牙设置" message:nil preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
            [alertC addAction:confirmAction];
            [self presentViewController:alertC animated:YES completion:nil];
            
            break;
        }
        case CBCentralManagerStatePoweredOn:
        {
            //开始扫描
//            [self.cbCentralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
            [self.cbCentralManager scanForPeripheralsWithServices:nil options:nil];
            break;
        }
        default:
            break;
    }
}


/**
 *  扫描到蓝牙设备后调用
 *  central     中心
 *  peripheral      外设
 *  advertisementData   外设广播的数据
 *  RSSI    接受信号强度,根据信号强度可以计算蓝牙设备的距离,计算公式为( d = 10^((abs(RSSI) - A) / (10 * n)) ),其中:d - 计算所得距离, RSSI - 接收信号强度(负值), A - 发射端和接收端相隔1米时的信号强度, n - 环境衰减因子
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    //拿到小米手环的外设
    if ([peripheral.name containsString:@"MI Band 2"])
    {
        _peripheral = peripheral;
        //停止扫描
        [self.cbCentralManager stopScan];
        //开始连接
        [self.cbCentralManager connectPeripheral:_peripheral options:nil];
    }
}
//当连接外设成功时调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"连接外设成功");
    //设置代理
    [_peripheral setDelegate:self];
    //扫描外设中的服务
    [peripheral discoverServices:nil];
}
//当连接外设失败时调用
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"连接外设失败--%@", error);
}
//当外设时区连接时调用,可以在在这里实现自动重新连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"丢失连接");
    //重新连接
    [_cbCentralManager connectPeripheral:_peripheral options:nil];
}


#pragma mark ====================    CBPeripheralDelegate    ====================
/**
 *  扫描到的小米手环的服务
 *  "",
 *  "",
 *  "",
 *  "",
 *  "",
 *  "",
 *  ""
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error)
    {
        NSLog(@"扫描服务失败--%@--%@", peripheral.name, error);
        return;
    }
    NSLog(@"扫描服务成功--%@", peripheral.services);
    //开始扫描服务
    for (CBService *service in peripheral.services)
    {
        [peripheral discoverCharacteristics:nil forService:service];
    
    }
}

/**
 *   扫描到服务中的特征
 *   2017-10-26 00:44:53.680903+0800 CoreBlueTooth[7135:693724] 扫描特征成功--Device Information--(
 *   "",
 *   "",
 *   "",
 *   "",
 *   ""
 *   )
 *   2017-10-26 00:44:53.775460+0800 CoreBlueTooth[7135:693724] 扫描特征成功--00001530-0000-3512-2118-0009AF100700--(
 *   "",
 *   ""
 *   )
 *   2017-10-26 00:44:53.872212+0800 CoreBlueTooth[7135:693724] 扫描特征成功--1811--(
 *   "",
 *   ""
 *   )
 *   2017-10-26 00:44:53.969354+0800 CoreBlueTooth[7135:693724] 扫描特征成功--1802--(
 *   ""
 *   )
 *   2017-10-26 00:44:54.067643+0800 CoreBlueTooth[7135:693724] 扫描特征成功--Heart Rate--(
 *   "",
 *   ""
 *   )
 *   2017-10-26 00:44:54.465547+0800 CoreBlueTooth[7135:693724] 扫描特征成功--FEE0--(
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   "",
 *   ""
 *   )
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (error)
    {
        NSLog(@"扫描特征失败--%@--%@", service.characteristics, error);
        return;
    }
    NSLog(@"扫描特征成功--%@--%@--%@", peripheral.name, service.UUID, service.characteristics);
    
    //这里外设需要订阅特征的通知,否则无法收到外设发送过来的数据,需要为每一个需要读取数据的特征单独设置
//    [peripheral setNotifyValue:YES forCharacteristic:service.characteristics];
    
//    [peripheral readValueForCharacteristic:service.characteristics.firstObject];
    
    //遍历特征
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        NSLog(@"%@", characteristic.UUID.UUIDString);
        //读取步数
        if ([characteristic.UUID.UUIDString isEqualToString:@"FED3"])
        {
            //可以通过两种方式来读取数据,方别进入两个回调
            [peripheral readValueForCharacteristic:characteristic];
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
    }
}

//获得从外设发来的数据,在方法"readValueForCharacteristic:"执行之后调用,
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error)
    {
        NSLog(@"读取特征数据失败--%@", error);
        return;
    }
    NSLog(@"%@", characteristic);
}
//中心(本机)读取从外设发来的数据,在方法"setNotifyValue:forCharacteristic:"执行之后调用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error)
    {
        NSLog(@"读取特征数据失败--%@", error);
        return;
    }
    NSLog(@"%@", characteristic);
}
//检测数据是否写入成功
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    
}

你可能感兴趣的:(OC 原生蓝牙框架CoreBlueTooth)