蓝牙

iOS中的蓝牙


概述

iOS中提供了4个框架用于实现蓝牙连接

  • 1、GameKit.framework(用法简单)

    只能用于iOS设备之间的同个应用的内连接,多用于游戏,从iOS7开始过期

  • 2、MultipeerConnectivity.framework(代替1)

    只能用于iOS设备之间的连接,从iOS7开始引入,主要用于非联网状态下,通过WIFI或蓝牙进行文件共享(仅限于沙盒的文件),多用于附近无网聊天。

  • 3、ExternalAccessory.framework(MFI)

    可用于第三方蓝牙设备交互,但是蓝牙设备必须经过苹果 MFI 认证(国内很少)。

  • 4、CoreBluetooth.framework(主流)

    可用于第三方蓝牙设备交流,必须要支持蓝牙4.0

    硬件至少是4s,系统至少是iOS6

    蓝牙4.0以低功耗著称,一般也叫BLE(Bluetooth Low Energy)

    目前应用比较多的案例:运动手环,嵌入式设备,智能家居


涉及到的系统/框架

  • HealthKit / 物联网 HomeKit / wattchOS1,2,3 / Beacon 


一、了解CoreBluetooth.framework

  • 中心模式流程

    1.建立一个CentralManager实例进行蓝牙管理

    2.搜索外围设备

    3.连接外围设备

    4.获得外围设备的服务

    5.获得服务的特征

    6.从外围设备读数据

    7.给外围设备发送数据

    8.其他:提醒


  • 外设模式流程 

    1.启动一个Peripheral管理对象

    2.本地peripheral设置服务,特征,描述,权限等

    3.peripheral发送广告

    4.设置处理订阅,取消订阅,读characteristic,写characteristic的代理方法



  • 中心设备,外设,服务,特征,描述之间的关系

    借鉴一张图大致了解一下:


  • peripheral,central:外设和中心设备,发起链接的是central(一般是指手机),被链接的设备是peripheral(运动手环)

    每个设备都会有一个或多个服务

    每个服务里都会有一个或多个特征

    特征就是具体键值对,提供数据的地方

    每个特征属性分为:读、写、通知等。

  • 外设服务特征的关系



二、中心设备代码实现步骤

1、建立一个CentralManager实例进行蓝牙管理

 
 
  1. // 引入头文件
  2. #import <CoreBluetooth/CoreBluetooth.h>
  3. /** 中心管理者 */
  4. @property (nonatomic, strong) CBCentralManager *centralManager;
  5. /** 懒加载初始化中心管理者 */
  6. - (CBCentralManager *)centralManager {
  7.    if (!_centralManager) {
  8.        _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:nil];
  9.    }
  10.    return _centralManager;
  11. }
  12. - (void)viewDidLoad {
  13.    [super viewDidLoad];
  14.    self.title = @"BELCenter";
  15.    // 先将中心管理者初始化
  16.    // 打开后会回调 CBCentralManagerDelegate 协议中的方法centralManagerDidUpdateState
  17.    [self centralManager];
  18. }


2、搜索外围设备

 
 
  1. // 引入协议@interface KYCenterBLEVC () <CBCentralManagerDelegate>
  2. #pragma mark - CBCentralManagerDelegate, 中心管理者初始化就会触发此代理方法
  3. /* 中心管理器初始化后,会回调该协议, 当中心管理器状态打开时,扫描外围连接设备 */
  4. #pragma mark - CBCentralManagerDelegate, 中心管理者初始化就会触发此代理方法
  5. - (void)centralManagerDidUpdateState:(CBCentralManager *)central {
  6.    /*
  7.     CBManagerStateUnknown = 0,
  8.     CBManagerStateResetting,
  9.     CBManagerStateUnsupported,
  10.     CBManagerStateUnauthorized,
  11.     CBManagerStatePoweredOff,
  12.     CBManagerStatePoweredOn,
  13.     */
  14.    switch (central.state) {
  15.        case 0:
  16.            NSLog(@"CBManagerStateUnknown");
  17.            break;
  18.        case 1:
  19.            NSLog(@"CBManagerStateResetting");
  20.            break;
  21.        case 2:
  22.            NSLog(@"CBManagerStateUnsupported");
  23.            break;
  24.        case 3:
  25.            NSLog(@"CBManagerStateUnauthorized");
  26.            break;
  27.        case 4:
  28.            NSLog(@"CBManagerStatePoweredOff");
  29.            break;
  30.        case 5: {
  31.            NSLog(@"CBManagerStatePoweredOn");
  32.             // 扫描外围设备,            
  33.             // 发现外设后,会自动回调CBCentralManagerDelegate协议方法centralManager:didDiscoverPeripheral:  
  34.            [self.centralManager scanForPeripheralsWithServices:nil     // 通过某些服务筛选外设
  35.                                                        options:nil];   // dict,条件
  36.        }
  37.            break;
  38.        default:
  39.            break;
  40.    }
  41. }


3、连接外围设备

 
 
  1. /** 发现外设后调用的方法 */
  2. - (void)centralManager:(CBCentralManager *)central                          // 中心管理者
  3. didDiscoverPeripheral:(CBPeripheral *)peripheral                           // 外设
  4.     advertisementData:(NSDictionary<NSString *, id> *)advertisementData    // 外设携带的数据
  5.                  RSSI:(NSNumber *)RSSI {                                   // 外设发出的外设蓝牙强度
  6.    NSLog(@"%s, line = %d, central=%@, peripheral=%@, advertisementData=%@, RSSI=%d", __FUNCTION__, __LINE__, central, peripheral, advertisementData, RSSI.intValue);
  7.     /*
  8.       1、过滤外设,可以根据设备名称,或者advertisementData 中携带的参数数据过滤设备
  9.       2、过滤信号强度,RSSI为负数,
  10.       3、一般会把搜索到的设备可视化为列表,然后由用户手动连接设备,此处demo过滤后直接连接了外设
  11.      */
  12.    if ([peripheral.name containsString:@"康鹏鹏"] && abs(RSSI.intValue) < 90 && abs(RSSI.intValue) > 0) {
  13.        // 通常过滤后的外设存储到可变数组中,此处使用属性仅对一组外设进行存储
  14.        self.peripheral = peripheral;        // 连接外设,连接成功后,回调centralManager:didConnectPeripheral:
  15.        [self.centralManager connectPeripheral:peripheral options:nil];
  16.    }
  17. }
  18. /** 管理者连接外设成功 */
  19. - (void)centralManager:(CBCentralManager *)central      // 中心管理者
  20.  didConnectPeripheral:(CBPeripheral *)peripheral {     // 外设
  21.    NSLog(@"%s, line = %d, 外设<-%@->连接成功", __FUNCTION__, __LINE__, peripheral.name);
  22.    // 连接成功之后可以进行一些数据的交互
  23.    // 1、设置外设代理
  24.    self.peripheral.delegate = self;
  25.    // 2、发现服务,nil代表不过滤
  26.    // 这里会触发外设CBPeripheralDelegate代理方法 peripheral: didDiscoverServices:
  27.    //[self.peripheral discoverServices:nil];
  28.    [self.peripheral discoverServices:@[[CBUUID UUIDWithString:Service1StrUUID]]];
  29. }
  30. /** 连接失败 */
  31. - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
  32.    NSLog(@"%s, line = %d, 外设<-%@->连接失败", __FUNCTION__, __LINE__, peripheral.name);
  33. }
  34. /** 丢失连接 */
  35. - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
  36.    NSLog(@"%s, line = %d, 外设<-%@->连接断开", __FUNCTION__, __LINE__, peripheral.name);
  37. }
  38. - (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict {
  39.    NSLog(@"%s, line = %d", __FUNCTION__, __LINE__);
  40. }


4、获得外围设备的服务

 
 
  1. // 添加CBPeripheralDelegate 协议
  2. @interface KYCenterBLEVC () <CBCentralManagerDelegate, CBPeripheralDelegate>
  3. #pragma mark - CBPeripheralDelegate 外设代理
  4. /** 发现外设服务后自动回调 */
  5. - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
  6.    NSLog(@"%s, line = %d, peripheral = %@", __FUNCTION__, __LINE__, peripheral);
  7.    if (error) {
  8.        NSLog(@"%s, line = %d, error = %@", __FUNCTION__, __LINE__, error);
  9.        return;
  10.    }
  11.    for (CBService *service in peripheral.services) {
  12.        // 发现服务,再让设备发现服务内部特征,根据UUID过滤外设服务(外设服务可以有多个)        // 发现特征后,会触发peripheral: didDiscoverCharacteristicsForService: error:
  13.        [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:notiyCharacteristicStrUUID]] forService:service];
  14.    }
  15. }


5、获得服务的特征

 
 
  1. /** 发现外设服务里的特征的时候调用的代理方法 */
  2. - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
  3.    for (CBCharacteristic *chart in service.characteristics) {
  4.        NSLog(@"%s, line = %d, chart = %@", __FUNCTION__, __LINE__, chart);
  5.        // 获取特征对应的描述,会触发peripheral: didDiscoverDescriptorsForCharacteristic: error:
  6.        [peripheral discoverDescriptorsForCharacteristic:chart];
  7.        // 获取特征值
  8.        // 发现外设特征后,回调peripheral: didUpdateValueForCharacteristic: error:
  9.        [peripheral readValueForCharacteristic:chart];
  10.    }
  11. }
  12. // 发现外设特征描述
  13. - (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
  14.    // 此处读取描述即可
  15.    for (CBDescriptor *descriptor in characteristic.descriptors) {
  16.        // 它会触发peripheral: didUpdateValueForDescriptor: error:
  17.        [peripheral readValueForDescriptor:descriptor];
  18.        NSLog(@"%s, line = %d, descriptor = %@", __FUNCTION__, __LINE__, descriptor);
  19.    }
  20. }


6、从外围设备读数据

  
  
  1. // 更新特征的value时调用,或读取特征值时自动调用
  2. - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
  3.    for (CBDescriptor *descriptor in characteristic.descriptors) {
  4.        NSLog(@"%s, line = %d, characteristicValue = %@", __FUNCTION__, __LINE__, [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding]);
  5.    }
  6. }
  7. // 更新特征的描述值的时候调用,或读取特征描述时自动调用
  8. - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error {
  9.    NSLog(@"%s, line = %d, descriptorValue = %@", __FUNCTION__, __LINE__, descriptor.value);
  10.    // 这里当描述的值更新时,回调此方法
  11. }


7、给外围设备发送数据

  
  
  1. - (void)ky_peripheral:(CBPeripheral *)peripheral didWriteData:(NSData *)data forCharacteristic:(nonnull CBCharacteristic *)characteristic {
  2.    /*
  3.     typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
  4.     CBCharacteristicPropertyBroadcast                                                = 0x01,
  5.     CBCharacteristicPropertyRead                                                    = 0x02,
  6.     CBCharacteristicPropertyWriteWithoutResponse                                    = 0x04,
  7.     CBCharacteristicPropertyWrite                                                    = 0x08,
  8.     CBCharacteristicPropertyNotify                                                    = 0x10,
  9.     CBCharacteristicPropertyIndicate                                                = 0x20,
  10.     CBCharacteristicPropertyAuthenticatedSignedWrites                                = 0x40,
  11.     CBCharacteristicPropertyExtendedProperties                                        = 0x80,
  12.     CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)        = 0x100,
  13.     CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)    = 0x200
  14.     };
  15.     打印出特征的权限(characteristic.properties),可以看到有很多种,这是一个NS_OPTIONS的枚举,可以是多个值
  16.     常见的又read,write,noitfy,indicate.知道这几个基本够用了,前俩是读写权限,后俩都是通知,俩不同的通知方式
  17.     */
  18.    NSLog(@"%s, line = %d, char.pro = %lu", __FUNCTION__, __LINE__, (unsigned long)characteristic.properties);
  19.    // 此时由于枚举属性是NS_OPTIONS,所以一个枚举可能对应多个类型,所以判断不能用 = ,而应该用包含&
  20.    // 判断是否支持写属性
  21.    if (characteristic.properties & CBCharacteristicPropertyWrite) {
  22.        // 核心代码在这里
  23.        [peripheral writeValue:data // 写入的数据
  24.             forCharacteristic:characteristic // 写给哪个特征
  25.                          type:CBCharacteristicWriteWithResponse];// 通过此响应记录是否成功写入
  26.    }
  27. }

8、其他:提醒

   
   
  1. /**
  2. 通知的订阅
  3. 实际核心代码是一个方法
  4. 一般这两个方法要根据产品需求来确定写在何处
  5. */
  6. - (void)ky_peripheral:(CBPeripheral *)peripheral regNotifyWithCharacteristic:(nonnull CBCharacteristic *)characteristic {
  7.    // 外设为特征订阅通知 数据会进入 peripheral: didUpdateValueForCharacteristic:error:方法
  8.    [peripheral setNotifyValue:YES forCharacteristic:characteristic];
  9. }
  10. // 通知的取消订阅
  11. - (void)ky_peripheral:(CBPeripheral *)peripheral CancleRegNotifyWithCharacteristic:(nonnull CBCharacteristic *)characteristic {
  12.    // 外设取消订阅通知 数据会进入 peripheral:didUpdateValueForCharacteristic:error:方法
  13.    [peripheral setNotifyValue:NO forCharacteristic:characteristic];
  14. }

9、断开连接

   
   
  1. // 断开连接
  2. - (void)ky_dismissConentedWithPeripheral:(CBPeripheral *)peripheral {
  3.    // 停止扫描
  4.    [self.centralManager stopScan];
  5.    if (peripheral) {
  6.        // 断开连接
  7.        [self.centralManager cancelPeripheralConnection:peripheral];
  8.    }
  9. }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值