iOS BLE开发

简介

BLE(Bluetooth Low Energy),低功耗蓝牙,使用2.4GHz无线电频率。从蓝牙4.0开始支持。通常我们的手机都已支持蓝牙4.0,iPhone从iPhone4s起已经支持蓝牙4.0(不是iPhone4s就是iPhone5,具体没仔细查),而从iPhone8开始,蓝牙已经使用5.0了,可以支持蓝牙mesh。

宣告与发现

BLE设备通过广播宣告(advertising)数据包的方式被发现,发现设备的等待时间存在概率性,取决于三个参数(宣告间隔、扫描间隔和扫描窗口)。对于iOS开发,并不需要关心这几个参数,系统底层已经为我们做了最佳的选择。

通信

蓝牙技术联盟沿用经典蓝牙的规范内容,为蓝牙低功耗定义了一些profile,这些profile定义了一个设备在特定应用情景下如何工作。profile都基于通用属性规范(GATT)。GATT定义了属性,作为通用的封装数据的单位,并定义了如何通过蓝牙连接传输属性从而达到传输数据的目的。
对于手机端来说,蓝牙设备的数据简单来说就是一堆UUID及其对应的值,iOS就是一堆CBUUID。

Exp:

将一个WiFi+BLE设备接入家庭WiFi,共分几步:

  1. 发现设备
  2. 获取设备信息,即获取设备的services和characters
  3. 连接设备
  4. 与设备交互信息
  5. 断开连接
  • 发现设备

//在你需要的地方初始化
//这里创建了一个串行队列,如果queue为nil,则使用主线程
//实际使用时globle_queue也没出现过问题
self.serial_queue = dispatch_queue_create("ble_queue", DISPATCH_QUEUE_SERIAL);
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:self.serial_queue options:nil];

//开始扫描
/* warn:扫描接口要在下面回调中调用,而不是初始化之后就可以调用。
在初始化之后立刻调用的话,由于当前central.state == CBManagerStateUnknown,会导致扫描失败
*/
//实现CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
    if (central.state == CBManagerStatePoweredOn) {
        //在这里启动扫描接口,nil表示获取所有services
        /*CBCentralManagerScanOptionAllowDuplicatesKey 
          YES表示只要收到一个BLE设备的广播包就会上报,不会对相同的BLE设备进行过滤,这样做是比较耗电的,但业务需求,要不断更新周围的BLE设备,所以这里还是选择了YES
          NO为默认值,同一个BLE设备的广播包会被合并成,只通知一次事件
        */
        [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@(YES)}];
    }    
}

//扫描到外围设备时回调此方法
//外围设备:建立连接过程中接受建立一个活跃的物理连接请求的LE设备定义为Peripheral 角色
//中心设备:建立连接过程中发起建立活跃物理连接请求的LE 设备定义为Central 角色
//在我们这个例子中手机就是中心设备,要接入家庭WiFi的这个设备就是外围设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    //在该方法中,可以通过advertisementData解析过滤出自己的设备
    //RSSI为信号强度,负数,值越大(越接近0),信号越好

    //找到自己要连接的设备后,可以将peripheral存起来,用于后面在某个点击事件中进行连接
}
  • 连接设备

//property getter
//我们只获取下面两个services
- (NSArray *)serviceUUIDs {
    if (_serviceUUIDs == nil) {
        //这里遇到过一个偶现的奇葩问题
        //128bit初始化uuid时偶现过崩溃,原因未知,所以使用16bit初始化
        CBUUID* uuid1 = [CBUUID UUIDWithString:@"FF00"];
        CBUUID* uuid2 = [CBUUID UUIDWithString:@"FF10"];
        _serviceUUIDs = @[uuid1, uuid2];
    }
    return _serviceUUIDs;
}

//在你合适的地方调用连接外围设备
self.centralManager connectPeripheral:self.peripheral options:nil];

//实现CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    //获取感兴趣的服务及属性
    [peripheral discoverServices:self.serviceUUIDs];
}


//实现CBPeripheralDelegate
//发现所有你要的服务后会回调该方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
    //获取该属性对应的服务
    for (int i = 0; i < peripheral.services.count; i++) {
        CBService *service = peripheral.services[i];
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

//发现该服务对应的属性时会回调该方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
    //这里我是做了个标记为,当获取到了所有我想要的属性之后,再开始读写操作
    //但这里对我是不方便的,业务需要我全部获取完之后才能读写,所以不能每次回调进来都进行相应的操作

    if(discoverEnd) {
        //这里就可以按照自己的协议进行读写了
        ...write(ssid+password)
        ...
    }
}
  • 读写设备

//实现相应的delegate
//读回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    //这里有一点比较奇怪的是,我对某一属性setNotifyValue
    //只有第一次是通过didUpdateNotificationStateForCharacteristic这个回调上来的
    //而其他时候都是通过读回调上来的

    //ooxxoo
    //在通信完成后断开设备连接
    self.centralManager cancelPeripheralConnection:peripheral];
}

//通知回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {

}


- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {


}

你可能感兴趣的:(iOS BLE开发)