iOS基于CoreBlutooth的蓝牙开发

来源:互联网 发布:ubuntu如何安装福昕 编辑:程序博客网 时间:2024/05/18 03:45

首先如同网络一般,蓝牙也类似的可以分服务端和客户端,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

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


原创粉丝点击