iOS基础之蓝牙

目录
    1. 蓝牙
       1.1 中央设备(接收数据)常用
       1.2 外围设备(发送数据)很少使用(测试协议时使用)
       1.3 相关类
    2. WiFi
1. 蓝牙

概念

CoreBluetooth蓝牙

    蓝牙开发分为2种:
        1.手机作为中央设备(常用99.99%)连接蓝牙设备;
        2.手机作为外围设备(不常用)连接中央设备。

    蓝牙4.0(现在多数设备已支持)以低功耗著称,又称BLE(Bluetoothlow energy)。
    蓝牙只能支持16进制,且每次传输最多20字节。
    蓝牙的数据交互类似于接口,硬件的service的UUID(相当于主地址)加上characteristic的UUID(相当于后边路径)
    所有可用的iOS设备既可以作为周边(Peripheral)也可以作为中央(Central),但不可以同时既是周边也是中央。
    蓝牙开发最核心的不是代码(代码是固定的),最核心的是协议(一般蓝牙的数据协议都会加密,不加密任何人都可以连接硬件)
    蓝牙设备向 中心设备 发送广播,必定有一个或多个服务,服务包含一个或多个特征(是和外界交互的最小单位)。


历史框架
                 只能用于iOS设备之间的连接(ios7前)
       只能用于iOS设备之间的连接(ios7后)
           可用于第三方蓝牙设备交互,但是蓝牙设备必须经过苹果MFi认证
               目前世界上最流行的蓝牙框架(99%)
相关名词
    Central(中心设备)接收并处理周边设备发送的数据
    Peripheral(周边设备)不断发送数据
    advertising(广播)
    Services(服务)
    Characteristic(特征)
使用步骤
    1.创建Central Manager中心设备管理者
    2.搜索外围设备
    3.连接外围设备
    4.寻找外围设备的某服务中的某特征
    5.从外围设备读取数据/向外围设备发送数据  
    6.断开连接

注意:
  info.plist + 权限
      Privacy - Bluetooth Peripheral Usage Description   App需要访问蓝牙

1.1 中央设备(接收数据)常用

import "YTCentralViewController.h"
#import 


#define ServiceUUID [CBUUID UUIDWithString:@"0000fee7-0000-1000-8000-00805f9b34fb"]
#define WirteCharactUUID [CBUUID UUIDWithString:@"000036f5-0000-1000-8000-00805f9b34fb"]
#define ReadCharactUUID [CBUUID UUIDWithString:@"000036f6-0000-1000-8000-00805f9b34fb"]


@interface YTCentralViewController ()
@property (nonatomic,strong) CBCentralManager *centralManager;      // 创建中心设备管理者(数据接收方)
@property (nonatomic,strong) NSMutableArray *peripheralArr;         // 存放外围设备
@end

@implementation YTCentralViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1.创建中心设备管理者
    self.centralManager=[[CBCentralManager alloc]initWithDelegate:self queue:nil];
}
-(void)dealloc{
    self.centralManager=nil;
}


#pragma mark dele CBCentralManagerDelegate
// 2.蓝牙状态发生改变时调用(必须实现),创建中心设备管理者调用一次
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
    if (@available(iOS 10.0, *)) {
    switch (central.state) {
        case CBManagerStatePoweredOn:{  // 蓝牙已打开
            NSLog(@"蓝牙开启");
            
            // 2.1 扫描外围设备(nil:扫描所有service)
            [self.centralManager scanForPeripheralsWithServices:nil options:nil];
        }
            break;
        case CBManagerStatePoweredOff:{ // 蓝牙未打开
            NSLog(@"蓝牙关闭");
        }
            break;
        case CBManagerStateUnauthorized:{
            NSLog(@"未授权使用蓝牙");
        }
            break;
        case CBManagerStateUnsupported:{
            NSLog(@"不支持蓝牙");
        }
            break;
        case CBManagerStateResetting:{
            NSLog(@"");
        }
            break;
        case CBManagerStateUnknown:{
            NSLog(@"");
        }
            break;
        default:{
            NSLog(@"");
        }
        break;
    }
    }
}
// 3.扫描到外围设备后调用
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
    
    // 3.1 找的唯一对应的设备后停止扫描
    if([peripheral.name isEqualToString:@"device01"]||[peripheral.name isEqualToString:@"iPhone"]){
        // 停止扫描
        [self.centralManager stopScan];
    }else{
        return;
    }
    
    // 添加扫描设备到数组,并联入外围设备
    if(peripheral){
        //
        if(![self.peripheralArr containsObject:peripheral]){
            [self.peripheralArr addObject:peripheral];
        }
        // 3.2 联入外围设备(联接成功后调用didConnectPeripheral)
        [self.centralManager connectPeripheral:peripheral options:nil];
    }
}
// 4.联入外围设备后调用
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{

    // 设置外围设备的dele  CBPeripheralDelegate
    [peripheral setDelegate:self];
    // 外围设备寻找服务
    [peripheral discoverServices:@[ServiceUUID]];
}
// 4.1联入外围设备失败后调用
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    //
    NSLog(@"error connect");
}
/*
// 取消联接后调用
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{}
// 进入后台前调用保存信息?
-(void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)dict{}
  */
    
    
    
    
#pragma mark dele CBPeripheralDelegate
// 5.外围设备寻找到服务后调用
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    if(error){  // 寻找服务失败
        NSLog(@"寻找服务失败");
    }
    
    // 从外围设备的服务中找到对应service和对应charact
    for(CBService *service in peripheral.services){
        if([service.UUID isEqual:ServiceUUID]){
            // 寻找对应特征
            [peripheral discoverCharacteristics:nil forService:service];
        }
    }
}

// 6.寻找到特征时调用
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    if(error){     // 寻找特征出错
        NSLog(@"寻找特征时出错");
    }
    // 找到对应service服务对应的charact特征,
    if([service.UUID isEqual:ServiceUUID]){
        for(CBCharacteristic *charact in service.characteristics){
            if([charact.UUID isEqual:WirteCharactUUID]){    // 写
                
                //
                Byte reg[16];
                reg[0]=0x05;
                reg[1]=0x01;
                reg[2]=0x06;
                
                for(int i=0;i<13;i++){
                    reg[i+3]=0x01;
                }
                
                NSData *data=[NSData dataWithBytes:reg length:16];
                [peripheral writeValue:data forCharacteristic:charact type:CBCharacteristicWriteWithResponse];
                
                /*
                // 更新通知状态:(调用didUpdateNoti,外围设备会收到订阅通知并添加到数据源中--需要发送数据时发送)
                [peripheral setNotifyValue:true forCharacteristic:charact];
                
                 // 或
                 // 读取特征值
                 [peripheral readValueForCharacteristic:charact];
                 if(charact.value){
                 NSLog(@"特征值:%@",[[NSString alloc]initWithData:charact.value encoding:NSUTF8StringEncoding]);
                 }
                 */
            }else if([charact.UUID isEqual:ReadCharactUUID]){   // 读
                // 1:监听通知(收到通知时调用didUpdateNoti,在其中调用2)(用于持续读取)
                [peripheral setNotifyValue:true forCharacteristic:charact];
                
                // 或
                
                // 2:调用didUpdateValueForC从外围设备中读取数据(用于一次读取)
                [peripheral readValueForCharacteristic:charact];
            }
        }
    }
}
// 7.通知状态更新时调用
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if(error){      // 通知状态更新时出错
        NSLog(@"通知状态更新时出错");
        return;
    }
    //
    if([characteristic.UUID isEqual:WirteCharactUUID]){

    }else if([characteristic.UUID isEqual:ReadCharactUUID]){
        
        if(characteristic.isNotifying){ // 正在监听(通知状态为true)
            if(characteristic.properties==CBCharacteristicPropertyNotify){  // 调用setNotifyValue后进入
                NSLog(@"已订阅特征通知。 ");
                return;
            }else if(characteristic.properties==CBCharacteristicPropertyRead){  // 外围设备发送数据后进入
                // 调用didUpdateValueForC从外围设备中读取数据
                [peripheral readValueForCharacteristic:characteristic];
            }
        }else{  // 停止监听(通知状态为false)
            NSLog(@"stop 监听通知");
            // 取消连接
            [_centralManager cancelPeripheralConnection:peripheral];
        }
    }
}

// 8. 从外围设备读取数据(readValueForCharacteristic时会调用)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if(error){
        NSLog(@"读取数据出错");
        return;
    }
    if(characteristic.value){
        NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
        NSLog(@"读取到数据:%@",value);
    }else{          // 无数据
        NSLog(@"未发现特征值");
    }
}





-(NSMutableArray *)peripheralArr{
    if(!_peripheralArr){
        _peripheralArr=[NSMutableArray arrayWithCapacity:10];
    }
    return _peripheralArr;
}

@end

1.2 外围设备(发送数据)

很少使用

    可用来在设备未做好之前测试协议。
#import "YTPeripheralViewController.h"
#import 


#define ServiceUUID [CBUUID UUIDWithString:@"0000fee7-0000-1000-8000-00805f9b34fb"]
#define WriteCharactUUID [CBUUID UUIDWithString:@"000036f5-0000-1000-8000-00805f9b34fb"]
#define ReadCharactUUID [CBUUID UUIDWithString:@"000036f6-0000-1000-8000-00805f9b34fb"]


@interface YTPeripheralViewController ()

@property (nonatomic,strong) CBPeripheralManager *peripheralManager;    // 发射数据管理者(外围设备)
@property (nonatomic,strong) NSMutableArray *centralArr;                // 联入的设备
@end

@implementation YTPeripheralViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1.创建 外围设备管理者(发射数据)
    self.peripheralManager=[[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
}
-(void)dealloc{
    self.peripheralManager=nil;
}


#pragma mark dele
// 2.蓝牙状态发生改变时调用(必须实现)
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    //
    switch (peripheral.state) {
        case CBManagerStatePoweredOn:{  // 蓝牙已打开
            NSLog(@"BLE open");
            
            // 2.1 添加服务
            CBMutableService *serviceM=[[CBMutableService alloc]initWithType:ServiceUUID primary:true];
            // 给服务添加特征
            CBMutableCharacteristic *writeCharaM=[[CBMutableCharacteristic alloc]initWithType:WriteCharactUUID properties:CBCharacteristicPropertyWrite value:nil permissions:CBAttributePermissionsWriteable|CBCharacteristicPropertyNotify];
            CBMutableCharacteristic *readCharaM=[[CBMutableCharacteristic alloc]initWithType:ReadCharactUUID properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable|CBCharacteristicPropertyNotify];
            /*
             CBCharacteristicPropertyBroadcast                                                = 0x01,
             CBCharacteristicPropertyRead                                                     = 0x02,
             CBCharacteristicPropertyWriteWithoutResponse                                     = 0x04,
             CBCharacteristicPropertyWrite                                                    = 0x08,
             CBCharacteristicPropertyNotify                                                   = 0x10,
             CBCharacteristicPropertyIndicate                                                 = 0x20,
             CBCharacteristicPropertyAuthenticatedSignedWrites                                = 0x40,
             CBCharacteristicPropertyExtendedProperties                                       = 0x80,
             CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0)    = 0x100,
             CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0)  = 0x200
             */
            /*
             CBAttributePermissionsReadable                   = 0x01,
             CBAttributePermissionsWriteable                  = 0x02,
             CBAttributePermissionsReadEncryptionRequired     = 0x04,
             CBAttributePermissionsWriteEncryptionRequired    = 0x08
             */
            [serviceM setCharacteristics:@[writeCharaM,readCharaM]];
            // 会调用didAddService方法
            [self.peripheralManager addService:serviceM];
        }
            break;
        case CBManagerStatePoweredOff:{  // 蓝牙未打开
            //
            NSLog(@"未打开蓝牙BLE");
        }
            break;
        default:{
        }
            break;
    }
}
// 3.外围设备添加servie后调用
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
    if(error){      // 添加servie出错
        NSLog(@"错误:%@",error.localizedDescription);
        return;
    }
    
    // 启动广播
    // 调用DidStartAdvertising
    [self.peripheralManager startAdvertising:@{CBAdvertisementDataLocalNameKey:@"外围设备名"}];
}
// 3.1启动广播
-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
    if(error){      // 启动广播出错
        NSLog(@"start error");
        return;
    }
    NSLog(@"开始 广播");
}
// 4.有设备联入后调用
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
    // 添加中心设备到数组
    if(![self.centralArr containsObject:central]){
        [self.centralArr addObject:central];
    }
}
// 4.1有设备取消联入后调用
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{
    NSLog(@"cancel");
}
// 5.收到写数据请求后调用
-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
    CBATTRequest *request = requests.lastObject;
    NSString *content = [[NSString alloc] initWithData:request.value encoding:NSUTF8StringEncoding];
}
// 5.1 收到读数据请求后调用
-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
    //
    Byte reg[16];
    reg[0]=0x05;
    reg[1]=0x02;
    reg[2]=0x01;
    reg[3]=0x00;
    for(int i=0;i<12;i++){
        reg[i+4]=0x00;
    }
    NSData *data=[NSData dataWithBytes:reg length:16];
    request.value = data;
    //    [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
    [peripheral respondToRequest:request withResult:CBATTErrorSuccess];
}
// 6.进入后台存储数据?
-(void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary *)dict{
    //
    NSLog(@"will restore state");
}
// 7.updateValue:forCharacteristic:onSubscribedCentrals失败后再次准备好更新特征值时调用
-(void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral{}

// 懒加载(已联入的中心设备)
-(NSMutableArray *)centralArr{
    if(!_centralArr){
        _centralArr=[NSMutableArray arrayWithCapacity:10];
    }
    return _centralArr;
}
@end

1.3 相关类

    // CBCentralManager 中心设备管理者(用来管理中心设备:扫描/连接 外围设备)
    // 创建        CBCentralManagerDelegate
    // CBCentralManager *centralManager=[[CBCentralManager alloc]initWithDelegate:self queue:nil];
    CBCentralManager *centralManager=[[CBCentralManager alloc]initWithDelegate:self queue:nil options:@{}];
    /*
     CBCentralManagerOptionShowPowerAlertKey            @(BOOL) 若蓝牙未打开,是否弹出alert
     CBCentralManagerOptionRestoreIdentifierKey         @""     用于蓝牙恢复链接
    */
    // CBCentralManager *centralManager=[CBCentralManager new];
    // [centralManager setDelegate:self];
    // 获取已连接的外围蓝牙设备(根据serviceID)
    NSArray *perArr=[centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:@"NSUUID"]]];
    // 获取已连接的外围蓝牙设备(根据Identifier)
    NSArray *perArr=[centralManager retrievePeripheralsWithIdentifiers:@[[CBUUID UUIDWithString:@"SERVICE ID"]]];
    // 扫描外围设备(services写nil 表示扫描所有)
    [centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"SERVICE ID"]] options:@{}];
    /*
     CBCentralManagerScanOptionAllowDuplicatesKey       @(BOOL) 是否重复扫描已发现的设备
     CBCentralManagerScanOptionSolicitedServiceUUIDsKey @[CBUDID] 扫描的指定服务UUID数组
     */
    // 停止扫描外围设备
    [centralManager stopScan];
    // 连接外围设备
    [centralManager connectPeripheral:peripheral options:@{}];
    /*
     CBConnectPeripheralOptionNotifyOnConnectionKey     @(BOOL) 是否弹出提示框(在后台连接上设备)
     CBConnectPeripheralOptionNotifyOnDisconnectionKey  @(BOOL) 是否弹出提示框(在后台断开连接设备)
     CBConnectPeripheralOptionNotifyOnNotificationKey   @(BOOL) 是否弹出提示框(在后台接受到数据时)
    */
    // 断开连接外围设备
    [centralManager cancelPeripheralConnection:peripheral];\


    /*
     CBCentralManagerRestoredStatePeripheralsKey
     CBCentralManagerRestoredStateScanServicesKey
     CBCentralManagerRestoredStateScanOptionsKey
    */
    // CBPeripheral:周边设备
    CBPeripheral *per=[CBPeripheral new];
    // dele
    [per setDelegate:nil];
    // name(readOnly)
    NSString *perName=per.name;
    // state(readOnly)
    CBPeripheralState perState=per.state;
    /*
     CBPeripheralStateDisconnected      未连接
     CBPeripheralStateConnecting        连接中
     CBPeripheralStateConnected         已连接
     CBPeripheralStateDisconnecting     断开连接中
     */
    // services(readOnly)
    NSArray *perServiceArr=per.services;
    // RSSI
    [per readRSSI];
    // 发现服务(根据ServiceID)
    [per discoverServices:@[[CBUUID UUIDWithString:@"SERVICE ID"]]];
    // 寻找特征(根据charactID service)
    [per discoverIncludedServices:@[charactUUID] forService:service];
    // ?
    [per discoverCharacteristics:@[charactUUID] forService:service];
    // 从外围设备读取数据(从相应特征值),调用didUpdateValue
    [per readValueForCharacteristic:charact];
    // 向特征写入值
    [per writeValue:[NSData data] forCharacteristic:charact type:CBCharacteristicWriteWithResponse];
    // CBCharacteristicWriteWithoutResponse/CBCharacteristicWriteWithResponse
    // 从外围设备读取数据,并订阅特征(打开通知,一有数据就获取,false则关闭),调用didUpdateNoti
    [per setNotifyValue:true forCharacteristic:charact];
    // 发现描述
    [per discoverDescriptorsForCharacteristic:charact];
    // 读取值从描述
    [per readValueForDescriptor:descriptor];
    // 向描述写入值
    [per writeValue:[NSData data] forDescriptor:descriptor];
    // CBService:服务
    CBService *service=[CBService new];
    // 发送此服务的周边设备(readOnly)
    CBPeripheral *servicePeripheral=service.peripheral;
    // primary/secondary(readOnly)
    BOOL serviceIsPrimary=service.isPrimary;
    // 已经发现的服务列表?(readOnly)
    NSArray *serviceIncludedServiceArr=service.includedServices;
    // 已经发现的特征列表?(readOnly)
    NSArray *serviceCharacteristicArr=service.characteristics;
    // 特征
    CBCharacteristic *charact=[CBCharacteristic new];
    // 此特征属于哪个service(readOnly)
    CBService *charactService=charact.service;
    // 特征类型(readOnly)
    CBCharacteristicProperties charactProperties=charact.properties;
    /*
     CBCharacteristicPropertyBroadcast                      广播
     CBCharacteristicPropertyRead                           读数据
     CBCharacteristicPropertyWriteWithoutResponse           写数据不要响应
     CBCharacteristicPropertyWrite                          写数据
     CBCharacteristicPropertyNotify                         通知
     CBCharacteristicPropertyIndicate
     CBCharacteristicPropertyAuthenticatedSignedWrites
     CBCharacteristicPropertyExtendedProperties
     */
    // 数据(readOnly)
    NSData *charactData=charact.value;
    // 改特征已经被发现的描述(readOnly)
    NSArray *charactDescriptorArr=charact.descriptors;
    // 该特征是否正在通知(readOnly)
    BOOL charactIsNotifying=charact.isNotifying;
    // CBDescriptor:描述
    CBDescriptor *descriptor=[CBDescriptor new];
    // 该描述属于哪个特征(readOnly)
    CBCharacteristic *descriptorCharacteristic=descriptor.characteristic;
    // value(readOnly)
    id descriptorValue=descriptor.value;
2. WiFi
WiFi通讯
       利用WiFi信号让手机和智能硬件设备进行数据传输处理的一种方式。
通讯原理
       使用Socket(套接字,Scoket = IP地址(找到哪台电脑即服务器)+端口号(找到服务器上的哪个应用))进行UDP(一对多)通讯
构成
      (1)路由器(提供WiFi信号,作为中间者负责转发传递数据)
      (2)硬件(手机/硬件设备,数据发送与接收方)


3种使用类型
       (1)手机(连上外部设备的WiFi)直接与外部设备(装有WiFi芯片)建立连接
       (2)手机和外部设备都连接上同一路由器
       (3)云端WiFi通讯(大趋势) ,手机<->服务器<->硬件,解决了前两种方法必须连上同一WiFi的局限。

你可能感兴趣的:(iOS基础之蓝牙)