iOS基于CoreBlutooth的蓝牙开发

首先如同网络一般,蓝牙也类似的可以分服务端和客户端,CoreBlutooth中有两个角色,一个是Peripheral,相当于服务器端,也就是我们打开手机蓝牙搜索时找到的各个设备;一个是Central,相当于客户端,也就是我们使用的手机,这两个角色和一般的手机使用主权引起的角色意识有点冲突。

蓝牙通信过程中有几个部分,一个是Service,即服务器提供的服务,一个服务端可能有一个或者多个Service,还有一个是Characteristic,即特征值,相当于沟通过程中的字段,服务端和客户端可以针对指定特征值进行数据交流。

因为没有蓝牙模块和蓝牙设备,所以我是在MAC端编写了Peripheral端,手机上运行Central端(蓝牙程序必须真机调试

Peripheral端

#import <CoreBluetooth/CoreBluetooth.h>

#define TRANSFER_SERVICE_UUID @"0bd51666-e7cb-469b-8e4d-2742f1ba77cc"	//服务的UUID
#define TRANSFER_CHARACTERISTIC_UUID @"1bd51666-e7cb-469b-8e4d-2742f1ba77dd"	//读取端口的特征值UUID
#define TRANSFER_CHARACTERISTIC1_UUID @"2bd51666-e7cb-469b-8e4d-2742f1ba77dd"	//写入端口的特征值UUID

@interface ViewController ()<CBPeripheralManagerDelegate>

@property (nonatomic,strong) CBPeripheralManager *peripheral;	//peripheral管理者

@property (nonatomic,strong) CBMutableCharacteristic *transferCharacteristic;	//读取端口特征值

@property (nonatomic,strong) CBMutableCharacteristic *transferCharacteristic1;	//写入端口特征值
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //按钮     用于更新字段值
    NSButton *button = [NSButton buttonWithTitle:@"dianji" target:self action:@selector(btnClick)];
    
    button.frame = NSRectFromCGRect(CGRectMake(10, 10, 100, 50));
    [self.view addSubview:button];
    
    self.peripheral = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];	//初始化,并设置代理
    
    CBMutableService *transferService = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES];	//构建服务
    // 构建两个特征值 参数Type:特征值UUID,properties:端口属性,通知、读、写、不带回应写入等等,value:特征值,如果要设置,特征值须为readonly,permissions:读写权限
    self.transferCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID] properties: CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
    // properties很重要,如果没有设置读属性,central调用read方法时会被禁止
    self.transferCharacteristic1 = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC1_UUID] properties: CBCharacteristicPropertyNotify |  CBCharacteristicPropertyWrite | CBCharacteristicPropertyWriteWithoutResponse value:nil permissions: CBAttributePermissionsReadEncryptionRequired | CBAttributePermissionsWriteEncryptionRequired];
    // 为服务添加特征值
    transferService.characteristics = @[self.transferCharacteristic,self.transferCharacteristic1];
    [self.peripheral addService:transferService];	//为peripheral添加服务
    [self.peripheral startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]}];           //开启广播
}

// 必须的代理,状态更新
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    NSLog(@"%ld",(long)peripheral.state);
}

// 接收到写入请求后的代理
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests{
    CBATTRequest *request = requests[0];
    NSLog(@"-----%d",[self.peripheral updateValue:request.value forCharacteristic:self.transferCharacteristic1 onSubscribedCentrals:nil]);
}
// 接收到读取请求后的代理
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
    NSLog(@"read");
}



- (void)btnClick{
    //    [self.peripheral updateValue:[NSData dataWithBytes:"Hello world" length:11] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil];
    
    //    self.transferCharacteristic.value = [NSData dataWithBytes:"Hello World" length:11];
    [self.peripheral updateValue:[NSData dataWithBytes:"hello World" length:11] forCharacteristic:self.transferCharacteristic1 onSubscribedCentrals:nil];	//更新指定特征值端口的值,并不会改变characteristic.value
    [self.peripheral updateValue:[NSData dataWithBytes:"hello" length:11] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil];
    NSLog(@"%@",[[NSString alloc]initWithData:self.transferCharacteristic1.value encoding:NSUTF8StringEncoding]);
    
}

- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];
    
    // Update the view, if already loaded.
}


@end


上面是服务端的代码,需要注意的是如果是自己开发蓝牙模块的话,相关service的UUID以及characteristic的UUID可以和模块开发人员商定,如果是手环或者其他商用成品的话,商家应该会提供相关UUID。

Central端

//
//  ViewController.m
//  testDD
//
//  Created by 周小伟 on 2017/7/7.
//  Copyright © 2017年 周小伟. All rights reserved.
//

#import "ViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>
// 三个特征值必须和服务端一样,不然找不到指定特征值
#define TRANSFER_SERVICE_UUID @"0bd51666-e7cb-469b-8e4d-2742f1ba77cc"
#define TRANSFER_CHARACTERISTIC_UUID @"1bd51666-e7cb-469b-8e4d-2742f1ba77dd"
#define TRANSFER_CHARACTERISTIC1_UUID @"2bd51666-e7cb-469b-8e4d-2742f1ba77dd"

@interface ViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate>

@property (nonatomic,strong) NSMutableArray *peripherArr;	//用于存储已经发现的设备

@property (nonatomic,strong) CBCentralManager *manager;		//central管理者

@property (nonatomic,strong) CBPeripheral *peripheral;		//当前连接的peripheral

@property (nonatomic,strong) CBCharacteristic *char1;		//读取的特征值端口

@property (nonatomic,strong) CBCharacteristic *char2;		//写入的特征值端口
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];	//初始化管理者
    //CBCentralManager非当前循环创建,如果不用强指针引用,出了应用域就会被释放,运行会报错
}

// central管理者代理,必须调用 判断蓝牙状态,如果状态为蓝牙打开的话,开始扫描
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    if (central.state == CBManagerStatePoweredOn) {
        [self.manager scanForPeripheralsWithServices:nil options:nil];
    }
}

// 扫描的代理方法 在停止扫描之前会不断调用
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
    if (!peripheral.name) {
        return;	//过滤没有名字的设备
    }
    [self.manager stopScan];	//停止扫描,也可以在连接设备后停止扫描
    
    //因为附近只有一个设备,所以没有对设备进行判断,实际上可以添加到数组中保存,顺便添加强引用
    [central connectPeripheral:peripheral options:nil];//连接设备,如果有必要可以针对设备属性进行判断,然后连接,比如设备名字,UUID等
    self.peripheral = peripheral;//只为了添加强引用,一般不这么写
    self.peripheral.delegate = self;//设置代理
}

//管理者代理,连接到设备后调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ [peripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]];//查找服务,参数如果设置为nil为查找所有服务
}

//peripheral代理,发现没用的服务时调用
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices{ NSLog(@"---- %@",invalidatedServices);
}

//发现服务后调用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    NSArray *services = peripheral.services;
    //获取设备的所有服务,下面的处理只因为服务端我只有一个service,所以没有进行处理,实际上一个设备会有多个service,需要遍历分别处置
    if (services.count) {
        CBService *service = services[0];
        CBUUID *writeUUID = [CBUUID UUIDWithString: TRANSFER_CHARACTERISTIC1_UUID];
        CBUUID *readUUID = [CBUUID UUIDWithString: TRANSFER_CHARACTERISTIC_UUID];
        [peripheral discoverCharacteristics:@[writeUUID, readUUID] forService:service];//查找指定特征值
    }
}

//peripheral代理,发现特征值时调用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    if (!error) {
        NSArray *characteristicArray = service.characteristics;//获取service的特征值,实际上一个service可能有多个特征值,需要自己处理,比如针对特征值的properties进行处理
        if(characteristicArray.count > 1){
            //因为读写操作是针对特征值进行的,所以要取得两个特征值
            CBCharacteristic *readCharacteristic = characteristicArray[0];
            CBCharacteristic *writeCharacteristic = characteristicArray[1];
            self.char1 = readCharacteristic;
            self.char2 = writeCharacteristic;
            
            // 通知使能, `YES` enable notification only, `NO` disable notifications and indications
            //[peripheral setNotifyValue:YES forCharacteristic:readCharacteristic];
            // [peripheral setNotifyValue:YES forCharacteristic:writeCharacteristic]; //为指定特征值添加通知,需要特征值的properties设置了notify属性才能设置,一般前面会加入判断 if(writeCharacteristic.properties & CBCharacteristicPropertyNotify)
            // [peripheral readValueForCharacteristic:readCharacteristic];//读取数据,需要特征值的properties设置了read属性,判断方法类似上一句// [peripheral readValueForCharacteristic:writeCharacteristic];
        }
        NSLog(@"Hello ");
    } else {
        NSLog(@"Discover Charactertics Error : %@", error);
    }
}
// peripheral代理,设置过notify属性的特征值的值发生变化后会调用这个方法,比如我的demo里面的写入特征值我设置了notify和write,然后写入后因为值改变了就会调用这个代理方法,在peripheral为特征值设置setNotifyValue:forCharacteristic:时就会调用这个方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (error) {
        NSLog(@"%@",characteristic);
        NSLog(@"%@",error.userInfo);
        return;
    }
    CBCharacteristicProperties properties = characteristic.properties;
    if (properties) {
        NSLog(@"%@",characteristic.value);
    }
}

// peripheral代理方法,写入操作后调用需要写入时设置type为withResponse才会调用
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (!error) {
        NSLog(@"Write Success");
    }else{
        NSLog(@"WriteValue Error = %@",error.userInfo);
    }
}

// peripheral代理方法,指定特征值的value值变化了后调用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (error) {
        NSLog(@"update value error:%@",error.userInfo);
    }else{
        NSData *responseData = characteristic.value;
        NSLog(@"||||||||||%@",[[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding]);
        NSLog(@"%@",[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding]);
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    static int i = 0;
    NSString *str = [NSString stringWithFormat:@"Hello %d",i];
    [self.peripheral writeValue:[NSData dataWithBytes:str.UTF8String length:strlen(str.UTF8String)]forCharacteristic:self.char2 type:CBCharacteristicWriteWithResponse];//写入操作,参数type,必须要withResponse才会有代理回调,如果要设置withoutResponse需要相关特征值设置了一样的属性
    NSLog(@"%ld",[self.peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse]);
    i++;
    // [self.peripheral readValueForCharacteristic:self.char1];//读取操作
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.
}
@end

主要的蓝牙交互过程就类似于上面所示,但实际上沟通过程并没有达到理想效果,比如写入操作只能进行一次,我并不知道是否因为设备的原因,如果有机会可以找到其他设备再来尝试。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值