iOS面试题相关知识点

iOS 蓝牙开发 - swift版

2019-08-10  本文已影响0人  孔雨露

@[TOC](iOS 蓝牙开发 )

1.蓝牙简介

  1. 目前都使用的是低功耗蓝牙4.0,蓝牙外设必需为4.0及以上(2.0需要MFI认证),否则无法开发,蓝牙4.0设施由于低耗电,所以也叫做BLE。
  2. CoreBluetooth框架的核心其实是两个东西,peripheral和central, 能了解成外设和中心,就是你的苹果手机就是中心,外部蓝牙称为外设。
  3. 服务和特征(service characteristic):简而言之,外部蓝牙中它有若干个服务service(服务你能了解为蓝牙所拥有的可以力),而每个服务service下拥有若干个特征characteristic(特征你能了解为解释这个服务的属性)。
  4. Descriptor(形容)使用来形容characteristic变量的属性。例如,一个descriptor能规定一个可读的形容,或者者一个characteristic变量可接受的范围,或者者一个characteristic变量特定的单位。
  5. 我们用的蓝牙板块是在淘宝买的, 大概十多元一个, ios大概每次能接受90个字节, 安卓大概每次能接收20个字节, 具体数字可可以会浮动, 应该是与蓝牙板块有关。

2. 蓝牙连接

自己实践的两个蓝牙demo:

  1. OC 编写的:Bluetooth4_configWifi
  2. swifit编写的:KYLBluetoothDemo_swift

2.1 CoreBluetooth框架

CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心。对应他们分别有一组相关的API和类

在这里插入图片描述
//objcetive c特征的定义枚举
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
};

2.2 外设、服务、特征间的关系

在这里插入图片描述 在这里插入图片描述

2.3 蓝牙连接过程

  1. 创建中心设备(CBCentralManager)
  2. 中心设备开始扫描(scanForPeripherals)
  3. 扫描到外围设备之后, 自动调用中心设备的代理方法(didDiscoverPeripheral)
  4. 如果设备过多, 可以将扫描到的外围设备添加到数组
  5. 开始连接, 从数组中过滤出自己想要的设备, 进行连接(connectPeripheral)
  6. 连接上之后, 自动调用中心设备的代理方法(didConnectPeripheral), 在代理中, 进行查找外围设备的服务(peripheral.discoverServices)
  7. 查找到服务之后, 自动调用外围设备的代理(didDiscoverServices), 可通过UUID,查找具体的服务,查找服务(discoverCharacteristics)
  8. 查找到特征之后, 自动调用外围设备的代理(didDiscoverCharacteristics), 通过UUID找到自己想要的特征, 读取特征(readValueForCharacteristic)
  9. 读取到特征之后, 自动调用外设的代理方法(didUpdateValueForCharacteristic),在这里打印或者解析自己想要的特征值.

2.4 蓝牙中心模式,外设模式

2.4.1 蓝牙中心模式

  1. 建立中心角色

  2. 扫描外设(discover)

  3. 连接外设(connect)

  4. 扫描外设中的服务和特征(discover)
    4.1 获取外设的services
    4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的 Descriptor和Descriptor的值

  5. 与外设做数据交互(explore and interact)

  6. 订阅Characteristic的通知

  7. 断开连接(disconnect)

2.4.2 蓝牙外设模式

  1. 启动一个Peripheral管理对象

  2. 本地Peripheral设置服务,特性,描述,权限等等

  3. Peripheral发送广告

  4. 设置处理订阅、取消订阅、读characteristic、写characteristic的委托方法

2.5 蓝牙设备状态

2.6 蓝牙连接代码实现

  1. 初始化
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
  1. 搜索扫描外围设备
/**
 *  --  初始化成功自动调用
 *  --  必须实现的代理,用来返回创建的centralManager的状态。
 *  --  注意:必须确认当前是CBCentralManagerStatePoweredOn状态才可以调用扫描外设的方法:
 scanForPeripheralsWithServices
 */
- (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:
            NSLog(@">>>CBCentralManagerStatePoweredOff");
            break;
        case CBCentralManagerStatePoweredOn:
        {
            NSLog(@">>>CBCentralManagerStatePoweredOn");
            // 开始扫描周围的外设。
            /*
             -- 两个参数为Nil表示默认扫描所有可见蓝牙设备。
             -- 注意:第一个参数是用来扫描有指定服务的外设。然后有些外设的服务是相同的,比如都有FFF5服务,那么都会发现;而有些外设的服务是不可见的,就会扫描不到设备。
             -- 成功扫描到外设后调用didDiscoverPeripheral
             */
            [self.centralManager scanForPeripheralsWithServices:nil options:nil];
        }
            break;
        default:
            break;
    }
}

#pragma mark 发现外设
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary*)advertisementData RSSI:(NSNumber *)RSSI{
    NSLog(@"Find device:%@", [peripheral name]);
    if (![_deviceDic objectForKey:[peripheral name]]) {
        NSLog(@"Find device:%@", [peripheral name]);
        if (peripheral!=nil) {
            if ([peripheral name]!=nil) {
                if ([[peripheral name] hasPrefix:@"根据设备名过滤"]) {
                    [_deviceDic setObject:peripheral forKey:[peripheral name]];
                     // 停止扫描, 看需求决定要不要加
//                    [_centralManager stopScan];
                    // 将设备信息传到外面的页面(VC), 构成扫描到的设备列表
                    if ([self.delegate respondsToSelector:@selector(dataWithBluetoothDic:)]) {
                        [self.delegate dataWithBluetoothDic:_deviceDic];
                    }
                }
            }
        }
    }
}

  1. 连接外围设备
// 连接设备(.h中声明出去的接口, 一般在点击设备列表连接时调用)
- (void)connectDeviceWithPeripheral:(CBPeripheral *)peripheral
{
    [self.centralManager connectPeripheral:peripheral options:nil];
}



#pragma mark 连接外设--成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    //连接成功后停止扫描,节省内存
    [central stopScan];
    peripheral.delegate = self;
    self.peripheral = peripheral;
    //4.扫描外设的服务
    /**
     --     外设的服务、特征、描述等方法是CBPeripheralDelegate的内容,所以要先设置代理peripheral.delegate = self
     --     参数表示你关心的服务的UUID,比如我关心的是"FFE0",参数就可以为@[[CBUUID UUIDWithString:@"FFE0"]].那么didDiscoverServices方法回调内容就只有这两个UUID的服务,不会有其他多余的内容,提高效率。nil表示扫描所有服务
     --     成功发现服务,回调didDiscoverServices
     */
    [peripheral discoverServices:@[[CBUUID UUIDWithString:@"你要用的服务UUID"]]];
    if ([self.delegate respondsToSelector:@selector(didConnectBle)]) {
       // 已经连接
        [self.delegate didConnectBle];
    }
}



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


#pragma mark 取消与外设的连接回调
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"%@", peripheral);
}

  1. 获得外围设备的服务
#pragma mark 发现服务回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
 
    //NSLog(@"didDiscoverServices,Error:%@",error);
    CBService * __nullable findService = nil;
    // 遍历服务
    for (CBService *service in peripheral.services)
    {
        //NSLog(@"UUID:%@",service.UUID);
        if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"你要用的服务UUID"]])
        {
            findService = service;
        }
    }
    NSLog(@"Find Service:%@",findService);
    if (findService)
        [peripheral discoverCharacteristics:NULL forService:findService];
}


#pragma mark 发现特征回调
/**
 --  发现特征后,可以根据特征的properties进行:读readValueForCharacteristic、写writeValue、订阅通知setNotifyValue、扫描特征的描述discoverDescriptorsForCharacteristic。
 **/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    for (CBCharacteristic *characteristic in service.characteristics) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你要用的特征UUID"]]) {
 
            /**
             -- 读取成功回调didUpdateValueForCharacteristic
             */
            self.characteristic = characteristic;
            // 接收一次(是读一次信息还是数据经常变实时接收视情况而定, 再决定使用哪个)
//            [peripheral readValueForCharacteristic:characteristic];
            // 订阅, 实时接收
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
 
            // 发送下行指令(发送一条)
            NSData *data = [@"硬件工程师给我的指令, 发送给蓝牙该指令, 蓝牙会给我返回一条数据" dataUsingEncoding:NSUTF8StringEncoding];
            // 将指令写入蓝牙
                [self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
        }
        /**
         -- 当发现characteristic有descriptor,回调didDiscoverDescriptorsForCharacteristic
         */
        [peripheral discoverDescriptorsForCharacteristic:characteristic];
    }
}


  1. 从外围设备读取数据
#pragma mark - 获取值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    // characteristic.value就是蓝牙给我们的值(我这里是json格式字符串)
    NSData *jsonData = [characteristic.value dataUsingEncoding:NSUTF8StringEncoding];
        NSDictionary *dataDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
    // 将字典传出去就可以使用了
}


#pragma mark - 中心读取外设实时数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (characteristic.isNotifying) {
        [peripheral readValueForCharacteristic:characteristic];
    } else { 
        NSLog(@"Notification stopped on %@.  Disconnecting", characteristic);
        NSLog(@"%@", characteristic);
        [self.centralManager cancelPeripheralConnection:peripheral];
    }
}

  1. 给外围设备发送(写入)数据

// 上文中发现特征之后, 发送下行指令的时候其实就是向蓝牙中写入数据
// 例:
// 发送检查蓝牙命令
- (void)writeCheckBleWithBle
{
    _style = 1;
    // 发送下行指令(发送一条)
    NSData *data = [@"硬件工程师提供给你的指令, 类似于5E16010203...这种很长一串" dataUsingEncoding:NSUTF8StringEncoding];
    [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}



#pragma mark 数据写入成功回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    NSLog(@"写入成功");
    if ([self.delegate respondsToSelector:@selector(didWriteSucessWithStyle:)]) {
        [self.delegate didWriteSucessWithStyle:_style];
    }
}

  1. 停止扫描

#pragma mark 停止扫描外设
- (void)stopScanPeripheral{
    [self.centralManager stopScan];
}


#pragma mark 扫描外设
- (void)scanDevice
{
    if (_centralManager == nil) {
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    [_deviceDic removeAllObjects];
     }
}

  1. 断开连接
#pragma mark 断开连接
- (void)disConnectPeripheral{
    /**
     -- 断开连接后回调didDisconnectPeripheral
     -- 注意断开后如果要重新扫描这个外设,需要重新调用[self.centralManager scanForPeripheralsWithServices:nil options:nil];
     */
    [self.centralManager cancelPeripheralConnection:self.peripheral];
}

上一篇 下一篇

猜你喜欢

热点阅读