美文网首页2017
iOS【蓝牙开发】实践一 数据接发

iOS【蓝牙开发】实践一 数据接发

作者: NJ_墨 | 来源:发表于2017-02-25 18:15 被阅读135次

    说明:这次实践的主要目的是简单掌握蓝牙的一些基本信息,实现数据的接发,中心与外设互相发送数据,大数据分块发送。最后为项目链接。

    中心1.png 外设2.png
    蓝牙基础(常见缩写)
    MFI: 专们为苹果设备制作的设备,详见:关于MFi认证你所必须要知道的事情;
    BLE:蓝牙4.0设备因为低耗电,所以也叫做BLE;
    MFI:开发使用ExternalAccessory 框架;
    4.0 BLE:开发使用CoreBluetooth 框架;
    
    Peripheral:外设,被连接的设备为Peripheral;
    Central:中心,发起连接的是Central;
    Service:服务,每个外设会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为 读read,写write,通知notiy几种;
    Characteristic:一个服务下面也可以存在多个特征,特征可以理解成具体实现功能的窗口,一般特征都会有value,也就是特征值,特征是与外界交互的最小单位;
    Description:每个特征可以对应一个或多个Description用户描述characteristic的信息或属性;
    UUID:可以理解成蓝牙上的唯一标识符,为了区分不同的服务和特征,或者给服务和特征取名字,我们就用UUID来代表服务和特征;
    
    
    快速生成UUID:
    打开Mac OS X的Terminal.app,用uuidgen命令生成一个128bit的UUID
    
    //服务id
    #define TRANSFER_SERVICE_UUID  @"0FB51F75-C9D5-45DC-BA61-065BD4A5E3E8"
    //特征id
    #define TRANSFER_CHARACTERISTIC_UP_UUID @"B678C8E2-9B1A-4952-A320-EF6D42F0831A"
    
    -------------------------------
    蓝牙中心模式流程:
    1. 建立中心角色
    2. 扫描外设(discover)
    3. 连接外设(connect)
    4. 扫描外设中的服务和特征(discover)
    5. 与外设做数据交互(explore and interact)
    6. 订阅Characteristic的通知
    7. 断开连接(disconnect)
    

    中心:首先要导入蓝牙库
    #import <CoreBluetooth/CoreBluetooth.h>
    
    遵守协议
    <CBCentralManagerDelegate,CBPeripheralDelegate>
    
    1. 建立中心角色 创建CBCentralManager对象
    _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    
    2. 扫描外设(discover)
    //查看设备是否支持 ,若支持则扫描
    -(void)centralManagerDidUpdateState:(CBCentralManager *)central{
        NSArray *uuidArray = [NSArray arrayWithObjects:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID],nil];
        NSDictionary *optionsDic = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithBool:NO],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
        [_centralManager scanForPeripheralsWithServices:uuidArray options:optionsDic];
    }
    
    3. 连接外设(connect)
    -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    
        if (_discoveredPeripheral != peripheral) {
            _discoveredPeripheral = peripheral;
            [_centralManager connectPeripheral:peripheral options:nil];
        }
    }
    
    4. 扫描外设中的服务和特征(discover)
    //当连接成功后,系统会通过回调函数告诉我们,然后我们就在这个回调里去扫描设备下所有的服务和特征
    -(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    
        peripheral.delegate = self;
        //指定服务 
        [peripheral discoverServices:[[NSArray alloc] initWithObjects:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID], nil]];
        //[peripheral discoverServices:nil];
    }
    
    -4.1 获取外设的services  6. 订阅Characteristic的通知
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    
        //处理我们需要的特征
        for (int i=0; i<peripheral.services.count; i++) {
            CBService *service = peripheral.services[i];       
        
            //指定需要查找的特征(对应外设中订阅特征)
            [peripheral discoverCharacteristics:[[NSArray alloc] initWithObjects:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_Up_UUID],[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_Image_UUID], nil] forService:service];
            //[peripheral discoverCharacteristics:nil forService:service];
        }
    }
    
    - 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的
    // 5 已搜索到Characteristics
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    
        //属于那个服务 根据不同的特征执行不同的命令
        if ([service.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]) {
        
            for (int i=0; i<service.characteristics.count; i++) {
                CBCharacteristic *characteristic = service.characteristics[i];
    
                if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_Up_UUID]]) {
                    _upCharacteristic = characteristic;
                    //监听设备
                    [peripheral setNotifyValue:YES forCharacteristic:characteristic];
                }
            }
        }
    }
    
    5. 与外设做数据交互(explore and interact)
    ///获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取。
    -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
        NSData *dataValue = characteristic.value;
    }
    //给外设发数据
    - (void)wrietPeripheral:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic value:(NSData *)data {
    
        if(_imageCharacteristic.properties & CBCharacteristicPropertyWrite || _imageCharacteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
           [self.discoveredPeripheral writeValue:data forCharacteristic:_imageCharacteristic type:CBCharacteristicWriteWithResponse];
        }
    }
    // 写入数据后的回调方法
    - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    }
    停止扫描
    [_centralManager stopScan];
    主动断开设备
    [_centralManager cancelPeripheralConnection:_discoveredPeripheral];
    
    大数据,分块发包
    - (void)actionSend:(UIButton *)btn {//发送大数据 分包处理
        NSInteger BLE_SEND_MAX_LEN = 512;
        imgName = [imgName isEqualToString:@"2.jpg"] ? @"3.jpg" : @"2.jpg";
        testImgView.image = [UIImage imageNamed:imgName];
    
        NSData *msgData = UIImageJPEGRepresentation([UIImage imageNamed:imgName], 1.0);
    
        for (int i = 0; i < [msgData length]; i += BLE_SEND_MAX_LEN) {
            // 预加 最大包长度,如果依然小于总数据长度,可以取最大包数据大小
            if ((i + BLE_SEND_MAX_LEN) < [msgData length]) {
                NSString *rangeStr = [NSString stringWithFormat:@"%i,%li", i, (long)BLE_SEND_MAX_LEN];
                NSData *subData = [msgData subdataWithRange:NSRangeFromString(rangeStr)];
                [[HMBLECenterHandle sharedHMBLECenterHandle] wrietPeripheral:nil
                                                              characteristic:nil
                                                                       value:subData];
                //根据接收模块的处理能力做相应延时
                usleep(20 * 1000);
            }
            else {
                NSString *rangeStr = [NSString stringWithFormat:@"%i,%i", i, (int)([msgData length] - i)];
                NSData *subData = [msgData subdataWithRange:NSRangeFromString(rangeStr)];
           
                [[HMBLECenterHandle sharedHMBLECenterHandle] wrietPeripheral:nil
                                                              characteristic:nil
                                                                       value:subData];
                usleep(20 * 1000);
                
                //发送结束标识
                NSData *exoData = [@"exo" dataUsingEncoding:NSUTF8StringEncoding];
                [[HMBLECenterHandle sharedHMBLECenterHandle] wrietPeripheral:nil
                                                              characteristic:nil
                                                                       value:exoData];
    
            }
        }
    }
    

    外设:首先要导入蓝牙库
    #import <CoreBluetooth/CoreBluetooth.h>
    
    遵守协议
    <CBPeripheralManagerDelegate>
    
    1. 建立外设角色 创建CBPeripheralManager对象
    _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
    
    2. 本地Peripheral设置服务,特性,描述,权限等等
    //1 查看设备是否支持 ,若支持则创建 服务和特征
    -(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    
        //一组特征值
        _upCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UP_UUID] properties:CBCharacteristicPropertyNotify | CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite | CBCharacteristicPropertyWriteWithoutResponse value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
    
        //一个服务中添加多个特征值
        transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES];
        NSArray *characters = [[NSArray alloc] initWithObjects:_upCharacteristic, nil];
        [transferService setCharacteristics:characters];
    
        // 2 添加一个服务
        [self.peripheralManager addService:transferService];  
    }
    
    3. Peripheral发送广告
    // 3 会监听didAddService
    -(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
        NSLog(@"添加服务");
        if (error != nil) {
            NSLog(@"添加服务失败: %@",error.localizedDescription);
        } else {        
            // 开始广播
            [self.peripheralManager startAdvertising:@{ CBAdvertisementDataLocalNameKey : @"Service_name", CBAdvertisementDataServiceUUIDsKey :@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
        }
    }
    
    4. 设置处理订阅、取消订阅、
    //当中央端连接上了此设备并订阅了特征时会回调 didSubscribeToCharacteristic
    -(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"中心已经预定了特征 --- %@",characteristic);
    }
    
    //当中央端取消订阅时会调用didUnsubscribeFromCharacteristic
    -(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"中心没有从特征预定 -- %@",characteristic);
    }
    
     4. 读characteristic、写characteristic的委托方法
    //当接收到中央端读的请求时会调用didReceiveReadRequest
    - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
        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:CBATTErrorReadNotPermitted];
        }
    }
    
    //当接收到中央端写的请求时会调用didReceiveWriteRequest
    - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
    CBATTRequest *request = requests[0];
        if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
            CBMutableCharacteristic *c = (CBMutableCharacteristic *)request.characteristic;
            c.value = request.value;
            NSData *data = c.value;
           [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
        } else {
            [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
        }
    }
    
    4. 往中心发送数据
    - (BOOL)updateValue:(NSData *)value characteristic:(CBMutableCharacteristic *)characteristic {
    
        if (value && characteristic) {
           return [self.peripheralManager updateValue:value forCharacteristic:characteristic onSubscribedCentrals:nil];
        }
        return NO;
    }
    

    项目Demo:蓝牙外设蓝牙中心

    相关文章

      网友评论

        本文标题:iOS【蓝牙开发】实践一 数据接发

        本文链接:https://www.haomeiwen.com/subject/xoxwwttx.html