zoukankan      html  css  js  c++  java
  • iOS蓝牙开发CoreBluetooth快速入门

    在iOS开发中,实现蓝牙通信有两种方式,一种是使用传统的GameKit.framework,另一种就是使用在iOS 5中加入的CoreBluetooth.framework。

    利用CoreBluetooth框架,我们可以轻松实现两个iOS设备、iOS设备与非iOS蓝牙设备的交互。要注意的一点是目前这个框架只能支持蓝牙4.0BLE标准,所以对硬件上是有一定要求的,iPhone 4S及以后的设备,第三代iPad及以后的设备是支持这一标准的。

    术语解释

    我们首先看一下CoreBluetooth框架的结构。这是CoreBluetooth/CoreBluetooth.h文件中的声明。

    1
    2
    3
    4
    #ifndef _CORE_BLUETOOTH_H_
    #define _CORE_BLUETOOTH_H_
    #endif
    #import #import #import #import #import #import #import #import #import #import #import

    其中,CBCentral开头的都是中心设备类,CBPeripheral开头的都是外设类。这就要讲到蓝牙设备的两个角色了。

    • 中心设备:中心设备可以理解为是处理数据的iOS设备,比如你的 iPhone、iPad 等。

    • 外设:外设顾名思义,就是产生数据的外部设备。这个外部设备可以是单片机、嵌入式设备甚至是另一个iOS设备等等。外设可以通过其传感器等产生有用数据,数据后通过蓝牙传给中心设备使用。

    在建立连接的之前,外设向外发出广播数据(advertisementData,官方描述“A dictionary containing any advertisement and scan response data.”),广播数据是一个字典类数据,中心设备可以获取一定范围内的外设发出的广播数据。

    现在开始

    初始化

    为了使用CoreBluetooth框架中的回调方法,我们要使用CBCentralManagerDelegate、CBPeripheralDelegate这两个协议。

    我们先要初始化一个CBCentralManager类的对象。代码如下:

    1
    2
    3
    4
    5
    6
    7
    @interface SKBluetoothManager : NSObject{
        CBCentralManager *manager;
        id delegate;
    }
     
    manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    manager.delegate = self;

    扫描外设

    初始化完成,我们就可以让管理器开始扫描外设了:

    1
    [manager scanForPeripheralsWithServices:nil options:nil];

    在设备发现周围外设的advertisementData后,会回调这个方法:

    1
    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;

    其中参数central指回调这个方法的中心设备,peripheral指发现的外设对象CBPeripheral,advertisementData就是前面说的字典类型广播数据,RSSI是当前外设的信号强度,单位是dbm。

    刚开始对陌生的蓝牙设备调试时,建议我们先用一个数组NSArray将所扫描到的外设进行保存:

    1
    [_peripherals addObject:peripheral];

    连接外设

    保存后,可根据设备的UUID来确定该设备是否为我们需要进行操作的蓝牙设备,在确认外设身份后,即可发起对外设的连接操作:

    1
    2
    3
    4
    5
    6
    if ([peripheral.identifier.UUIDString isEqualToString:kPeripheralUUID]) {
        [manager stopScan];
        [manager connectPeripheral:peripheral options:nil];
        NSLog(@"连接外设:%@",peripheral.description);
        self.peripheral = peripheral;
    }

    在此步操作后,我们完成了对蓝牙设备的扫描工作,接下来的回调方法分为两种情况:

    连接到外设后

    1
    2
    3
    4
    5
    6
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
        NSLog(@"已经连接到:%@", peripheral.description);
        peripheral.delegate = self;
        [central stopScan];
        [peripheral discoverServices:nil];
    }

    一旦连接好外设,我们就可以马上停止扫描。然后发起对服务的搜索:

    1
    - (void)discoverServices:(NSArray *)serviceUUIDs;

    此处参数位需要扫描的服务的UUID的数组。文档中特别提到,若该参数为nil,将会扫描所有的服务。

    连接失败后

    在连接外设失败的回调方法中,提供了error参数,可根据实际需要来做异常处理,在此不做过多说明

    1
    2
    3
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
        NSLog(@"连接%@失败",peripheral);
    }

    在搜索到蓝牙设备的服务后,将会回调

    1
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error

    若有错误发生,通过NSError异常处理。

    扫描服务

    由于服务在peripheral里是以NSArray的形式存在的,所以我们要对peripheral中的所有服务进行遍历:

    1
    2
    3
    4
    5
    6
    7
    8
    for (CBService *service in peripheral.services) {
        //发现服务
        if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
            NSLog(@"发现服务:%@", service.UUID);
            [peripheral discoverCharacteristics:nil forService:service];
            break;
        }
    }

    扫描特征值

    在遍历中,趁热打铁,直接对其特征值进行扫描,

    1
    [peripheral discoverCharacteristics:nil forService:service];

    这里与扫描service是相同的,若扫描所有的特征值,直接传入nil作为参数即可。

    这里的kServiceUUID是我们根据蓝牙设备的具体情况,提前设置好的UUID常量。本文所有kXxxxxUUID均为预设的UUID常量。

    同样的,characteristics也是一个数组,我们利用像遍历services一样的方式来遍历所有的特征值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
        if (error) {
            NSLog(@"搜索特征%@时发生错误:%@", service.UUID, [error localizedDescription]);
            return;
        }
        NSLog(@"服务:%@",service.UUID);
        for (CBCharacteristic *characteristic in service.characteristics) {
    //        NSLog(@"特征:%@",characteristic);
            //发现特征
            if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicWriteUUID]]) {
                _writeCharacteristic = characteristic;
            }
            if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicNotifyUUID]]) {
                NSLog(@"监听特征:%@",characteristic);//监听特征
                [self.peripheral setNotifyValue:YES forCharacteristic:characteristic];
                _isConnected = YES;
            }
        }
    }

    特别要提到的是,我们不同的蓝牙设备有不同的服务和特征值。我的蓝牙模块的说明文档中已经说清楚了,write特征、read特征、notify特征,所以在此根据自身需要,来对不同的特征值进行操作。

    设置监听

    我在此要解释一下,当我们试图去读取蓝牙外设发过来的数据时,一定要找准特征值,用这个方法进行订阅。每次特征值变化的时候,就会有回调方法执行,从而达到读取数据的目的。容易出错误,一定分清楚到底哪个特征值该被订阅。

    在订阅了特征值后,我们尝试用蓝牙外设发送一些数据出来,即可回调下一阶段的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
        if (error) {
            NSLog(@"更新特征值%@时发生错误:%@", characteristic.UUID, [error localizedDescription]);
            return;
        }
    // 收到数据
        [delegate didGetDataForString:[self hexadecimalString:characteristic.value]];
    //    NSLog(@"%@",[self hexadecimalString:characteristic.value]);
    }

    数据的转换

    我们接收到的数据,正是characteristic.value,这是一个NSData类数据,我们可以通过UTF8StringEncoding来转化为NSString,为了代码结构清晰,我专门把NSData和NSString互转写成了两个方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //将传入的NSData类型转换成NSString并返回
    - (NSString*)hexadecimalString:(NSData *)data{
        NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        return result;
    }
    //将传入的NSString类型转换成NSData并返回
    - (NSData*)dataWithHexstring:(NSString *)hexstring{
        NSData *aData;
        return aData = [hexstring dataUsingEncoding: NSASCIIStringEncoding];
    }

    在拿到字符串后,通过各种回调方法,处理UI变动。

    封装的 SKBluetooth 的地址:SKBluetooth

  • 相关阅读:
    WHU-1551-Pairs(莫队算法+分块实现)
    JS日历控件 灵活设置: 精确的时分秒.
    java集群优化——多线程下的单例模式
    课程设计——银行系统
    互联网金融,巨头天下还是创业者天堂?
    Android 使用图片异步载入框架Universal Image Loader的问题
    程序C++ to C#交互
    浅谈asp.net通过本机cookie仿百度(google)实现搜索input框自己主动弹出搜索提示
    全栈JavaScript之路(十六)HTML5 HTMLDocument 类型的变化
    推荐安卓开发神器(里面有各种UI特效和实例)
  • 原文地址:https://www.cnblogs.com/weiboyuan/p/6101296.html
Copyright © 2011-2022 走看看