CoreBluetooth-中央模式

来源:互联网 发布:苹果乐器软件 编辑:程序博客网 时间:2024/05/14 16:00
刚刚接触蓝牙的可以先下载个LightBlue,里面可以比较直观的看到蓝牙设备的一些信息,可以用作开发时的参考和结果的对照。
  1. 蓝牙4.0
    • 蓝牙4.0是由蓝牙技术联盟在2012年发布的最新蓝牙版本,较3.0版本更省电、成本低、延迟低、超长有效连接距离、AES-128加密等。支持两种部署方式:双模式和单模式。双模式包含传统蓝牙部分(Classsic Bluetooth)和低功耗蓝牙部分(Bluetooth Low Energy 即BLE)。
    • CoreBluetooth API是基于蓝牙4.0BLE标准,也就是说使用CoreBluetooth开发的时候,对应的蓝牙设备必须也遵循蓝牙4.0。
    • iPhone 4S及以后的设备,第三代iPad及以后的设备是支持这一标准的。
  2. CoreBluetooth开发分两种模式
    • 周边模式(Peripheral):自己作为蓝牙设备,对外广播数据。
    • 中央模式(Central):接收周边蓝牙发来的广播数据。一般手机开发都是中央模式,这篇博客也只讲解中央模式,周边模式和相关的IBeacon之后再说。
  3. 中央模式的开发
    3.1 引入框架 CoreBluetooth.framework,
    引入头文件< CoreBluetooth/CoreBluetooth.h>
    遵循协议CBCentralManagerDelegate,CBPeripheralDelegate

    3.2 定义声明“中央管理者” CBCentralManager

@property (nonatomic,strong) CBCentralManager * cbCentralMgr;
_cbCentralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

初始化并设置代理后,会 自动 回调方法- (void)centralManagerDidUpdateState:(CBCentralManager *)central ;来监测手机的蓝牙状态。

扫描周边蓝牙,要 手动 调用方法
- (void)scanForPeripheralsWithServices:(nullable NSArray< CBUUID > )serviceUUIDs options:(nullable NSDictionary< NSString *, id> *)options;
第一个参数serviceUUIDs,是用来过滤蓝牙的。为nil,扫描所有的蓝牙设备,不为空,只扫描有指定服务的蓝牙设备。(服务下面会讲到)

我们想下蓝牙的使用情况,打开手机蓝牙->自动扫描周边设备->列出周边设备->点击连接某个设备->与设备交互->断开连接

上面两个方法一般是一块使用 发现设备蓝牙打开后,就开始扫描周围设备

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {    if (central.state == CBCentralManagerStatePoweredOn) {//手机打开了蓝牙        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],CBCentralManagerScanOptionAllowDuplicatesKey, nil];        //开始扫描周围蓝牙设备        [self.cbCentralMgr scanForPeripheralsWithServices:nil options:dic];      }}

每扫描到一个周围的蓝牙设备后,会 自动 回调方法- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary< NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI;
参数含义如下:
/**
* @param central 中心设备
* @param peripheral 外围设备
* @param advertisementData 特征数据
* @param RSSI 信号质量(信号强度)
*/
这里可以先用一个数组记录下扫描到的peripheral,然后用tableview的方式列举出来,点击某个cell的时候就去连接对应的外围设备,调用下面的连接蓝牙方法去连接。

连接蓝牙 要 手动 调用方法去连接蓝牙
- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary< NSString *, id> *)options;
第一个参数就是刚刚扫描到的外围设备,第二个参数一般为[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]

连接到蓝牙后,会 自动 回调方法 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;

连接失败的话, 会 自动 回调方法- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;

到目前为止,是能连接到外围的蓝牙设备,照着上面的步骤来,比较简单。接下来是与外围蓝牙设备交互,容易出现问题的地方。

向外围设备读写数据之前,先了解下外围设备CBPeripheral。
1.用属性NSUUID *identifier来区分每个外围设备,而不是同安卓开发拿到外围设备的Mac地址。这个identifier其实是根据外围设备的Mac地址和手机蓝牙模块的Mac地址,通过一定方式计算得到的。这就是说同一个外围设备在不同手机上identifier是不一样的,这样就不能用像安卓一样用固定Mac地址来区分固定的外围设备。(如果一定要Mac地址,可以让蓝牙开发商,将Mac地址放在广播数据包中)

2.服务CBService,表示外围设备能干啥,每个服务用CBUUID *UUID区分。比如标准蓝牙自带的180F,用来读取蓝牙电量。

3.特征值CBCharacteristic,特征值用来描述每个服务具体的操作。这个是与蓝牙交互的最终单位,区分每个特征值的也是CBUUID *UUID。
与蓝牙交互包括:读数据、写数据,
读数据的方式包括:发广播(也叫订阅) 和 读取,
写数据包括:有回调的写 和 没回调的写。
如何区分一个特征值到底是具备哪个交互属性?通过属性值CBCharacteristicProperties properties

typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {    CBCharacteristicPropertyBroadcast                                               = 0x01,    CBCharacteristicPropertyRead                                                    = 0x02,    CBCharacteristicPropertyWriteWithoutResponse                                    = 0x04,    CBCharacteristicPropertyWrite                                                   = 0x08,    CBCharacteristicPropertyNotify                                                  = 0x10,    CBCharacteristicPropertyIndicate                                                = 0x20,    CBCharacteristicPropertyAuthenticatedSignedWrites                               = 0x40,    CBCharacteristicPropertyExtendedProperties                                      = 0x80,    CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)     = 0x100,    CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)   = 0x200};

这个是可以自由组合的,比如同时具备读和写两种特征,当然这个由外围设备开发决定。

通过特征值来读\写数据:
1.广播(订阅),如果这个特征值具备属性”CBCharacteristicPropertyNotify = 0x10“,就可以通过注册广播的方式来,定期获取蓝牙数据,也就是蓝牙会每过一段时间自动发送与这个特征值相关的数据。(如心跳率 步数等 这些数据要持续更新,让外围设备每隔一段时间自己对外广播,中央设备读取就好了)
调用的方法等下介绍。

2.读取 如果这个特征值具备属性”CBCharacteristicPropertyRead = 0x02“ 就可以直接读取。

3.有回调的写 ”CBCharacteristicPropertyWrite = 0x08“
4.没回调的写”CBCharacteristicPropertyWriteWithoutResponse = 0x04“

如何获取蓝牙服务 和 特征值
这些是外围设备的属性,通过代理CBPeripheralDelegate,来回调。
在连接上外围设备的回调中,设置外围设备的代理为当前控制器,并开始调用方法- (void)discoverServices:(nullable NSArray< CBUUID > )serviceUUIDs;来获取服务

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {   //给外围设备设置代理    peripheral.delegate = self;    /*    查询外围设备的服务      如果不知道要查那个服务,参数给nil,返回所有的服务。    如果知道服务的uuid可以直接给具体的参数,服务的uuid可由外围设备开发商提供,    或者先读取所有的服务,找到有用的之后再过来修改    */    [peripheral discoverServices:nil]; }

找到服务的回调方法- (void)peripheral:(CBPeripheral )peripheral didDiscoverServices:(nullable NSError )error; 这个时候peripheral.services里就是该外围设备的全部服务。 获取每个服务的特征值调用方法- (void)discoverCharacteristics:(nullable NSArray< CBUUID *> *)characteristicUUIDs forService:(CBService *)service;

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {    for (CBService * service in peripheral.services) {     //可以把服务打印出来看看      NSLog(@"service = %@",service);      if (service == 自己想要的服务) {       /*获取该服务的特征值        同样的参数 如果不知道特征值的uuid,就填nil,获取所有的特征值*/            [peripheral discoverCharacteristics:nil forService:service];        }    }}

找到特征值的回调方法- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;这时service.characteristics里就是这个服务的特征值,一般我们找到特征值后不会立马就向外围设备读写数据,要等到一定条件之后才执行这个操作,那么这个时候,就要把特征值存在本地,注意 每个外围设备的特征值都要存储。

- - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {    for (CBCharacteristic * characteristic in service.characteristics) {    //看下特征值的读写属性    NSLog(@"%ld",characteristic.properties);    if (characteristic.properties & 0x10) { //CBCharacteristicPropertyNotify  广播(订阅)            [peripheral setNotifyValue:YES forCharacteristic:characteristic];        }        if (characteristic.properties & 0x02) { //CBCharacteristicPropertyRead  读取             [peripheral readValueForCharacteristic:characteristic];        }        if (characteristic.properties & 0x08) { //CBCharacteristicPropertyWrite 有回调的写            Byte dataArr1[1];            dataArr1[0] = 0x07;            NSMutableData * sendData1 = [[NSMutableData alloc] initWithBytes:dataArr1 length:sizeof(dataArr1)];            //写数据 注意最后的参数            [peripheral writeValue:sendData1 forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];        }        if (characteristic.properties & 0x04) { //CBCharacteristicPropertyWriteWithoutResponse 没回调的写        Byte dataArr1[1];            dataArr1[0] = 0x07;            NSMutableData * sendData1 = [[NSMutableData alloc] initWithBytes:dataArr1 length:sizeof(dataArr1)];            //写数据 注意最后的参数            [peripheral writeValue:sendData1 forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];        }    }}

广播 (订阅)的回调方法

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {//    NSLog(@"蓝牙发送过来的数据 characteristic = %@",characteristic);}

还有一个回调方法 不管是广播还是读取,都会调用

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {    if (characteristic == 自己的特征1 && characteristic.value) {       //记录 characteristic.value    }    if (characteristic == 自己的特征2 && characteristic.value) {       //记录 characteristic.value    }}

写数据的回调

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {//    NSLog(@"给蓝牙发数据的回调 error = %@ characteristic = %@",error,characteristic);}

其他的方法
停止扫描,在蓝牙连接的时候,最后停止扫描,让连接成功率更大 [self.cbCentralMgr stopScan];
获取信号强度,扫描时获取的信号强度可能不是很准,这时可以自己去请求信号强度 [CBPeripheral*per readRSSI];
主动断开连接 [self.cbCentralMgr cancelPeripheralConnection:_currentPeripheral];
连接断开时的回调- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;

0 0
原创粉丝点击