最近刚做了一个蓝牙开发的项目,要求通过蓝牙接收数据和写入数据,下面将蓝牙开发的流程做一个简单的介绍。
概念理解
开发蓝牙之前,通过看看官方文档和网上查看资料了解了蓝牙开发的大体流程,首先得理解关于蓝牙开发中的一些概念。我们都是通过基于蓝牙4.0的低功耗蓝牙进行开发,开发用到的框架是CoreBluetooth
。下面解释几个概念:
中心者模式和管理者模式
中心者模式:这个使用的很普遍,就是手机作为主机,蓝牙作为外设。
管理者模式:我们手机自己作为外设,自己创建服务和特征,然后有其他的设备连接我们的手机。
中心CBCentral
和外设CBPeripheral
BLE中的交互涉及两个重要角色:中心CBCentral
和外围设备(简称外设)CBPeripheral
。外设通常具有其他设备所需要的数据,中心通常使用外设提供的信息来实现特定的功能。
服务CBService
和特征CBCharacteristic
每个外设CBPeripheral
中包含一个或者多个服务CBService
以及有关其连接信号的信息。服务是实现一个函数或者功能的设备的数据采集和相关行为的集合。每个服务下面又有很多特征CBCharacteristic
,特征提供了这些服务的详细内容。我们在进行BLE蓝牙开发的时候,就需要通过特定服务下面的特定特征来获取想要的数据和内容,特征是与外界交互的最小单位。
UUID
关于UUID,暂时可以理解为蓝牙的唯一标识,每个服务和特征都会有相应的UUID,可以通过UUID来代表特定的服务和特征。
步骤:
- 导入头文件
#import <CoreBluetooth/CoreBluetooth.h>
- 遵守协议
CBCentralManagerDelegate,CBPeripheralDelegate
- 建立中心管理者
CBCentralManager
对蓝牙进行管理 - 扫描外设,使用方法
scanForPeripheralsWithServices
- 连接扫描到的外设
- 扫描并获取外设的服务
CBService
- 扫描并获取外射服务下面的特征
CBCharacteristic
- 获取特征的值,即从外围设备读取数据,用
didUpdateValueForCharacteristic
方法 - 订略特征的通知,实现当连接蓝牙成功之后以通知的方式实现对特征里面数据的读写。需要调用
- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic
方法 - 写入数据,调用
writeValue:forCharacteristic:type:
方法
示例代码
- 建立中心管理者和外设
/** 主设备(可以扫描和链接外设备) */
@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;
}
}
- 在扫描到外设的代理方法里面持有外设
// 扫描到设备会进入方法
- (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来显示,就会得到如下效果:
网友评论