iOS蓝牙开发基础篇

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

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

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

并签订协议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](http://upload-images.jianshu.io/upload_images/2519635-ec4134981dde2308.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

what?

[CoreBluetooth] API MISUSE: 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所持有,
使用时:

iOS蓝牙开发基础篇_第1张图片
屏幕快照 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 *)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 *)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;
    }
    
}
iOS蓝牙开发基础篇_第2张图片
屏幕快照 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);
}
iOS蓝牙开发基础篇_第3张图片
屏幕快照 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];


        
    }
}
iOS蓝牙开发基础篇_第4张图片
![![![屏幕快照 2017-10-11 下午2.02.44.png](http://upload-images.jianshu.io/upload_images/2519635-0fb6dee21f697b05.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](http://upload-images.jianshu.io/upload_images/2519635-721aef62c99396b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](http://upload-images.jianshu.io/upload_images/2519635-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);

}

iOS蓝牙开发基础篇_第5张图片
屏幕快照 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](http://upload-images.jianshu.io/upload_images/2519635-7d6d063c49542973.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
](http://upload-images.jianshu.io/upload_images/2519635-379e65d2cc1e2f9f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
](http://upload-images.jianshu.io/upload_images/2519635-cb9c524dfd1ddb53.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
](http://upload-images.jianshu.io/upload_images/2519635-1a3d5be07e58b248.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
](http://upload-images.jianshu.io/upload_images/2519635-de9772803e8d6909.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
](http://upload-images.jianshu.io/upload_images/2519635-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蓝牙开发基础篇)