1.什么是蓝牙 4.0 ?
全新的蓝牙4.0技术并不是一种技术,而是由传统蓝牙,高速蓝牙和低功耗蓝牙合而为一。并且这三种蓝牙可以组合使用,也可以单独使用。其中,低功耗蓝牙即BLE是蓝牙4.0的核心规范。仅传统蓝牙是不需要App的,比如市面上大多数蓝牙耳机,只要连上电脑或者手机,在电脑或者手机设备上播放音乐,系统底层就可以通过传统蓝牙协议,将音频数据传到蓝牙音箱上播放。换句话说,就是传统蓝牙部分在App上不可控,在iOS上,更是连判断在设置中是否连接了传统蓝牙都办不到,iOS可控的部分只有BLE。如果想要实现像小米手环那样的功能,在手机上有来电和短信,或者其他软件信息提醒的时候,在你的蓝牙设备上也能接收到。那这个功能的实现需要用到ANCS(Apple Notification Center Service)。ANCS在可以看成一个超级权限,但却跟传统蓝牙工作在不同局域,一个是通知,一个是音频。具体的实现,我们都不得而知,这部分属于苹果系统的功能了。
iOS蓝牙每次最多接收155字节的数据,安卓5.0以下最大接收20字节,5.0以上可以更改最大接收量,能达到500多字节
2.蓝牙开发关键词
中心设备(central):就是用来扫描周围蓝牙硬件的设备,比如通过你手机的蓝牙来扫描并连接智能手环,这时候你的手机就是中心设备。
外设(peripheral):被扫描的设备。比如当你用手机的蓝牙扫描连接智能手环的时候,智能手环就是外设。
服务(services):外设广播和运行的时候会有服务,可以理解成一个功能模块,中心设备可以读取服务。外设可以有多个服务。
特征(characteristic):在服务中的一个单位,一个服务可以有多个特征,特征会有一个value,一般读写的数据就是这个value。
3.实现iOS蓝牙中心设备
项目中自己可写个单例类来简易地封装下蓝牙相关代码,保证项目中只存在一个中心管理者,其它类只需调用相关方法即可
1.创建一个中心管理者
@property (nonatomic, strong) CBCentralManager *centralManager;
// 创建中心设备管理器,会回调centralManagerDidUpdateState
self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
2.扫描外设
//两个参数为nil,默认扫描所有的外设,可以设置一些服务,进行过滤搜索
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
//扫描指定设备(注:要在后台进行扫描第一个参数必须不能为nil,设为nil时不会触发扫描到设备的回调,同时需外设广播出自己的SeviceUUID)
NSArray *services = @[[CBUUID UUIDWithString:kBleServiceID]];
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
[self.centralManager scanForPeripheralsWithServices:services options:dictionary];
3.当扫描到外设时,会触发以下代理方法
//这里默认扫到MI,主动连接,当然也可以手动触发连接
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"扫描连接外设:%@ %@",peripheral.name,RSSI);
if ([peripheral.name hasSuffix:@"MI"]) {
//保存外设,并停止扫描,达到节电效果
thePerpher = peripheral;
[self.centralManager stopScan];
//进行连接
[self.centralManager connectPeripheral:peripheral options:nil];
}
}
4.当连接到外设时,会调用以下代理方法
//连接外设成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"连接外设成功!%@",peripheral.name);
[peripheral setDelegate:self];
//扫描服务
[peripheral discoverServices:nil];
}
//连接外设失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"连接到外设 失败!%@ %@",[peripheral name],[error localizedDescription]);
}
5.当扫描到外设服务时,会调用以下代理方法,然后继续扫描对应服务的特征
//扫描到服务
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if (error)
{
NSLog(@"扫描外设服务出错:%@-> %@", peripheral.name, [error localizedDescription]);
return;
}
NSLog(@"扫描到外设服务:%@ -> %@",peripheral.name,peripheral.services);
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
NSLog(@"开始扫描外设服务的特征 %@...",peripheral.name);
}
6.当扫描到服务的特征时,会调用以下代理方法
//扫描到特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error)
{
NSLog(@"扫描外设的特征失败!%@->%@-> %@",peripheral.name,service.UUID, [error localizedDescription]);
return;
}
NSLog(@"扫描到外设服务特征有:%@->%@->%@",peripheral.name,service.UUID,service.characteristics);
//获取Characteristic的值
for (CBCharacteristic *characteristic in service.characteristics){
//这里外设需要订阅特征的通知,否则无法收到外设发送过来的数据
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
//当我们定义好每个特征是干什么用的,我们需要读取这个特征的值,当特征值更新了会调用
//- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error方法
//需要说明的是UUID是硬件定义好给你,如果硬件也是个新手,那你可以先打印出所有的UUID,找出有用的
[peripheral readValueForCharacteristic:characteristic];
}
}
7.当接收到数据和读取到数据时,会调用以下代理方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
if (error) {
NSLog(@"读取数据失败!%@-> %@",peripheral.name, [error localizedDescription]);
return;
}
NSLog(@"%@ %@",characteristic.UUID.UUIDString,characteristic.value);
}
8.发送数据
//最后一个参数,可能有两种,如果你的程序出现连接正常,而你确定执行了写数据的函数,但是硬件确实没有反应,那很有可能是你的CBCharacteristicWriteType不对,需要使用另一种,一共是两种CBCharacteristicWriteWithResponse和CBCharacteristicWriteWithoutResponse
[thePerpher writeValue:itemData forCharacteristic:theCharacteristic type:CBCharacteristicWriteWithoutResponse];
9.断开蓝牙
[self.centralManager cancelPeripheralConnection: thePerpher];
//蓝牙断开后会调用以下代理方法
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
}
10.- retrieveConnectedPeripheralsWithServices:根据制定的服务,找到当前系统处于连接状态的蓝牙中包含这个服务的所有蓝牙对象
NSArray *devices = [self.centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:kBleShortServiceID],[CBUUID UUIDWithString:kBleServiceID]]];