iOS蓝牙开发-1-通识

2017-07-11  本文已影响0人  ___吉

个人认为iOS蓝牙开发算是相对偏门的领域,做iOS蓝牙相关的开发也有一段时间了,在此写下蓝牙相关的东西,一则方便日后查阅,二则给后来之人一点参考。
关于蓝牙是什么就不多做解释了,百度一搜一大把。iOS蓝牙开发。蓝牙开发有两种:

    1.手机做中心 - CBCentralManager、外部硬件做外设 - CBPeripheral
    2.外部硬件做中心 - CBCentralManager、手机做外设 - CBPeripheral

一般情况下开发,都是以手机(App)作为中心,硬件设备(智能硬件、手环之类的)作为外设。这里只记录这种情况。
最开始主要有两个概念:服务(CBService)、特征(CBCharacteristic)。
服务:每个设备都会有一些服务,每个服务里面都会有一些特征,特征就是具体键值对,提供数据的地方。每个特征属性分为这么几种:读,写,通知这么几种方式。
特征:读、写数据等。
画了个简图,没有美术天赋。(“、、、”表示重复的省略)


外设-服务-特性结构图.png

刚接触蓝牙的人,估计会不太理解服务和特性是什么东西,我刚刚开始也没搞明白。我最开始接触蓝牙是从大学玩单片机的蓝牙2.0开始的(苹果是4.0),学电子的应该知道。那时候只需要设置AT指令,调用收发数据的API即可。
我的理解是:
【1】服务是用来提供传输数据通道的。比如,一个人体数据监测仪,服务A用于传输心率、服务B用于传输血压、服务C用于传输脑电波。。。嗯,是的,这么打个比方吧。
【2】服务A中(传输心率那个)有:特性1->用于App读取数据、特性2->用于App发送指令或者数据到硬件,特性3->用于读取硬件的mac地址。这样就将这些功能模块(心率、血压、脑电波等)及其接口(读取、发送等)都很好的分开了,我理解成面向对象的解耦。
下面是已手机App作为中心的大致步骤:

-[1]创建管理中心,并设置其代理。

-[2]判断手机蓝牙状态。(是否打开、是否支持、是否授权),这里可以做提示用户打开手机蓝牙之类的。。。

-[3]扫描外设。每次扫描到一个蓝牙外设,都会通过管理中心的代理方法将外设相关信息回调过来(外设名字、信号值、广播等),此时可以自定义一个对象用于保存这个外设。然后将这个模型添加进数组中,利用自定义tableview显示出来,就和网络请求展示数据类似。

-[4]连接外设。

-[5]连接外设成功,寻找需要的服务(比如我只需要监测心率,不需要发脑电波,那就只需要找到监测心率那个服务就好了)。

-[6]寻找到需要的服务后,接下来 -> 寻找需要的特性(读数据、写指令、读mac等需要的特性)。找到的这些特性,都将其作为属性保存起来,后续发送数据等会需要用到。

-[7]订阅读数据的特性。这个特性是这样的:每次硬件传输数据过来时,都会走这里,所以需要订阅它,以便能收到硬件的数据。

-[8]发送自己的指令,注意:每次最多只能发送20个字节。

-[9]有以上步骤,收发数据基本OK了,还有的就是写完善功能:自动连接,断线重连,过滤不需要的设备,按指定mac地址连接等等。

以下是相关代码:

///创建管理中心
    _manager = [[CBCentralManager alloc] initWithDelegate:self
                                                    queue:nil];///默认在主线程中
///初始化数组,用于存放搜索到的外设
    _allPeripheral = [NSMutableArray array];
///开始扫描外设
    [_manager scanForPeripheralsWithServices:nil options:nil];

以下是管理中心代理方法:

#pragma mark - - - - - -管理中心代理 - - - - - - - - -

#pragma mark 【1】监测蓝牙状态
///这里可以做相应处理,当蓝牙状态: 未打开->提示用于打开蓝牙
///                            其他->提示其他
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state){
        case CBManagerStateUnknown:
            NSLog(@"蓝牙-未知");
            break;
        case CBManagerStateUnsupported:
            NSLog(@"蓝牙-不支持");
            break;
        case CBManagerStateUnauthorized:
            NSLog(@"蓝牙-未授权");
            break;
        case CBManagerStatePoweredOff:{///蓝牙关闭
            NSLog(@"蓝牙-已关闭");
        }
            break;
        case CBManagerStateResetting:
            NSLog(@"蓝牙-复位");
            break;
        case CBManagerStatePoweredOn:{///蓝牙打开
            NSLog(@"蓝牙-已打开");
        }
            break;
    }
}

#pragma mark 【2】发现外部设备
- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI{
    BLEModel *model = [[BLEModel alloc] initWithPeripheral:peripheral rssi:RSSI advertisementData:advertisementData];
    for (BLEModel *saveModel in _allPeripheral) {
        if ([saveModel.peripheral isEqual:peripheral]) {
            return;
        }
    }
    [_allPeripheral addObject:model];
    [_tableView reloadData];
}

#pragma mark 【3】连接外部蓝牙设备
- (void)connectToPeripheral:(CBPeripheral *)peripheral{
    if (!peripheral) {
        return;
    }
    [_manager connectPeripheral:peripheral options:nil];
}

#pragma mark 【4】连接外部蓝牙设备成功
- (void)centralManager:(CBCentralManager *)central
  didConnectPeripheral:(CBPeripheral *)peripheral{
    ///连接成功
    NSLog(@"连接成功,开始寻找服务和特征");
    [peripheral discoverServices:nil];
}

#pragma mark 【5】连接外部蓝牙设备失败
- (void)centralManager:(CBCentralManager *)central
didFailToConnectPeripheral:(CBPeripheral *)peripheral
                 error:(NSError *)error{
    ///这里可以尝试重连
    [_manager connectPeripheral:peripheral options:nil];
}

#pragma mark 【6】蓝牙外设连接断开,自动重连
- (void)centralManager:(CBCentralManager *)central
didDisconnectPeripheral:(CBPeripheral *)peripheral
                 error:(NSError *)error{
    ///当连接断开时,会走这个回调,可以做重连等
}

以下是外设代理方法:

#pragma mark - - - - - -外设代理 - - - - - - - - - 
#pragma mark 【1】寻找蓝牙服务
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    
    if(error){
        NSLog(@"外围设备寻找服务过程中发生错误,错误信息:%@",error.localizedDescription);
    }
    
    CBUUID *serviceUUID=[CBUUID UUIDWithString:kServiceUUID];
    for (CBService *service in peripheral.services) {
        
        if([service.UUID isEqual:serviceUUID]){
            [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kNotifyUUID],[CBUUID UUIDWithString:kWriteUUID],[CBUUID UUIDWithString:kReadMacUUID]] forService:service];
        }
    }
}

#pragma mark 【2】寻找蓝牙服务中的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    if (error) {//报错直接返回退出
        NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
        return;
    }
    
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyUUID]]){///订阅读数据,执行此行代码,每次收到数据时才会走下面 “【8】直接读取特征值被更新后”
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            self.readCharacteristic = characteristic;
        }
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kWriteUUID]]) {
            ///保存写数据的特征,用于给硬件设备发送数据
            self.writeCharacteristic = characteristic;
        }
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kReadMacUUID]]) {
            ///这个特征,在我目前的项目中是读取蓝牙mac地址的
            self.readMACCharacteristic = characteristic;
        }
    }
    if (_readCharacteristic && _writeCharacteristic) {
        NSLog(@"连接成功");///此时才算真正连接成功,因为此时才有读、写特征,可以正常进行数据交互
    }
}

#pragma mark 【3】直接读取特征值被更新后、即收到订阅的那个特征的数据
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    
    if (error) {
        NSLog(@"更新特征值时发生错误,错误信息:%@",error.localizedDescription);
        return;
    }
    NSLog(@"收到数据 -- %@",characteristic.value);
    ///这里就是处理数据了
}

然后是tableView代理

#pragma mark - tableViewDelegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _allPeripheral.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    BLETableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BLETableViewCell" forIndexPath:indexPath];
    cell.model = _allPeripheral[indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    BLEModel *bleModel = _allPeripheral[indexPath.row];
    bleModel.peripheral.delegate = self;
    [_manager connectPeripheral:bleModel.peripheral options:nil];
}

代码中的几个宏定义:

#pragma mark - 蓝牙服务及属性
#define         kServiceUUID                                        @"0001"
#define         kWriteUUID                                          @"0002"
#define         kNotifyUUID                                         @"0003"
#define         kReadMacUUID                                        @"0004"

为什么是0001、0002等,是有原因的,这里介绍个App,叫LightBlue,可以查看到蓝牙设备的特性。当然这些也可以找硬件工程师拿到,或者自己查DataSheet也可以查到。

IMG_1632.PNG
再介绍个串口调试助手,CoolTerm。能实时调试蓝牙模块。
代码地址:https://github.com/chan106/BLEDemo.git
上一篇下一篇

猜你喜欢

热点阅读