iOS蓝牙开发基础篇

作者: SunshineTeemo | 来源:发表于2017-12-18 16:07 被阅读321次

    最近做的是蓝牙的项目在开发过程中有了很多或深或浅的认识,这里分享给大家,共勉!
    这里的基础篇主要讲一下最基础的iOS蓝牙开发。

    蓝牙连接可以大致分为以下几个步骤
    1.建立一个Central Manager实例进行蓝牙管理
    2.搜索外围设备
    3.连接外围设备
    4.获得外围设备的服务
    5.获得服务的特征
    6.从外围设备读数据
    7.给外围设备发送数据

    第一步:建立中心管理者进行蓝牙管理
    在使用蓝牙的地方导入#import <CoreBluetooth/CoreBluetooth.h>

    并签订协议CBCentralManagerDelegate,CBPeripheralDelegate

    - (void)useBlueTooth
    {
        //初始化
        //CBCentralManager是蓝牙中心的管理类,控制着蓝牙的扫描,连接,蓝牙状态的改变。
        self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:nil];
        //扫描设备
      [self.centralManager scanForPeripheralsWithServices:nil options:nil];
     }
    #pragma mark CBCentralManagerDelegate
    - (void)centralManagerDidUpdateState:(CBCentralManager *)central
    {
        CBManagerState state = central.state;
        NSString *stateString = nil;
        switch(state)
        {
            case CBManagerStateResetting:
                stateString = @"CBManagerStateResetting";
                break;
            case CBManagerStateUnsupported:
                stateString = @"CBManagerStateUnsupported";
                break;
            case CBManagerStateUnauthorized:
                stateString = @"CBManagerStateUnauthorized";
                break;
            case CBManagerStatePoweredOff:
                stateString = @"CBManagerStatePoweredOff";
                break;
            case CBManagerStatePoweredOn:
                stateString = @"CBManagerStatePoweredOn";
                break;
            case CBManagerStateUnknown:
            default:
                stateString = @"CBManagerStateUnknown";
        }
        NSLog(@"蓝牙状态:%@",stateString);
    }
    
    

    打印结果:


    ![屏幕快照 2017-09-29 上午10.10.15.png](https://img.haomeiwen.com/i2519635/ec4134981dde2308.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    what?

    [CoreBluetooth] API MISUSE: <CBCentralManager: 0x170274b80> can only accept this command while in the powered on state
    查阅资料:https://stackoverflow.com/questions/23338767/ios-core-bluetooth-getting-api-misuse-warning
    只有在确定蓝牙打开的情况下,才可以调用扫描的方法
    那下面那就又是什么鬼?

    [CoreBluetooth] XPC connection invalid

    查阅资料:http://www.jianshu.com/p/ec659ffcacfe
    发现创建出的CBCentralManager实例必须被VC所持有,如果是封装出来的类,该实例也必须被VC所持有,
    使用时:

    屏幕快照 2017-09-29 上午10.10.15.png

    从打印结果可以看出现在可以了

    屏幕快照 2017-09-29 上午10.25.37.png

    第二步:搜索外设
    根据上面的试验,要在确认蓝牙连接的情况下,扫描并打印外设

    #pragma mark 扫描外设
    - (void)scan
    {
            [self.centralManager scanForPeripheralsWithServices:nil options:nil];
    
    }
    //扫描到设备会调用
    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
    {
           NSLog(@"peripheral:%@",peripheral);
    
    }
    

    这里扫描外设会看到这种各样的蓝牙外设,还会有自己的笔记本电脑


    屏幕快照 2017-10-12 下午5.09.00.png 屏幕快照 2017-10-12 下午5.09.18.png

    注意:蓝牙连接过的设备扫描不上,需要在设置—蓝牙—忽略该设备

    第三步:蓝牙连接
    扫描到设备后,如果有目标设备,就需要蓝牙连接该设备,但是扫描到的设备很多很多,而且会有重复的,这里可以通过设备的名字来匹配是否是目标设备,然后选择是否连接。一般APP中会让用户自己选择连接哪个手环。

    #pragma mark CBCentralManagerDelegate
    //连接成功的回调
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
    {
        NSLog(@"连接成功peripheral:%@",peripheral);
        
        //连接成功之后寻找服务,传nil会寻找所有服务
        [peripheral discoverServices:nil];
        self.peripheral = peripheral;
        peripheral.delegate = self;
    }
    //连接失败的回调
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
    {
        NSLog(@"连接失败peripheral:%@",peripheral);
    
    }
    //断开连接的回调
    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
    {
        NSLog(@"断开连接peripheral:%@",peripheral);
    }
    //扫描到设备会调用
    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
    {
    //    NSLog(@"peripheral:%@",peripheral);
        //手环测试
        if ([peripheral.name isEqualToString:@"NAME"]) {
            NSLog(@"扫描到peripheral:%@,advertisementData:%@",peripheral,advertisementData);
    
            //发起连接
            [self.centralManager connectPeripheral:peripheral options:nil];
    
            //必须引用要不会报错
            self.peripheral = peripheral;
        }
        
    }
    
    屏幕快照 2017-10-12 下午5.30.24.png

    连接状态的回调也可以清楚的看到,连接成功的话,我们就可以搜索外设的服务;连接失败会回调,可以打印查看为何失败。

    第四步:获得蓝牙的服务
    连接成功后,可以获得蓝牙的服务
    蓝牙的各个服务,可以理解为蓝牙提供的数据分类,特征是具体的各个数据

    #pragma mark CBCentralManagerDelegate
    //连接成功的回调
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
    {
        NSLog(@"连接成功peripheral:%@",peripheral);
        
        //连接成功之后寻找服务,传nil会寻找所有服务
        [peripheral discoverServices:nil];
        self.peripheral = peripheral;
        peripheral.delegate = self;
    }
    
    //发现服务的回调
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    {
        NSLog(@"self.peripheral.services:%@",peripheral.services);
    
        NSLog(@"error:%@",error);
    }
    
    屏幕快照 2017-10-12 下午5.50.16.png

    这里具体的服务定义要看蓝牙协议,其中的UUID是唯一识别服务的。

    第五步:获得蓝牙的特征
    发现服务后我们可以搜索服务下的特征,一般一个服务下都包含多个特征

    //发现服务的回调
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    {
        NSLog(@"self.peripheral.services:%@",peripheral.services);
    
        NSLog(@"error:%@",error);
        if (!error) {
            for (CBService *service in peripheral.services) {
    //            NSLog(@"发现服务serviceUUID:%@", service.UUID.UUIDString);
                    //发现特定服务的特征值
                    [service.peripheral discoverCharacteristics:nil forService:service];
                
            }
            
        }
    }
    //发现特征回调
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
    {
        for (CBCharacteristic *characteristic in service.characteristics) {
              NSLog(@"发现特征:%@",characteristic);
            //订阅
            [self.peripheral setNotifyValue:YES forCharacteristic:characteristic];
    
    
            
        }
    }
    
    ![![![屏幕快照 2017-10-11 下午2.02.44.png](https://img.haomeiwen.com/i2519635/0fb6dee21f697b05.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://img.haomeiwen.com/i2519635/721aef62c99396b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://img.haomeiwen.com/i2519635/5b75d0dd5cb0f488.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    特征也包含唯一识别的UUID

    第六步:获得蓝牙数据,解析蓝牙数据
    无论是read还是notify都是在这个方法获得数据

    //数据接收的回调
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
            //获取订阅特征回复的数据
        NSData *data = characteristic.value;
        //获得数据的字节长度
        NSUInteger dataLength = data.length;
    
        NSLog(@"dataLength:%lu",dataLength);
        NSLog(@"UDID:%@,data:%@",characteristic.UUID,data);
    
    }
    
    
    屏幕快照 2017-10-09 下午4.44.17.png

    这里我们已经获得了蓝牙的部分数据,但是这一串的是个什么鬼?原来蓝牙传过来的数据并不是我们平时的10进制数据,是16进制的数据,我们需要根据蓝牙协议进行解析,如果没有协议,那就看代码吧!跟我一样,哈哈!

    屏幕快照 2017-10-11 下午2.02.44.png

    比如分段计步的蓝牙数据是这样的。每2位数代表一个字节的16进制数据,蓝牙协议规定分段计步是14个字节,这里总共是28位数。

    第七步:蓝牙数据的写入
    这里我们试一下里程、热量是否显示的设置的写
    数据的写入也是要看协议的,如果没有协议,看代码吧。

    //发现特征回调
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
    {
        for (CBCharacteristic *characteristic in service.characteristics) {
            NSLog(@"发现特征:%@",characteristic);
            //订阅
            [self.peripheral setNotifyValue:YES forCharacteristic:characteristic];
    
          ![![![![![![屏幕快照 2017-10-13 上午10.01.12.png](https://img.haomeiwen.com/i2519635/7d6d063c49542973.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    ](https://img.haomeiwen.com/i2519635/379e65d2cc1e2f9f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    ](https://img.haomeiwen.com/i2519635/cb9c524dfd1ddb53.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    ](https://img.haomeiwen.com/i2519635/1a3d5be07e58b248.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    ](https://img.haomeiwen.com/i2519635/de9772803e8d6909.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    ](https://img.haomeiwen.com/i2519635/115bade4a14ca3ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
            NSString *settingString = [NSString stringWithFormat:@"4|%d|%d|%d|0000000", 0, 0,0];
            NSData *sendData = [settingString dataUsingEncoding:NSUTF8StringEncoding];
            //该设置的服务UUID
            CBUUID *timeSyncServiceUUID = [CBUUID UUIDWithString:@"1820"];
            //该特征的UUID
            CBUUID *timeSyncCharacteristicUUID = [CBUUID UUIDWithString:@"6e400003-b5a3-f393-e0a9-e50e24dcca9e"];
            for(CBService *service in self.peripheral.services)
            {
    //            NSLog(@"self.peripheral.services:%@",service);
                if ([service.UUID isEqual: timeSyncServiceUUID]) {
                    for(CBCharacteristic *characteristic in service.characteristics)
                    {
                        if([characteristic.UUID isEqual:timeSyncCharacteristicUUID])
                        {
    //                        NSLog(@"sendData:%@",sendData);
                            //写入数据
                            [peripheral writeValue:sendData forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
                            
                        }
                    }
                }
            }
            
        }
    }
    //是否写入成功的代理
    - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
        if (error) {
            NSLog(@"===写入错误:%@",error);
        }else{
            NSLog(@"===写入成功%@",characteristic);
        }
    }
    
    屏幕快照 2017-10-12 下午6.12.34.png

    蓝牙授权的问题
    根据苹果文档中所说的,APP没有蓝牙授权是无法访问外设数据,然而我发现我们有授权APP,APP依然可以访问外设的数据,为什么?

    屏幕快照 2017-10-13 上午10.01.12.png

    感谢以下博客的作者的分享!

    参考资料
    基础:http://www.cocoachina.com/ios/20150915/13454.html
    实现:http://www.jianshu.com/p/f7a53b3a0fc8
    解析:http://www.jianshu.com/p/1f41e6fe06bf
    http://www.jianshu.com/p/1b3c8fc6995a

    苹果:https://developer.apple.com/bluetooth/
    https://developer.apple.com/hardwaredrivers/BluetoothDesignGuidelines.pdf
    https://learn.adafruit.com/introduction-to-bluetooth-low-energy?view=all
    https://race604.com/gatt-profile-intro/

    相关文章

      网友评论

        本文标题:iOS蓝牙开发基础篇

        本文链接:https://www.haomeiwen.com/subject/xaedwxtx.html