iOS蓝牙开发有三个框架
1.GameKit.framework:iOS7之前的蓝牙通讯框架,从iOS7开始过期。
2.MultipeerConnectivity.framework:iOS7开始引入的新的蓝牙通讯开发框架,用于取代GameKit。
3.CoreBluetooth.framework:功能强大的蓝牙开发框架,要求设备必须支持蓝牙4.0。目前蓝牙开发多半基于此框架,此笔记也是写基于此框架。
蓝牙开发有两种模式:
1.手机作为中心设备,获取外设的数据
2.手机作为外设,对外提供数据(这里不做讨论)
本文记录的是利用tableView展示所有扫描到的外设,点击某行时连接对应的外设进行数据交互
实现流程如下:
1.导入蓝牙库,宏定义服务UUID、读数据UUID、写数据UUID
#import <CoreBluetooth/CoreBluetooth.h>
#define kServiceUUID @"1234"//服务UUID
#define kReadUUID @"1236"//读数据UUID
#define kWriteUUID @"1235"//写数据UUID
@property (nonatomic, strong) CBPeripheral *peripheral; //外设
@property (nonatomic, strong) CBCentralManager *myCentralManager; //管理中心
@property (nonatomic, strong) CBCharacteristic *readCharacteristic; //读取数据特性
@property (nonatomic, strong) CBCharacteristic *writeCharacteristic; //写数据特性
2.创建蓝牙管理中心
_myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];//如果设置为nil,默认在主线程中跑。设置了代理为self,记得遵守协议
3.监测蓝牙状态,如果蓝牙开启则扫描外设
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state){
case CBCentralManagerStateUnknown:
break;
case CBCentralManagerStateUnsupported:
JCLog(@"模拟器不支持蓝牙调试");
break;
case CBCentralManagerStateUnauthorized:
break;
case CBCentralManagerStatePoweredOff:{
JCLog(@"蓝牙处于关闭状态");
}
break;
case CBCentralManagerStateResetting:
break;
case CBCentralManagerStatePoweredOn:{
JCLog(@"蓝牙已开启");
//开始扫描外设
[_myCentralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:[NSNumber numberWithBool:NO]}];
}
break;
}
}
4.实现代理方法。当扫描到外部设备时会进入 发现外设 的代理方法
- (void)centralManager:(CBCentralManager *)central //管理中心
didDiscoverPeripheral:(CBPeripheral *)peripheral //发现的外设
advertisementData:(NSDictionary *)advertisementData //外设中的广播数据
RSSI:(NSNumber *)RSSI //外设信号强度
{
// JCLog(@"蓝牙广播数据------>>%@",advertisementData);
//每次扫描出一个外设都会进入此代理方法,在此方法中可以将外设添加到外设数组中,在添加进数组之前,判断一下数组中是否已经添加过这个外设,如果添加过,则不再添加。
if(![_allperipherals containsObject:peripheral]){
[_allperipherals addObject:peripheral];
//添加了新的外设进去,刷新tableview
[_tableView reloadData];
}
5.连接外设
//在选中某行cell的代理方法中,调用连接蓝牙外设的方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//需要连接的外设必须强引用,否则出了此方法作用域后会被释放掉,导致蓝牙断开连接,控制台输出错误信息
_peripheral = _allperipherals[indexPath.row];
JCLog(@"连接蓝牙:%@",_peripheral.name);
[_myCentralManager connectPeripheral:_peripheral options:nil];//连接蓝牙
}
6.蓝牙连接成功,会调用代理方法
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
// JCLog(@"成功连接%@",peripheral.name);
_peripheral = peripheral;
peripheral.delegate = self;
//开始寻找外设服务
[peripheral discoverServices:nil];
/*
关于外设服务是什么的理解:
每个蓝牙4.0的设备都是通过服务和特征来展示自己的,一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征。
特征是与外界交互的最小单位。比如说,一台蓝牙4.0设备,用特征A来描述自己的出厂信息,用特征B来与收发数据等。
此处应用时可以借助一个叫:LightBlue的App来理解蓝牙外设的服务及特征值。
最近做的一个蓝牙项目中,硬件工程师选择的蓝牙模块有提供开发文档,文档中有提供 服务值 及 读取数据 和 写数据 的特征值。按照文档中提供的对应的值填写即可。借助LightBlue也可以看到服务值和读写数据特征值。具体值,参考步骤1中的三个宏定义。
理解不是很到位,请见谅。
*/
}
连接外部蓝牙设备失败调用此方法,可在此再次连接外设
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
//如何实现自动断线重连,就是在断开的委托方法中,执行连接蓝牙的方法 可以在此处重新调用连接蓝牙方法
// JCLog(@"连接%@失败--error:%@",peripheral.name,error);
[_myCentralManager connectPeripheral:_peripheral options:nil];//连接蓝牙
}
7.发现蓝牙外设服务,调用此方法
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if(error){
JCLog(@"外围设备寻找服务过程中发生错误,错误信息:%@",error.localizedDescription);
}
//遍历查找到的服务
CBUUID *serviceUUID=[CBUUID UUIDWithString:kServiceUUID];
for (CBService *service in peripheral.services) {
if([service.UUID isEqual:serviceUUID]){
//外围设备查找指定服务中的特征
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kReadUUID],[CBUUID UUIDWithString:kWriteUUID]] forService:service];
}
}
}
8.寻找蓝牙服务中的特性
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if (error) {//报错直接返回退出
JCLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
return;
}
for (CBCharacteristic *characteristic in service.characteristics)//遍历服务中的所有特性
{
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kReadUUID]]){//找到收数据特性
[peripheral setNotifyValue:YES forCharacteristic:characteristic];//订阅其特性(这个特性只有订阅方式)
_readCharacteristic = characteristic;
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kWriteUUID]]) {//找到发数据特性
_writeCharacteristic = characteristic;
}
}
}
9.收到外设更新发送过来的数据
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
JCLog(@"更新特征值时发生错误,错误信息:%@",error.localizedDescription);
return;
}
NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
JCLog(@"读取到特征值:%@",value);//即收到的数据
}
10.发送数据到外设
-(void)sendString:(NSString *)string{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[_eripheral writeValue:data
forCharacteristic:_writeCharacteristic
type:CBCharacteristicWriteWithoutResponse];
}
/*======至此,蓝牙收发数据的流程基本结束======*/
/*
注意点:
1.发送到外设的数据最大为20字节
2.接收的数据也为20字节
3.MAC端蓝牙调试助手推荐使用CoolTerm,相当于windows中的串口调试助手
4.iPhone手机端调试,推荐LightBlue
*/
/*
关于服务UUID,特征值UUID的解释
1.此文介绍的蓝牙开发是固定的服务UUID、固定的特征值UUID。即已获知项目中使用的外设蓝牙模块的服务UUID、特征值UUID,这两个数据可以在硬件工程师那里拿到,蓝牙模块文档中一般会提及。如果没有,则使用上文中提到的调试工具LightBlue可以获取得到。
2.最近开发项目中的服务UUID、特征值UUID都是使用LightBlue获知。
*/
如有错误之处,望看到此文的大神批评指正,谢谢!
网友评论