iOS蓝牙的使用

一、结构图

iOS蓝牙的使用_第1张图片
Paste_Image.png

二、服务和特征

每个设备都会有一些服务,每个服务里面都会有一些特征,特征就是具体键值对,提供数据的地方。每个特征属性分为这么几种:读,写,通知这么几种方式。

外设、服务、特征间的关系

iOS蓝牙的使用_第2张图片
Paste_Image.png

蓝牙中心模式流程

  1. 建立中心角色
  2. 扫描外设(discover)
  3. 连接外设(connect)
  4. 扫描外设中的服务和特征(discover)
    • 4.1 获取外设的services
    • 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值
  5. 与外设做数据交互(explore and interact)
  6. 订阅Characteristic的通知
  7. 断开连接(disconnect)

蓝牙外设模式流程

  1. 启动一个Peripheral管理对象
  2. 本地Peripheral设置服务,特性,描述,权限等等
  3. Peripheral发送广告
  4. 设置处理订阅、取消订阅、读characteristic、写characteristic的委托方法

三、创建Peripheral

3.1 导入头文件、设置代理、设置宏
//这里的uuid是通过mac终端生成的
#define kServiceUUID        @"51BE80A3-9A24-4777-8180-3DBA54A0786A" //服务的UUID
#define kCharacteristicUUID @"ABA43AD0-6DA8-44C5-ABA5-9301092B8B79" //特征的UUID

@interface ViewController ()
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) CBMutableCharacteristic *customCharacteristic;
@property (nonatomic, strong) CBMutableService *customService;
@property (strong,nonatomic) NSMutableArray *centralM;
@property (weak, nonatomic) IBOutlet UITextView *lof;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    // ① 创建Peripheral Manager
    self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
}

3.2 调用代理方法

//判断当前设备蓝牙是否可用,如果可用,调用setupService方法
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    NSLog(@"%ld",(long)peripheral.state);
    switch (peripheral.state) {
        case CBPeripheralManagerStatePoweredOn:
            [self setupService];
            break;
        case CBPeripheralManagerStateResetting:
            NSLog(@"CBPeripheralManagerStateResetting");
            break;
        case CBPeripheralManagerStateUnsupported:
            NSLog(@"CBPeripheralManagerStateUnsupported");
            break;
        default:
            NSLog(@"Peripheral Manager did change state");
            break;
        }
}

//创建Serivice和Characteristic 并添加到Peripheral
- (void)setupService{
    NSLog(@"setupService!!");
    [self writeToLog:@"init setupService..."];
    //特征值
    CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID];
    // 这里 properties 和 permissions 代表着不同的权限 按照业务需要添加 比如 读写操作等
    self.customCharacteristic = [[CBMutableCharacteristic alloc] initWithType: characteristicUUID properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsWriteable | CBAttributePermissionsReadable];
    
    CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
    self.customService = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
    
    [self.customService setCharacteristics:@[self.customCharacteristic]];
    
    [self.peripheralManager addService:self.customService];
}

当调用addService方法的时候,会调用代理方法 didAddService

- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
    [self writeToLog:@"init didAddService..."];
    NSLog(@"init didAddService");
    if (error == nil) {
        // 外围设备开始广播服务 这里指定ServiceUUID用于中心设备找到他
    [self.peripheralManager startAdvertising:
        @{ CBAdvertisementDataLocalNameKey : kPeripheralName, CBAdvertisementDataServiceUUIDsKey :@[[CBUUID UUIDWithString:kServiceUUID]] }];
    }
}

开启广播后,会调用另外一个代理方法 peripheralManagerDidStartAdvertising

-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
    
    if(error){
        NSLog(@"error====%@",error);
    }else{
        [self writeToLog:@"init peripheralManagerDidStartAdvertising..."];
        NSLog(@"init peripheralManagerDidStartAdvertising");
    }
}

此时,就等待central设备连接,如果连接成功,会调用代理方法 didSubscribeToCharacteristic

-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
    
    [self updateCharacteristicValue];
    if (![self.centralM containsObject:central]) {
        [self.centralM addObject:central];
    }
#     [self writeToLog:@"init didSubscribeToCharacteristic"];
    NSLog(@"init didSubscribeToCharacteristic");
}

如果central想要对我们的characteristic进行读写操作,会调用下面的代理方法,这里会有权限设置,需要在前面创建的时候指定

-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
    NSLog(@"didReceiveWriteRequests");
    CBATTRequest *request = requests[0];
    //判断是否有写数据的权限
    
    if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
        //需要转换成CBMutableCharacteristic对象才能进行写值
        CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
        c.value = request.value;
        
        NSString *dataStr = [[NSString alloc]initWithData:request.value encoding:NSUTF8StringEncoding];
        
        [self writeToLog:[NSString stringWithFormat:@"收到central发来的数据%@",dataStr]];
        
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    }else{
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
    
}

-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
    NSLog(@"didReceiveReadRequest");
    //判断是否有读数据的权限
    if (request.characteristic.properties & CBCharacteristicPropertyRead) {
        NSData *data = request.characteristic.value;
        [request setValue:data];
        //对请求作出成功响应
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    }else{
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}

四、创建central

4.1导入头文件,创建对象,设置代理
#import "ClientViewController.h"
#import 

@interface ClientViewController () 
@property (nonatomic, strong) CBCentralManager *manager;
@end
- (void)viewDidLoad {  
    [super viewDidLoad];  
    self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
4.2调用代理方法 CBCentralManagerDelegate(主要用于连接)
//初始化的时候回调用此代理方法检查蓝牙状态
- (void)centralManagerDidUpdateState: (CBCentralManager *)central {
    switch (central.state) {
        case CBCentralManagerStatePoweredOn:
            // Scans for  peripheral with uuid
            NSLog(@"自动搜索!");
            [self writeToLog:@"正在搜索..."];
            [self.manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
            break;
        case CBCentralManagerStatePoweredOff:
            NSLog(@"蓝牙没有打开,请先打开蓝牙");
            break;
        case CBCentralManagerStateUnsupported:
            NSLog(@"当前设备不支持蓝牙");
            break;
        default:
            NSLog(@"Central Manager did change state");
        break;
    }
}

如果蓝牙可用,就开始扫描设备 通过下面方法,这里的kServiceUUID是在Peripheral端定义好的,用于找我们需要的service

[self.manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];

如果找到了对于的服务,就会调用代理方法didDiscoverPeripheral

    -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
    NSLog(@"init didDiscoverPeripheral");
    [self writeToLog:@"发现peripheral..."];
    
    if (self.peripheral != peripheral) {
        self.peripheral = peripheral;
        NSLog(@"Connecting to peripheral %@", peripheral); 
        // 这里尝试连接我们发现的 peripheral
        [self.manager connectPeripheral:peripheral options:nil];
    }
}

连接成功和连接失败都会调用对应的代理方法

-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"init didFailToConnectPeripheral");
    [self writeToLog:@"连接peripheral失败..."];
}

-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"init didConnectPeripheral");
    // Stops scanning for peripheral
    [self.manager stopScan];
    [self writeToLog:@"连接peripheral成功..."];
    
    // Clears the data that we may already have
    [self.data setLength:0]; 
    
    // Sets the peripheral delegate
    [self.peripheral setDelegate:self];
    
    // Asks the peripheral to discover the service
    [self.peripheral discoverServices:@[ [CBUUID UUIDWithString:kServiceUUID] ]];
}

连接成功后,根据对于的UUID去寻找对于的服务,这里的UUID在Peripheral端定义好的,如果发现服务后会调用didDiscoverServices方法

4.3调用代理方法 CBPeripheralDelegate(主要用于和peripheral进行交互)
- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverServices:(NSError *)error {
  if (error) {
      NSLog(@"Error discovering service: %@", [error localizedDescription]);
//        [self cleanup];
      return;
  }
  for (CBService *service in aPeripheral.services) {
      NSLog(@"Service found with UUID: %@",service.UUID);
      // Discovers the characteristics for a given service
      // 这里可以对特定service 找到特定characteristics 
      if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
          [self writeToLog:[NSString stringWithFormat:@"发现service,service.UUID:%@",service.UUID]];
          [self.peripheral discoverCharacteristics:@[[CBUUID UUIDWithString: kCharacteristicUUID]] forService:service];
      }
  }
}

如果找到对于的charcateristic的话,会调用对于代理方法,可以在这里面进行处理

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService: (CBService *)service error:(NSError *)error {
  if (error) {
      NSLog(@"Error discovering characteristic:%@", [error localizedDescription]);
//        [self cleanup];
            return;
      }
  if ([service.UUID isEqual:[CBUUID UUIDWithString: kServiceUUID]]) {
      for (CBCharacteristic *characteristic in service.characteristics) {
          NSLog(@"遍历服务!");
          [self writeToLog:@"遍历characteristics..."];
              if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]]) {
                  self.characteristic = characteristic;
                  //1.调用此方法会调用didUpdateNotificationStateForCharacteristic 这个代理方法
                  [peripheral setNotifyValue:YES forCharacteristic:characteristic];
                  //2.或者不调用代理方法 直接读取
//                    [peripheral readValueForCharacteristic:characteristic];
//                    if (characteristic.value) {
//                        NSString *value = [[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
//                    }
          }
      }
  }
}

如果我们用方式1 对我们的characteristic 进行处理的话,会调用对应代理方法didUpdateNotificationStateForCharacteristic

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic: (CBCharacteristic *)characteristic error:(NSError *)error {
  if (error) {
      NSLog(@"Error changing notification state:%@", error.localizedDescription);
  }
   // Exits if it's not the transfer characteristic
   if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]]) {
       return;
  }
   // Notification has started
  if (characteristic.isNotifying) {
      [self writeToLog:@"监听characteristic的变化..."];
          NSLog(@"Notification began on %@", characteristic);
          // 这里我们进行readValueForCharacteristic 操作,会调用对于的代理方法didUpdateValueForCharacteristic
          [peripheral readValueForCharacteristic:characteristic];
     } else { // Notification has stopped
          // so disconnect from the peripheral
          NSLog(@"Notification stopped on %@. Disconnecting", characteristic);
         [self.manager cancelPeripheralConnection:self.peripheral];
    }
}

// 在这个方法中我们能拿到由peripheral传来的特定characteristic
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
  NSString *dataStr = [[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
  [self writeToLog:[NSString stringWithFormat:@"读取characteristic.value = %@",dataStr]];
  NSLog(@"characteristic===%@",characteristic);
}
4.4传递数据给peripheral
- (IBAction)writeData:(id)sender {
  NSString *valueStr =[NSString stringWithFormat:@"UgenCentral--%@",[NSDate date]];
  NSData *value = [valueStr dataUsingEncoding:NSUTF8StringEncoding];
  NSLog(@"self.characteristic===%@",self.characteristic);
  //通过下面这个方法可以给peripheral更新特性 如果type 选择withResponse的话,还会调用下面的代理方法
  [self.peripheral writeValue:value forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
  [self writeToLog:[NSString stringWithFormat:@"central端更新特性:%@",valueStr]];
}

//会告诉你是否成功 
//我们刚刚穿的数据可以在 第三节的 didReceiveWriteRequests代理方法中找到
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
  if (error) {
      NSLog(@"error wtrting characteristic value: %@",[error localizedDescription]);
  }
  [self writeToLog:@"成功给外围设备写数据"];
}

参考代码 https://git.oschina.net/jxjxwujie/IOS_bluetooth.git

你可能感兴趣的:(iOS蓝牙的使用)