蓝牙开发分为中心者和管理者模式:
1.中心者模式是常用模式(99.99%),即我们的手机作为主机,连接蓝牙外部设备;
2.管理者模式用得比较少,这种模式是手机作为外设,自己创建服务和特征,由其他设备连接手机。
蓝牙开发需要先了解几个概念:
服务 (services) :蓝牙外设对外广播必定会有一个服务,可能有多个,服务下面包含一些特征,服务可以理解成一个模块的窗口;
特征 (characteristic) :存在于服务下面的,一个服务下面可以存在多个特征,特征可以理解成具体实现功能的窗口,一般特征都会有value,也就是特征值,特征是与外界交互的最小单位;
UUID:可以理解成蓝牙上的唯一标识符 (硬件上肯定不是这个意思,但是这样理解便于我们开发) ,为了区别不同的服务和特征,或者给服务和特征取名字,我们就用UUID来代表服务和特征。
蓝牙连接大致分为以下几个步骤:
1.建立一个central Manager实例进行蓝牙管理
2.搜索外围设备
3.连接外围设备
4.获取外围设备的服务
5.获取服务的特征
6.从外围设备读取数据
7.给外围设备发送数据
//导入系统的BLE框架
#import <CoreBluetooth/CoreBluetooth.h>
//遵守2个协议
@interface ViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate>
@property (nonatomic, strong) CBCentralManager *cbManager; //中心管理者
@property (nonatomic, strong) CBPeripheral *peripheral; //连接到的外围设备
@end
初始化中心管理者,并设置代理
_cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
#pragma mark - delegate
//只有中心管理者初始化,就会触发此代理方法,判断手机蓝牙状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
/*
{
CBManagerStateUnknown = 0,
CBManagerStateResetting,
CBManagerStateUnsupported,
CBManagerStateUnauthorized,
CBManagerStatePoweredOff,
CBManagerStatePoweredOn,
}
*/
switch (central.state) {
case 0:
NSLog(@"CBManagerStateUnknown");
break;
case 1:
NSLog(@"CBManagerStateResetting");
break;
case 2:
NSLog(@"CBManagerStateUnsupported");
break;
case 3:
NSLog(@"CBManagerStateUnauthorized");
break;
case 4:
NSLog(@"CBManagerStatePoweredOff");
break;
case 5:
{
NSLog(@"CBManagerStatePoweredOn");//蓝牙已开启
//搜索外设
//services 通过某些服务筛选外设 option 条件
[self.cbManager scanForPeripheralsWithServices:nil options:nil];
//搜索成功之后,会调用找到外设的代理方法
}
break;
default:
break;
}
}
如果手机的蓝牙已经开启,就可以开始搜索外围设备。发现了外围设备,就会调用下面的代理方法
//发现外设
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
//中心管理者 central
//外设 peripheral
//外设携带的数据 advertisementData
//外设发出的蓝牙信号强度 RSSI
NSLog(@"peripheral = %@\n data = %@\n rssi = %@", peripheral, advertisementData, RSSI);
/*小米手环获得的数据
peripheral = <CBPeripheral: 0x1702e1800, identifier = EC3BFC7E-0BAF-41B5-B043-3BA19C2F34D7, name = MI_SCALE, state = disconnected>
data = {
kCBAdvDataIsConnectable = 1;
kCBAdvDataManufacturerData = <5701880f 10ad8a4c>;
kCBAdvDataServiceData = {
181D = <820000e1 07031116 0719>;
};
kCBAdvDataServiceUUIDs = (
181D
);
}
rssi = -62
*/
//加过滤条件
if ([peripheral.name hasPrefix:@"MI"]) {
//此处可以对advertisementData(外设携带的广播数据)进行一些处理
//通过过滤,通常会有一些外设,然后把这些外设存储到一个可变数组中,此处只有一个手环,先按照一个外设进行处理
self.peripheral = peripheral;
//连接外设
[self.cbManager connectPeripheral:self.peripheral options:nil];
}
}
发现外设之后,可以通过一些过滤条件,连接你想连接的外围设备。连接外设,是否连接成功,通过以下代理方法返回。
#pragma mark - 连接外围设备状况
//中心管理者连接外设成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"连接外设成功 name : %@", peripheral.name);
//连接成功之后,可以进行服务和特征的发现
//设置外设的代理
self.peripheral.delegate = self;
//外设发现服务器 nil代表不过滤 这里会触发代理方法(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
[self.peripheral discoverServices:nil];
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"外设连接失败 name : %@", peripheral.name);
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"断开连接 name : %@", peripheral.name);
}
如若外设连接成功,即可进行服务和特征的发现discoverServices
#pragma mark - 获取外设的服务 和 特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
NSLog(@"发现外设服务");
if (error) {
NSLog(@"服务不可用");
}else {
for (CBService *ser in peripheral.services) {
NSLog(@"discover service : %@", ser);
//添加条件,只针对UUID是181D的服务进行特征发现。也可以不加条件,对设备的所有服务进行特征发现,去掉if判断即可。
if ([ser.UUID isEqual:[CBUUID UUIDWithString:@"181D"]]) {
[peripheral discoverCharacteristics:nil forService:ser];
}
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(nonnull CBService *)service error:(nullable NSError *)error {
NSLog(@"发现外设特征");
CBService *finalService = [peripheral.services objectAtIndex:peripheral.services.count -1];
if (service.UUID == NULL || finalService.UUID == NULL) {
return;
}
if (error) {
NSLog(@"特征发现失败!");
}else {
NSLog(@"discover characteristics:%@", service);
for (CBCharacteristic *cbchar in service.characteristics) {
NSLog(@"%@", cbchar);
if ([service.UUID isEqual:[CBUUID UUIDWithString:@"181D"]]) {
if ([cbchar.UUID isEqual:[CBUUID UUIDWithString:@"2A9E"]]) {
[self notification:service.UUID characteristicUUID:cbchar.UUID peripoheral:peripheral];
NSLog(@"2A9E========2A9E");
}else if ([cbchar.UUID isEqual:[CBUUID UUIDWithString:@"2A9D"]]) {
[self notification:service.UUID characteristicUUID:cbchar.UUID peripoheral:peripheral];
NSLog(@"2A9D========2A9D");
}else if ([cbchar.UUID isEqual:[CBUUID UUIDWithString:@"2A9C"]]) {
[self notification:service.UUID characteristicUUID:cbchar.UUID peripoheral:peripheral];
NSLog(@"2A9C========2A9C");
}else {
NSLog(@"没有相关的特征UUID");
}
}else {
NSLog(@"不是181D的服务!");
}
}
}
if ([self compareCBUUID:service.UUID UUID2:finalService.UUID]) {
//全部服务读取完毕时才能使用
// [self.cbManager cancelPeripheralConnection:peripheral];
// NSLog(@"关闭外设连接!!!!");
}
}
- (void)notification:(CBUUID *)serviceUUID characteristicUUID:(CBUUID *)characteristicUUID peripoheral:(CBPeripheral *)peripheral {
CBService *service = [self getServiceFromUUID:serviceUUID peri:peripheral];
if (!service) {
NSLog(@"could not find service");
return;
}
CBCharacteristic *cbChar = [self getCharacteristicFromUUID:characteristicUUID service:service];
if (!cbChar) {
NSLog(@"could not find characteristic");
return;
}
//读取数据,如果数据是不断更新的,则需要用setNotifyValue这个
[peripheral readValueForCharacteristic:cbChar];
// [peripheral setNotifyValue:YES forCharacteristic:cbChar];
NSLog(@"setNotifyValue for charac:%@", cbChar);
}
- (CBService *)getServiceFromUUID:(CBUUID *)UUID peri:(CBPeripheral *)peri {
for (CBService *ser in peri.services) {
if ([self compareCBUUID:ser.UUID UUID2:UUID]) {
return ser;
}
}
return nil;//在这个外设中没有找到相应服务
}
- (BOOL)compareCBUUID:(CBUUID *)UUID UUID2:(CBUUID *)UUID2 {
if ([UUID isEqual:UUID2]) {
return YES;
}else {
return NO;
}
}
- (CBCharacteristic *)getCharacteristicFromUUID:(CBUUID *)UUID service:(CBService *)service {
for (CBCharacteristic * cbChar in service.characteristics) {
if ([self compareCBUUID:cbChar.UUID UUID2:UUID]) {
return cbChar;
}
}
return nil;//在这个服务中没有找到相应特征
}
发现了特征以后,针对某一特征进行数据读取,readValueForCharacteristic读取数据,如果数据是不断变化的,则要用setNotifyValue这个方法。数据读取都通过下面这个方法,获取到。
#pragma mark - 从外设读数据
//蓝牙传过来的数据都要经过这个回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(@"value = %@", characteristic.value);
if (error) {
NSLog(@"读取数据出错了:%@",error);
}else {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2A9E"]]) {
if (characteristic.value || !error) {
//处理数据
}
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2A9D"]]) {
[self writeValueToPeripheraleWithCharac:characteristic];
}
}
}
#pragma mark - 给外围设备发送数据
- (void)writeValueToPeripheraleWithCharac:(CBCharacteristic *)cbChar {
uint8_t val = 1;
NSData *valData = [NSData dataWithBytes:(void *)&val length:sizeof(val)];
[self.peripheral writeValue:valData forCharacteristic:cbChar type:CBCharacteristicWriteWithResponse];
}
以上就是蓝牙通信的一个基本通信传输过程。
参考:http://www.cocoachina.com/ios/20161021/17813.html
如果文章有任何问题,可留言我,进行更改,谢谢!