关于ios蓝牙的相关编程,网上又很多教程,在这里,我经过了一段时间的学习,总结出了一些自己理解的东西,纪录在这里,方便自己和其他人看。
现在用到的蓝牙技术又3.0和4.0.两者之间在ios编程中的区别仅仅是4.0版本的蓝牙收发数据报时,传递数据的长度只有20个字节。因此在数据处理的时候,要根据具体情况来调整。首先,我们先以3.0为例。
(本文中仅是一些我用到的方法,可以解决一般的数据收发,如有不足之处,欢迎提出,本文中就不再详细阐述蓝牙中心设备和外围设备的概念了)
首先介绍蓝牙框架中的几个常用类:
1.CBCentralManager
这个是管理本机蓝牙的搜索外围设备,链接,首发数据的类,一个对象代表一个中心设备
2.CBPeripheral
一个该类的对象代表一个外围设备,储存了该外围设备的所有信息
3.CBService
一个该类对象代表一个服务,一个外围设备可以包含多个服务
4.CBCharacteristic
一个该类对象代表一个特征,一般来说特征只有一个读和一个写,特征是用来收发数据的工具。一个服务包含多个特征。
下面我们开始学习如何使用这些类,控制蓝牙设备。
(在教程过程中我添加了一些自己想的小技巧)
1. 首先,我们在使用蓝牙时,最好只是用一个对象来控制整个App的蓝牙,因此,最好创建一个单独的类,并且设置为一个单例。如果不了解单例,请百度单例模式。
<span style="font-size:18px;">@interface BlueToothController : NSObject
//单例
+(BlueToothController*)Instance;
@end</span>
<span style="font-size:18px;">+(BlueToothController*)Instance
{
if (instance == nil) {
instance = [[BlueToothController alloc] init];
}
return instance;
}</span>
<span style="font-size:18px;">static BlueToothController* instance;
</span>
2. 我们还需要让自己写的蓝牙控制类,遵守CBCentralManagerDelegate和CBPeripheralDelegate协议,用来分别控制中心设备和外围设备。
<span style="font-size:18px;">@interface BlueToothController : NSObject <CBCentralManagerDelegate,CBPeripheralDelegate></span>
3. 然后我们需要建立一个中心设备的实例,用来控制本机(中心设备)
//中心设备管理对象
@property (nonatomic, strong) CBCentralManager *centralMgr;
4. 第三步,我们就开始进行蓝牙设备的控制了,首先是检查中心设备也就是本机的蓝牙状态,使用用CBCentralManagerDelegate协议中的centralManagerDidUpdateState方法。在以下例子中,检测蓝牙状态正常的时候,就直接使用scanForPeripheralsWithServices函数进行搜索。
了。
<span style="font-size:18px;">//查询到状态时调用的函数
-(void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSDictionary* dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
switch (central.state)
{
case CBCentralManagerStatePoweredOn:
[self.centralMgr scanForPeripheralsWithServices:nil options:dic];
NSLog(@"开始搜索蓝牙~~~");
break;
default:
NSLog(@"设备存在问题,无法搜索蓝牙~");
break;
}
}</span>
5. 在搜索函数启动后,设备开始启动搜索外围设备,如果搜索到了一个外围设备,就会调用一次协议中的didDiscoverPeripheral方法获取该设备,此项步骤中,我们需要保存外围设备的信息,因此,要创建一个成员变量,用来保存所有的外围设备。
<span style="font-size:18px;">//蓝牙设备列表
@property (nonatomic, strong) NSMutableArray *arrayBLE;</span>
然后写代理方法,将搜索到的外围设备储存到数组中,代码中的BLUENAME 为之前定下的需要连接的设备名字。在保存蓝牙设备信息的时候如果设备名符合,那么调用一个自定义方法ConnectBlueTooth方法来连接蓝牙。自定义方法在下面写出
//处理搜索到的设备
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
BELInfo *discoveredBLEInfo = [[BELInfo alloc] init];
discoveredBLEInfo.discoveredPeripheral = peripheral;
discoveredBLEInfo.rssi = RSSI;
//如果已经连接过了设备,就不再保存了
if (self.arrayBLE.count > 0) {
return;
}
// 储存连接搜索到的蓝牙设备
if (![self saveBLE:discoveredBLEInfo]) {
NSLog(@"蓝牙设备保存连接失败!~~");
};
}
//保存蓝牙设备
-(BOOL)saveBLE:(BELInfo*)info
{
NSLog(@"发现设备%@:%@",info.discoveredPeripheral.name,info.discoveredPeripheral.identifier.UUIDString);
//如果名字为我们需要连接的蓝牙的名字,则连接并且保存返回yes
if ([info.discoveredPeripheral.name isEqualToString:BLUENAME]) {
//连接蓝牙
if (![self ConnectBlueTooth:info.discoveredPeripheral]) {
NSLog(@"蓝牙连接失败!");
return NO;
}
else
{
[self.arrayBLE addObject:info];
NSLog(@"蓝牙连接成功!连接上设备名为:%@,UUID为%@的设备",info.discoveredPeripheral.name,info.discoveredPeripheral.identifier.UUIDString);
}
}
return YES;
}
//连接蓝牙函数
-(BOOL)ConnectBlueTooth:(CBPeripheral*)pheral
{
//连接蓝牙设备
[self.centralMgr connectPeripheral:pheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
//记录连接的蓝牙,并且设置该外围设备协议
self.ConnectPeripheral = pheral;
[pheral setDelegate:self];
return YES;
}
在上述的自定义的连接蓝牙函数中ConnectPerpheral函数是框架库函数给出的,但在连接之后,我们需要保存我们连接上的外围设备实例,因此,我们需要一个成员变量指针。
@property (nonatomic,strong) CBPeripheral* ConnectPeripheral;
以上函数中表示对这个指针的初始化的部分
//记录连接的蓝牙,并且设置该外围设备协议
self.ConnectPeripheral = pheral;
[pheral setDelegate:self];
return YES;
6. 在连接了外围设备之后,会自动回调协议中的
didConnectPeripheral函数,可以在这个函数中调用搜索该设备的相关服务的操作
//连接蓝牙的时候触发的协议函数
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
//获取T100的UUID
CBUUID* MyServerUUID = [CBUUID UUIDWithString:BLUE_SERVER];
//打包成数组
NSArray* uuida = [NSArray arrayWithObject:MyServerUUID];
//搜索该UUID服务
[peripheral discoverServices:uuida];
NSLog(@"开始搜索蓝牙的服务!!");
}
BLUE_SERVER是需要搜索到的设备的服务
7. 如果没有连接到设备,或者设备断开连接,则会调用协议中的
didDisconnectPeripheral函数,我在示例中,让在断开或者没有搜索到设备之后的操作是,继续搜索设备。
//没连上的时候的响应函数
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"蓝牙断开了链接!!开始重新搜索!!");
//清除之前保存的所有信息
[self.arrayBLE removeAllObjects];
SendCharacteristic = nil;
ReadCharacteristic = nil;
MyServer = nil;
NSDictionary* dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
//重新搜索
[self.centralMgr scanForPeripheralsWithServices:nil options:dic];
}
8. 在搜索到服务之后,会回调协议中的
didDiscoverServices函数,然后我们将访问每一个服务,并且搜索这个服务的所有特征。
//搜索服务的时候触发的协议函数
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (!error) {
for (CBService* server in peripheral.services) {
//搜索该服务的特征值
[peripheral discoverCharacteristics:nil forService:server];
NSLog(@"搜索到了一个服务:%@",server.UUID.UUIDString);
}
}
else
{
NSLog(@"搜索服务失败,失败提示:%@",error);
}
}
9. 在搜索到特征后,会回调协议中的
didDiscoverCharacteristicsForService函数,我们将获取约定好的读特征和写特征存储在,再次创建的成员变量中。读特征直接绑定,写特征储存在发数据时使用。
//储存特征值
CBCharacteristic* SendCharacteristic;//读特征
CBCharacteristic* ReadCharacteristic;//写特征
//扫描完每一个特征值之后调用该函数
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (!error) {
//扫描每一个特征
for (CBCharacteristic* character in service.characteristics) {
NSLog(@"搜索到一个特征:%@",character.UUID.UUIDString);
//如果是写的特征,写入发送特征对象中
if ([character.UUID.UUIDString isEqualToString:BLUE_CHARACTERISTIC_WRITE]) {
SendCharacteristic = character;
}
//如果是读特征,写入读特征对象中
if ([character.UUID.UUIDString isEqualToString:BLUE_CHARACTERISTIC_READ]) {
ReadCharacteristic = character;
//绑定监听
[self.ConnectPeripheral setNotifyValue:YES forCharacteristic:character];
}
}
}
else
{
NSLog(@"搜索特征失败,失败提示:%@",error);
}
}
10. 到了这一步,蓝牙连接和准备工作已经全部结束,剩下的就是收发数据的阶段了。收发数据的方法有很多,本例中使用的是发一条数据等到
这条数据处理完之后再发送另一条数据的方式。过程中会用到线程,信号灯相关知识。
首先是发数据:
11. 发数据时只需要调用writeValue函数,但是发送数据需要使用本类单例的对象自己调用,因此,我们自定义一个函数,这个函数会创建一个线程,在线程中发送数据报,发送结束后,将会用信号灯等待,如果再50秒内没有收到信号就自动继续,如果收到信号,就可以发送另一条数据。
<span style="font-size:18px;">//通过特征值发送数据
-(void)SendData:(NSData*)data
{
NSLog(@"开启了一个线程发送数据:%@",data);
NSThread* sendThread = [[NSThread alloc] initWithTarget:self selector:@selector(SenddataByOtherWay:) object:data];
[sendThread start];
}
//发送数据线程
//
-(void)SenddataByOtherWay:(NSData*)data
{
NSLog(@"发出数据:%@",data);
//需要响应的发送数据
if (SendCharacteristic) {
[self.ConnectPeripheral writeValue:data forCharacteristic:SendCharacteristic type:CBCharacteristicWriteWithResponse];
}
//发送完之后,等待处理完成,如果五十秒后,无反馈,继续
[condition waitUntilDate:[[NSDate alloc] initWithTimeIntervalSinceReferenceDate:50]];
}
//</span>
在数据发送之后会回调didWriteValueForCharacteristic函数,我没有做处理,只是把数据显示了一下,在这里就不做说明了。
12. 其次是读数据,再设备收到外围设备反馈回来的数据时,会自动回调
didUpdateValueForCharacteristic函数,这个是在第九步中监听了读特征才会调用的函数。
因为蓝牙收到的数据很多,考虑到可复用性,我们创建一个协议,用来让使用蓝牙的对象,自己定义数据的处理方法。
@protocol BlueToothControllerDelegate <NSObject>
-(void)BlueToothEventWithReadData:(CBPeripheral*)peripheral Data:(NSData*)data;
@end
@property (nonatomic , strong) id<BlueToothControllerDelegate> delegate;
13. 在didUpdateValueForCharacteristic函数中,我们直接从特征值中获取到数据,然后让代理方法去处理,在代理方法处理结束后,发送一个信号灯,让发数据的线程继续工作。
//监听读特征的协议函数
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSData* data = characteristic.value;
NSLog(@"收到了一条数据:%@",data);
//调用协议的函数
if (self.delegate) {
[self.delegate BlueToothEventWithReadData:peripheral Data:data];
}
//处理完成数据后,开启信号灯
[condition signal];
}
以上就是我总结的蓝牙的常用方法。不足之处,欢迎提出。