技术资讯

IOS开发:IOS与蓝牙交互开发实战

TIME:2018-09-04

最近刚做了一个蓝牙开发的项目,要求通过蓝牙接收数据和写入数据,下面将蓝牙开发的流程做一个简单的介绍。

概念理解

开发蓝牙之前,通过看看官方文档和网上查看资料了解了蓝牙开发的大体流程,首先得理解关于蓝牙开发中的一些概念。我们都是通过基于蓝牙4.0的低功耗蓝牙进行开发,开发用到的框架是CoreBluetooth。下面解释几个概念:

中心者模式和管理者模式

中心者模式:这个使用的很普遍,就是手机作为主机,蓝牙作为外设。
管理者模式:我们手机自己作为外设,自己创建服务和特征,然后有其他的设备连接我们的手机。

中心CBCentral和外设CBPeripheral

BLE中的交互涉及两个重要角色:中心CBCentral和外围设备(简称外设)CBPeripheral。外设通常具有其他设备所需要的数据,中心通常使用外设提供的信息来实现特定的功能。

服务CBService和特征CBCharacteristic

每个外设CBPeripheral中包含一个或者多个服务CBService以及有关其连接信号的信息。服务是实现一个函数或者功能的设备的数据采集和相关行为的集合。每个服务下面又有很多特征CBCharacteristic,特征提供了这些服务的详细内容。我们在进行BLE蓝牙开发的时候,就需要通过特定服务下面的特定特征来获取想要的数据和内容,特征是与外界交互的最小单位。

UUID

关于UUID,暂时可以理解为蓝牙的唯一标识,每个服务和特征都会有相应的UUID,可以通过UUID来代表特定的服务和特征。

步骤:

  1. 导入头文件#import <CoreBluetooth/CoreBluetooth.h>
  2. 遵守协议CBCentralManagerDelegate,CBPeripheralDelegate
  3. 建立中心管理者CBCentralManager对蓝牙进行管理
  4. 扫描外设,使用方法scanForPeripheralsWithServices
  5. 连接扫描到的外设
  6. 扫描并获取外设的服务CBService
  7. 扫描并获取外射服务下面的特征CBCharacteristic
  8. 获取特征的值,即从外围设备读取数据,用didUpdateValueForCharacteristic方法
  9. 订略特征的通知,实现当连接蓝牙成功之后以通知的方式实现对特征里面数据的读写。需要调用- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic方法
  10. 写入数据,调用writeValue:forCharacteristic:type:方法

示例代码

  1. 建立中心管理者和外设
/** 主设备(可以扫描和链接外设备) */ @property (nonatomic, strong) CBCentralManager * centeralManger; /** 外设备 */ @property (nonatomic, strong) CBPeripheral * peripheral; /** 用于保存被发现的设备 */ @property (nonatomic, strong) NSMutableArray * discoverPeripherals;

2.初始化中心管理者并设置委托

// 初始化并设置委托和线程 self.centeralManger = [[CBCentralManager alloc]initWithDelegate:self queue:nil]; self.discoverPeripherals = [[NSMutableArray alloc]init];

将queue的参数设置为nil,默认就是在住线程中执行。

3.初始化中心管理者之后,用代理方法监听主设备状态的改变,当检测到蓝牙打开以后,调用扫描外设的方法:

#pragma mark - CBCentralManagerDelegate /**
 * 主设备状态改变
 */ - (void)centralManagerDidUpdateState:(CBCentralManager *)central{ switch (central.state) { case CBManagerStateUnknown: NSLog(@">>>CBManagerStateUnknown"); break; case CBManagerStateResetting: NSLog(@">>>CBManagerStateResetting"); case CBManagerStateUnsupported: NSLog(@">>>CBManagerStateUnsupported"); case CBManagerStateUnauthorized: NSLog(@">>>CBManagerStateUnauthorized"); case CBManagerStatePoweredOff: NSLog(@">>>CBManagerStatePoweredOff"); //            [SVProgressHUD setBackgroundColor:[UIColor yellowColor]]; [SVProgressHUD showErrorWithStatus:@"请打开手机蓝牙开关!"]; break; case CBManagerStatePoweredOn: NSLog(@">>>CBManagerStatePoweredOn"); // 开始扫描外围设备 /**
             * 第一个参数为nil就是扫描周围所有的外设,扫描后会进入
             */ [self.centeralManger scanForPeripheralsWithServices:nil options:nil]; break; default: break;
    }
}
  1. 在扫描到外设的代理方法里面持有外设
// 扫描到设备会进入方法 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
    
    [SVProgressHUD dismiss]; NSLog(@"扫描到设备名字:%@", peripheral.name); for (CBPeripheral * thePeripheral in self.discoverPeripherals) { if ([thePeripheral.name isEqual:peripheral.name]) return;
        
    } if (peripheral) { //找到设备必须持有他,否者CBCentralManager也不会保存peripheral [self.discoverPeripherals addObject:peripheral]; NSLog(@"discoverPeripherals - %@", self.discoverPeripherals);
        
        [self.BLEtableView reloadData]; // 如果在这停止扫描 那么就每次就只能扫描到一个外设备 //        [self.centeralManger stopScan]; }else{
        
        [SVProgressHUD showErrorWithStatus:@"没有扫描到蓝牙设备!"];
    }   
}

5.用代理方法实现连接蓝牙,在连接成功的方法里面实现读取数据的通知,并设置外设的代理

// 连接成功 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ NSLog(@">>>连接到名称为(%@)的设备 - 成功", peripheral.name);
    
    [SVProgressHUD showSuccessWithStatus:@"连接成功!"];
    
    
    peripheral.delegate = self; //扫描外设备的services [peripheral discoverServices:nil]; //发送通知 [self notifyCharacteristic:peripheral characteristic:self.characteristic];

    [self.centeralManger stopScan];
} // 连接到peripherals - 失败 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);
    [SVProgressHUD showErrorWithStatus:@"连接失败!"];
} // peripherals连接断开 - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@">>>外设连接断开连接 %@: %@\n", [peripheral name], [error localizedDescription]);
    
    [SVProgressHUD showErrorWithStatus:@"连接断开!"];
}

6.用外设的代理方法扫描服务和特征

#pragma mark  - CBPeripheralDelegate // 扫描到services - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ if (error) { NSLog(@"扫描失败原因 - %@", error.localizedDescription); return;
    } NSLog(@"扫描到服务 - %@", peripheral.services); // 扫描每个服务的特征 for (CBService * service in peripheral.services) { NSLog(@"service 的 UUID : %@", service.UUID); // 扫描每个service的characteristics [peripheral discoverCharacteristics:nil forService:service];
        
    }
} // 扫描到characteristics - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{ if (error) { NSLog(@"error Discovered characteristics for %@ with error:%@", service.UUID, error.localizedFailureReason); return;
    } for (CBCharacteristic * characteristics in service.characteristics) { NSLog(@"services:%@ 的 characteristics:%@", service.UUID, characteristics.UUID);
        
        
    } // 获取characteristics的值 for (CBCharacteristic * characteristics in service.characteristics) {
        
        [peripheral readValueForCharacteristic:characteristics];
        
    } // 搜索characteristics的description for (CBCharacteristic * characteristics in service.characteristics) {
        
        [peripheral discoverDescriptorsForCharacteristic:characteristics];
        
        [peripheral setNotifyValue:YES forCharacteristic:characteristics];
    }
    
    
}

7.读取特征的值(即读取数据)

// 获取characteristics的值 - 从外围设备读数据 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ NSLog(@"characteristic 的 uuid:%@--value:%@",characteristic.UUID,characteristic.value); if (error) { NSLog(@"Error changing notification state: %@", [error localizedDescription]);
    } self.characteristic = characteristic;   
}

8.实现写入数据的方法

// 写数据 - 将数据写入特征 - (void)writeCharactereristic:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic value:(NSData *)value{ NSLog(@"------------%lu", (unsigned long)characteristic.properties); //只有 characteristic.properties 有write的权限才可以写 if(characteristic.properties & CBCharacteristicPropertyWrite){ /*
         最好一个type参数可以为CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,区别是是否会有反馈
         */ [peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
    }else{ NSLog(@"该字段不可写!");
    }
    
}

8 征订通知和取消通知

// 6.订阅特征的通知 //设置通知 -(void)notifyCharacteristic:(CBPeripheral *)peripheral
             characteristic:(CBCharacteristic *)characteristic{ //设置通知,数据通知会进入:didUpdateValueForCharacteristic方法 [peripheral setNotifyValue:YES forCharacteristic:characteristic];
    
} //取消通知 -(void)cancelNotifyCharacteristic:(CBPeripheral *)peripheral
                   characteristic:(CBCharacteristic *)characteristic{
    
    [peripheral setNotifyValue:NO forCharacteristic:characteristic];
}

如果将扫描到的蓝牙用tebleView来显示,就会得到如下效果:



上一篇

网页设计:vue.js响应式原理解析与实现

下一篇

软件开发:大型零售企业的数据库优化案例