iOS蓝牙之扫描、链接、读写数据(二)

接上篇iOS蓝牙之扫描、链接、读写数据(一)

一、关于蓝牙从连接到读发数据

在这篇文章中,我会按照上篇文章中介绍的流程,来代码实现:(由于自己也是第一次做蓝牙模块内容,有不对的地方还希望大神指正,小弟不胜感激......)

#import "BLEController.h"
//需要遵守蓝牙协议
#import 

#define kPeripheralName            @"B10001"//硬件设备蓝牙名称
#define kServiceUUID               @"0003CDD0-0000-1000-8000-00805F9B0131" //服务的UUID
#define kWriteCharacteristicUUID   @"0003CDD2-0000-1000-8000-00805F9B0131" //特征的UUID
#define kNotifyCharacteristicUUID  @"0003CDD1-0000-1000-8000-00805F9B0131" //特征的UUID

@interface BLEController ()
@property (nonatomic, strong) CBCentralManager *manager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBCharacteristic *character;
@end

1、创建蓝牙管理者对象

//第二个参数:nil默认为主线程
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

2、该方法当蓝牙状态改变(打开或者关闭)的时候就会调用

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state) {
        case CBManagerStateUnknown:{
            NSLog(@"未知蓝牙状态");
        }
            break;
        case CBManagerStateResetting:{
            NSLog(@"系统服务连接暂时丢失");
        }
            break;
        case CBManagerStateUnsupported:{
            NSLog(@"该设备不支持蓝牙");
        }
            break;
        case CBManagerStateUnauthorized:{
            NSLog(@"该设备蓝牙为被授权");
        }
            break;
        case CBManagerStatePoweredOn:{
            NSLog(@"该设备蓝牙已打开");
            //打开后,开始扫描
            [self scanBlueTooth];
        }
            break;
        case CBManagerStatePoweredOff:{
            NSLog(@"该设备蓝牙没有打开");
        }
            break;
    }
}

3、发现外设后调用的方法

//查到外设后,连接设备,停止扫描
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
    //可在该方法内部区分扫描到的蓝牙设备
    NSLog(@"发现外设peripheral:%@ =====RSSI:%@ =====UUID:%@", peripheral, RSSI, peripheral.identifier);
    
    //判断是否为你要连接的设备(我这里用设备名称判断的,扫描到我要的外设蓝牙名称后,停止扫描、连接外设)
    if ([peripheral.name isEqualToString:kPeripheralName]) {
        //扫描到设备之后停止扫描
        [_manager stopScan];
         //开始连接外设
        [_manager connectPeripheral:peripheral options:nil];
        _peripheral = peripheral;
    }
}

3.1、连接成功会被调用

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"=====>%@连接成功 =====>UUID: %@",peripheral.name,peripheral.identifier);
    //连接设备之后设置蓝牙对象的代理,扫描服务
    [self.peripheral setDelegate:self];

    // 外设发现服务,传nil代表不过滤
    // 这里会触发外设的代理 didDiscoverServices 方法
    [self.peripheral discoverServices:nil];

    //这是我们业务需求:0.2s给我们的采集卡设备发一次命令来读数
    [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(timered:) userInfo:nil repeats:YES];
}
//返回的蓝牙服务通知通过代理实现(已经发现服务)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    NSLog(@"=====>发现服务");
    //一个设备可能为多个服务,所以要取你需要读写的那个服务(我们的外设就一个服务)
    for (CBService *service in peripheral.services) {
        if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
            
            //根据你要的那个服务去发现特性
            [self.peripheral discoverCharacteristics:nil forService:service];
        }
    }
}

3.2、连接失败会被调用

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"=====>连接失败");
}

3.3、断开连接会被调用

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"=====>断开连接");
    //断开连接后,可能是因为信号不好,所以我们的需求是继续连接我们的设备
    [self scanBlueTooth];
}

4、获得外围设备的服务、获得服务的特征

//已经发现特性
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    //首先读到外设特征服务,写到宏定义
    NSLog(@"发现特征服务:%@",service.UUID);
    
    for (CBCharacteristic *c in service.characteristics) {
        //其次读到两个特征值,写到宏定义
        NSLog(@"特征UUID: %@",c.UUID);
        
        //哪个特征值是读的,哪个特征值是写的,一般硬件兄弟都会告诉你的
        //(即使没有告诉你,也可以自己在AppStore下载LightBlue或UsrBleAssistent蓝牙调试工具看到你需要读写的特征值)
        if ([c.UUID.UUIDString isEqualToString:kWriteCharacteristicUUID]) {
            //找到可写特征值D2
            _character = c;
            
        }else if ([c.UUID.UUIDString isEqualToString:kNotifyCharacteristicUUID]){
            //设置通知
            [_peripheral setNotifyValue:YES forCharacteristic:c];
        }
    }
}

5、从外围设备读数据

//获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (!error) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyCharacteristicUUID]]) {
            // 此处的byte数组就是接收到的数据
            NSString *str = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
            NSLog(@"原始数据:%@===>字符串:%@",characteristic.value,str);
        }
    }else{
        NSLog(@"error:%@", error);
    }
}

6、给蓝牙发送数据(及前边定时器的实现)

#pragma mark - 定时器时间事件
- (void)Timered:(NSTimer*)timer {
    //读信号强度
    [_peripheral readRSSI];
    //实时读数
    [self writeDataToBle:@"*APP.VALUE#$"];
}

//向蓝牙写入数
- (void)writeDataToBle:(NSString *)aStr{
    if (_character.properties & CBCharacteristicPropertyWrite) {
        //BLE设备接收的数据需要转为NSData类型
        NSData *data = [aStr dataUsingEncoding:NSUTF8StringEncoding];
        //第一个参数:你要写入的数据,为data类型。
        //第二个参数:你要向哪个特征写入,一般就是属性为写入的那个特征。
        //第三个参数:你写入的方式,这个参数有两个枚举分别是:
        //CBCharacteristicWriteWithResponse//写入之后有回应
        //CBCharacteristicWriteWithoutResponse//写入之后不需要回应
        //使用WithResponse的成功后就会调用:-(void)peripheral:(CBPeripheral )peripheral didWriteValueForCharacteristic:(CBCharacteristic )characteristic error:(NSError )error;
        [_peripheral writeValue:data forCharacteristic:_character type:CBCharacteristicWriteWithResponse];
    }else{
        NSLog(@"该字段不可写!");
    }
}

到此BLE4.0从扫描到读数,已经实现

二、关于蓝牙你需要知道

1、一般蓝牙的读写通道(Characteristics)都是分开的,少数蓝牙会读写用同一个通道,在发现目标通道后,我们要利用写通道来向蓝牙写入数据,用读通道来读取蓝牙发来的包。

2、对于外设的BLE的唯一标识,亲测同一设备的 UUID 对于每台iOS设备都不一样,只能尽量保证设备的唯一性;特别是自动重连的过程,让用户没有感知
(在此工程中,我是根据外设的名字来判断链接的!因为我们的BLE外设名称都是以公司名称字母开头的!)

3、关于BLE的Mac:在开发过程中发现CBCentralManager 和 CBPeripheral 里边都找不到和Mac地址相关的字段,所以如果领导要外设的Mac,你大胆的告诉他:你无能为力!
(但有个解决办法:你可以与硬件工程师商量,能否将BLE的Mac地址写在服务里,这样可以为自动连接的唯一性做准备)

4、关于Read还是Notify的区别:
比如说我现在连接的是一个骑行设备上的蓝牙,此设备上有一个照明灯,现在APP可以控制这个灯,我发送开启的命令之后,如果开启成功那么设备会返回给我一个开启成功的信息,此时就是读取。
而通知呢就是:APP需要实时显示该设备的速度,我不需要向设备发送任何的指令,只要它的速度发生了变化,就会向APP发送此时的速度信息(关于区别这块内容是百度其他作者的,不知对不对)

当然关于BLE的实现也有好多优秀的三方库实现:比如 BabyBluetooth
BabyBluetooth是一个最简单易用的蓝牙库,基于CoreBluetooth的封装,并兼容iOS和Mac OS X。
BabyBluetooth的优点:
1、基于原生CoreBluetooth框架封装的轻量级的开源库,可以帮你更简单地使用CoreBluetooth API。
2、CoreBluetooth所有方法都是通过委托完成,代码冗余且顺序凌乱。BabyBluetooth使用block方法,可以重新按照功能和顺序组织代码,并提供许多方法减少蓝牙开发过程中的代码量。
3、链式方法体,代码更简洁、优雅。
4、通过channel切换区分委托调用,并方便切换
5、完善的文档,且项目处于活跃状态,不断的更新中
6、github上star最多的纯Bluetooch类库(非PhoneGap和SensorTag项目)

你可能感兴趣的:(iOS蓝牙之扫描、链接、读写数据(二))