iOS开发蓝牙功能主要分扫描中心和外设设备
Central: 中心设备,发起蓝牙连接的设备(一般指手机)
Peripheral: 外设,被蓝牙连接的设备(一般是运动手环/蓝牙模块)
Service:服务,每个设备会提供服务,一个设备有很多服务
Characteristic:特征,每个服务中包含很多个特征,这些特征的权限一般分为:读(read)/写(write)/通知(notice)几种,可以通过特征进行读写数据(重要角色)(中心设备写入数据的时候一定要找到可写入特征才可以写入成功)
Descriptor:描述者,每个特征可以对应一个或者多个描述者,用于描述特征的信息或者属性
准备工作,iOS设备,接入#import <CoreBluetooth/CoreBluetooth.h>,一台手机作为中心设备,一台手机作为外设(或者蓝牙设备硬件),外安装一个蓝牙调试助手作为辅助工具,查找那些特征是可写,可读等等。
以下是流程代码
1.中心设备开启扫描,调用即可[self mCentral];
- (CBCentralManager *)mCentral
{
if (!_mCentral) {
_mCentral = [[CBCentralManager alloc] initWithDelegate:self
queue:dispatch_get_main_queue()
options:nil];
}
return _mCentral;
}
2.中心设备初始化后,调用以下代理方法(必须实现的)
//2、只要中心管理者初始化,就会触发此代理方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state) {
case CBManagerStateUnknown:
NSLog(@"CBCentralManagerStateUnknown");
break;
case CBManagerStateResetting:
NSLog(@"CBCentralManagerStateResetting");
break;
case CBManagerStateUnsupported:
NSLog(@"CBCentralManagerStateUnsupported");
break;
case CBManagerStateUnauthorized:
NSLog(@"CBCentralManagerStateUnauthorized");
break;
case CBManagerStatePoweredOff:
NSLog(@"CBCentralManagerStatePoweredOff");
break;
case CBManagerStatePoweredOn:
{
NSLog(@"CBCentralManagerStatePoweredOn");
//3、搜索外设
[self.mCentral scanForPeripheralsWithServices:nil // 通过某些服务筛选外设
options:nil]; // dict,条件
}
break;
default:
break;
}
}
3.发现外部设备后(蓝牙设备)的回调方法
//4、发现外设后调用的方法
- (void)centralManager:(CBCentralManager *)central // 中心管理者
didDiscoverPeripheral:(CBPeripheral *)peripheral // 外设
advertisementData:(NSDictionary *)advertisementData // 外设携带的数据
RSSI:(NSNumber *)RSSI // 外设发出的蓝牙信号强度
{
if (peripheral.name) {
[self.dataArr addObject:peripheral];
}
[self.tableView reloadData];
//(ABS(RSSI.integerValue) > 35)
//5、发现完之后就是进行连接
if([peripheral.name isEqualToString:mBLEName]){
self.mPeripheral = peripheral;
self.mPeripheral.delegate = self;
[self.mCentral connectPeripheral:peripheral options:nil];
}
}
4.确定要连接哪个蓝牙设备(我这里用的是tableView显示发现的外部蓝牙设备,所以在这个代理方法里面点击具体某个设备即可)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CBPeripheral *per = self.dataArr[indexPath.row];
self.mPeripheral = per;
self.mPeripheral.delegate = self;
[self.mCentral connectPeripheral:per options:nil];
}
5.中心设备链接外部设备成功,失败,丢失的回调方法,如果需要自动重连,可以失败,丢失接口里面重新调用连接
//6、中心管理者连接外设成功
- (void)centralManager:(CBCentralManager *)central // 中心管理者
didConnectPeripheral:(CBPeripheral *)peripheral // 外设
{
NSLog(@"%@==设备连接成功", peripheral.name);
//7、外设发现服务,传nil代表不过滤
[self.mPeripheral discoverServices:nil];
}
// 外设连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"设备连接失败==%@", peripheral.name);
}
// 丢失连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"设备丢失连接==%@", peripheral.name);
}
6.连接蓝牙成功以后实现以下回调,查找我们需要的特征,跟进具体某个特征是否可写,可读,进行记录,后续作为写入和读取数据使用(查询是否可写可读时可以手机安装一个蓝牙调试助手查看)
//8、发现外设的服务后调用的方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
// 是否获取失败
if (error) {
NSLog(@"备获取服务失败==%@", peripheral.name);
return;
}
for (CBService *service in peripheral.services) {
self.mService = service;
NSLog(@"发现外设的服务后调用的方法 设备的服务==%@,UUID==%@,count==%lu",service,service.UUID,peripheral.services.count);
//9、外设发现特征
[peripheral discoverCharacteristics:nil forService:service];
}
}
//10、发现外设特征的时候调用的代理方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
//这里面校验哪些特征值是可写或者可读 ,这里可以使用手机端搜索蓝牙调试助手,查看哪些特征服务可写
for (CBCharacteristic *cha in service.characteristics) {
if([cha.UUID.UUIDString isEqualToString:@"8667556C-9A37-4C91-84ED-54EE27D90049"]){//找到可写的特征
self.mCharacteristic = cha;
NSLog(@"从服务中发现外设特征的时候调用的代理方法设备的服务==%@,服务对应的特征值==%@,UUID==%@---%@,",service,cha,cha.UUID,cha.UUID.UUIDString);
//11、获取特征对应的描述 会回调didUpdateValueForDescriptor
[peripheral discoverDescriptorsForCharacteristic:cha];
//12、获取特征的值 会回调didUpdateValueForCharacteristic
[peripheral readValueForCharacteristic:cha];
//打开外设的通知,否则无法接受数据
[peripheral setNotifyValue:YES forCharacteristic:self.mCharacteristic];
}
}
}
//13、更新描述值的时候会调用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error
{
NSLog(@"描述==%@",descriptor.description);
}
//14、更新特征值的时候调用,可以理解为获取蓝牙发回的数据 获取外设发来的数据,不论是read和notify,获取数据都从这个方法中读取
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSData* data = characteristic.value;
NSString* value = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"uuid:%@ value : %@",characteristic.UUID,value);
}
//15、通知状态改变时调用
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if(error){
NSLog(@"改变通知状态");
}
}
//16、发现外设的特征的描述数组
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error
{
// 在此处读取描述即可
for (CBDescriptor *descriptor in characteristic.descriptors) {
self.mDescriptor = descriptor;
NSLog(@"发现外设的特征descriptor==%@",descriptor);
}
}
7。写入数据以及写入失败成功回调(写入数据时候的特征一定要是可写的特征,这个上一步那里可以知道)
//17、发送数据
- (void)send{
// 核心代码在这里
[self.mPeripheral writeValue:[@"写数据" dataUsingEncoding:NSUTF8StringEncoding]// 写入的数据
forCharacteristic:self.mCharacteristic // 写给哪个特征(这个特征必须是可写特征才能写入成功,因为一个蓝牙外设可以有很多个服务,很多特征)
type:CBCharacteristicWriteWithResponse];// 通过此响应记录是否成功写入
}
//向peripheral中写入数据后的回调函数
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
NSLog(@"写入失败%@",error);
return;
}
NSLog(@"write value success(写入成功) : %@===%@", characteristic,[[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding]);
}
到此,中心设备相关实现就完成了,如果是跟蓝牙硬件交互,只需要跟硬件相关人员协调写入数据和读取数据的各种格式,以及写入哪个特征等等即可
以下是外部设备相关实现,也就是如果把手机作为外设时候的代码实现
1.定义外设,初始化外设相关名称,代码
@interface NoticePeripheraManagerViewController ()<CBPeripheralManagerDelegate>
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) UILabel *statusL;
@property (strong, nonatomic) NSData *dataToSend;
@property (nonatomic, strong) CBMutableCharacteristic *cumCharacteristic;
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.statusL = [[UILabel alloc] initWithFrame:self.view.bounds];
self.statusL.textColor = [UIColor blackColor];
self.statusL.font = [UIFont systemFontOfSize:18];
self.statusL.numberOfLines = 0;
[self.view addSubview:self.statusL];
/*
和CBCentralManager类似,蓝牙设备打开需要一定时间,打开成功后会进入委托方法
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
模拟器永远也不会得CBPeripheralManagerStatePoweredOn状态
*/
self.peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
// 初始化数据
self.dataToSend = [@"snadjfkhaw加大困难的是咖啡euijferlfmn ksxncjxznvjeajfrnjadnfjasfndsafnjsadkfnjsa" dataUsingEncoding:NSUTF8StringEncoding];
}
//peripheralManager状态改变
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
switch (peripheral.state) {
//在这里判断蓝牙设别的状态 当开启了则可调用 setUp方法(自定义)
case CBManagerStatePoweredOn:
NSLog(@"powered on");
self.statusL.text = [NSString stringWithFormat:@"设备名%@已经打开,可以使用center进行连接",@"ios设备"];
[self setUp];
break;
case CBManagerStatePoweredOff:
NSLog(@"powered off");
self.statusL.text = @"powered off";
break;
default:
break;
}
}
- (void)setUp{
/*
typedef NS_OPTIONS(NSUInteger, CBAttributePermissions) {
CBAttributePermissionsReadable = 0x01, //可读
CBAttributePermissionsWriteable = 0x02, //可写
CBAttributePermissionsReadEncryptionRequired = 0x04, //可读,需要建立安全连接
CBAttributePermissionsWriteEncryptionRequired = 0x08 // //可写,需要建立安全连接
} NS_ENUM_AVAILABLE(10_9, 6_0);
*/
CBMutableCharacteristic *writeReadCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"FF01"] properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite value:nil permissions:CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite];
CBMutableService *service = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"FF66"] primary:YES];
[service setCharacteristics:@[writeReadCharacteristic]];
self.cumCharacteristic = writeReadCharacteristic;
[self.peripheralManager addService:service];
}
//添加了服务回调
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
NSLog(@"添加服务成功开始广播");
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:@"FF66"]],CBAdvertisementDataLocalNameKey:@"ios设备"}];
}
//当中央端连接上了此设备并订阅了特征时会回调 didSubscribeToCharacteristic:
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"当中央端连接上了此设备并订阅了特征时会回调");
[self.peripheralManager updateValue:[@"订阅特征" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic onSubscribedCentrals:nil];
}
//当接收到中央端读的请求时会调用didReceiveReadRequest:
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if (request.characteristic.properties & CBCharacteristicPropertyRead) {
NSData *data = [@"收到读的请求" dataUsingEncoding:NSUTF8StringEncoding];
self.statusL.text = @"收到读的请求";
[request setValue:data];
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
} else {
[self.peripheralManager respondToRequest:request withResult:CBATTErrorReadNotPermitted];
}
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests
{
self.statusL.text = @"收到写的请求";
NSData *res= [[NSString stringWithFormat:@"Hello"] dataUsingEncoding:NSUTF8StringEncoding];
[self.peripheralManager updateValue:res
forCharacteristic:self.cumCharacteristic
onSubscribedCentrals:nil];
}