iOS-BLE蓝牙开发持续更新

作者: 煜寒了 | 来源:发表于2015-07-21 18:03 被阅读58043次

    在写这个博客之前,空余时间抽看了近一个月的文档和Demo,系统给的解释很详细,接口也比较实用,唯独有一点,对于设备的唯一标示,网上众说
    纷纭,在这里我目前也还没有自己的见解,只是在不断的测试各种情况,亲测同一设备的UUID对于每台iPhone设备都不一样,只能尽量保证设备的唯一性,特别是自动重连的过程,让用户没有感知。我之前也找了很久,发现CBCentralManager和CBPeripheral里边都找不到和Mac地址有关的东西,后来发现一般是外设在Device Information服务中的某个特征返回的。经过与硬件工程师的协商,决定APP端将从这个服务中获取到蓝牙设备以及我的iPhone手机的蓝牙Mac地址,为自动连接的唯一性做准备。

    这里经过和硬件工程师的测试,发现设备端在获取手机蓝牙MAC地址的时候,当用户手机重启之后,这个地址也是会随机变化的,也就是说,作为开发者,只有设备的MAC地址能够保持唯一性不变化。

    ble.png

    有疑问的朋友可以先去这里瞅一瞅
    一个关于蓝牙4.0的智能硬件Demo详解

    • 下面是两台iPhone6连接同一台蓝牙设备的结果:
    **成功连接**** peripheral: <CBPeripheral: 0x1700f4500, identifier = 50084F69-BA5A-34AC-8A6E-6F0CEADB21CD, name = 555555555588, state = connected> with UUID: <__NSConcreteUUID 0x17003d980> 50084F69-BA5A-34AC-8A6E-6F0CEADB21CD**
    ****
    ****
    **成功连接**** peripheral: <CBPeripheral: 0x1742e3000, identifier = 55B7D759-0F1E-6271-EA14-BC5A9C9EEEEC, name = 555555555588, state = connected> with UUID: <__NSConcreteUUID 0x174036c00> 55B7D759-0F1E-6271-EA14-BC5A9C9EEEEC**
    

    进入正题

    iOS的蓝牙开发很简单,只要包含一个库,创建CBCentralManager实例,实现代理方法,然后就可以直接和设备进行通信。

    发现附近的特定蓝牙设备
    #import <CoreBluetooth/CoreBluetooth.h>
    

    首先可以定义一些即将使用到的UUID的宏

    #define kPeripheralName     @"360qws Electric Bike Service" //外围设备名称
    #define kServiceUUID        @"7CACEB8B-DFC4-4A40-A942-AAD653D174DC" //服务的UUID
    #define kCharacteristicUUID @"282A67B2-8DAB-4577-A42F-C4871A3EEC4F" //特征的UUID
    

    如果不是把手机作为中心设备的话,这些没有必要设置。
    这里我也没有用到,仅仅是提了一下,具体操作后续添加。

    对于生成UUID,大家可以谷歌一下,直接通过mac终端生成32位UUID。

    1.声明属性

    @property (weak, nonatomic) IBOutlet UITableView *bluetoothTable;
    @property (weak, nonatomic) IBOutlet UITextView *resultTextView;
    
    @property BOOL cbReady;
    @property(nonatomic) float batteryValue;
    @property (nonatomic, strong) CBCentralManager *manager;
    @property (nonatomic, strong) CBPeripheral *peripheral;
    
    @property (strong ,nonatomic) CBCharacteristic *writeCharacteristic;
    
    @property (strong,nonatomic) NSMutableArray *nDevices;
    @property (strong,nonatomic) NSMutableArray *nServices;
    @property (strong,nonatomic) NSMutableArray *nCharacteristics;
    

    2.遵守协议(这里我用到了table)

    @interface ViewController () <CBCentralManagerDelegate, CBPeripheralDelegate, UITableViewDataSource, UITableViewDelegate>
    

    3.初始化数据

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
        
        _cbReady = false;
        _nDevices = [[NSMutableArray alloc]init];
        _nServices = [[NSMutableArray alloc]init];
        _nCharacteristics = [[NSMutableArray alloc]init];
        
        _bluetoothTable.delegate = self;
        _bluetoothTable.dataSource = self;
        
        count = 0;
    }
    

    4.实现蓝牙的协议方法

    • (1)检测蓝牙状态
    //开始查看服务,蓝牙开启
    -(void)centralManagerDidUpdateState:(CBCentralManager *)central
    {
        switch (central.state) {
            case CBCentralManagerStatePoweredOn:
            {
                [self updateLog:@"蓝牙已打开,请扫描外设"];
                [_activity startAnimating];
                [_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]]  options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
            }
                break;
            case CBCentralManagerStatePoweredOff:
                [self updateLog:@"蓝牙没有打开,请先打开蓝牙"];
                break;
            default:
                break;
        }
    }
    

    注:[_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];中间的@[[CBUUID UUIDWithString:@"FF15"]]是为了过滤掉其他设备,可以搜索特定标示的设备。

    • (2)检测到外设后,停止扫描,连接设备
    //查到外设后,停止扫描,连接设备
    -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
    {
        [self updateLog:[NSString stringWithFormat:@"已发现 peripheral: %@ rssi: %@, UUID: %@ advertisementData: %@ ", peripheral, RSSI, peripheral.identifier, advertisementData]];
        
        _peripheral = peripheral;
        [_manager connectPeripheral:_peripheral options:nil];
        
        [self.manager stopScan];
        [_activity stopAnimating];
        
        BOOL replace = NO;
        // Match if we have this device from before
        for (int i=0; i < _nDevices.count; i++) {
            CBPeripheral *p = [_nDevices objectAtIndex:i];
            if ([p isEqual:peripheral]) {
                [_nDevices replaceObjectAtIndex:i withObject:peripheral];
                replace = YES;
            }
        }
        if (!replace) {
            [_nDevices addObject:peripheral];
            [_bluetoothTable reloadData];
        }
    }
    
    • (3)连接外设后的处理
    //连接外设成功,开始发现服务
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
        NSLog(@"%@", [NSString stringWithFormat:@"成功连接 peripheral: %@ with UUID: %@",peripheral,peripheral.identifier]);
        
        [self updateLog:[NSString stringWithFormat:@"成功连接 peripheral: %@ with UUID: %@",peripheral,peripheral.identifier]];
        
        [self.peripheral setDelegate:self];
        [self.peripheral discoverServices:nil];
        [self updateLog:@"扫描服务"];
    }
    //连接外设失败
    -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
    {
        NSLog(@"%@",error);
    }
    
    -(void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error
    {
        NSLog(@"%s,%@",__PRETTY_FUNCTION__,peripheral);
        int rssi = abs([peripheral.RSSI intValue]);
        CGFloat ci = (rssi - 49) / (10 * 4.);
        NSString *length = [NSString stringWithFormat:@"发现BLT4.0热点:%@,距离:%.1fm",_peripheral,pow(10,ci)];
        [self updateLog:[NSString stringWithFormat:@"距离:%@", length]];
    }
    
    • (4)发现服务和搜索到的Characteristice
    //已发现服务
    -(void) peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
        
        [self updateLog:@"发现服务."];
        int i=0;
        for (CBService *s in peripheral.services) {
            [self.nServices addObject:s];
        }
        for (CBService *s in peripheral.services) {
            [self updateLog:[NSString stringWithFormat:@"%d :服务 UUID: %@(%@)",i,s.UUID.data,s.UUID]];
            i++;
            [peripheral discoverCharacteristics:nil forService:s];
            
            if ([s.UUID isEqual:[CBUUID UUIDWithString:@"FF15"]]) {
                BOOL replace = NO;
                // Match if we have this device from before
                for (int i=0; i < _nDevices.count; i++) {
                    CBPeripheral *p = [_nDevices objectAtIndex:i];
                    if ([p isEqual:peripheral]) {
                        [_nDevices replaceObjectAtIndex:i withObject:peripheral];
                        replace = YES;
                    }
                }
                if (!replace) {
                    [_nDevices addObject:peripheral];
                    [_bluetoothTable reloadData];
                }
            }
        }
    }
    
    //已搜索到Characteristics
    -(void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
        [self updateLog:[NSString stringWithFormat:@"发现特征的服务:%@ (%@)",service.UUID.data ,service.UUID]];
        
        for (CBCharacteristic *c in service.characteristics) {
            [self updateLog:[NSString stringWithFormat:@"特征 UUID: %@ (%@)",c.UUID.data,c.UUID]];
            
            if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF01"]]) {
                _writeCharacteristic = c;
            }
            
            if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF02"]]) {
                [_peripheral readValueForCharacteristic:c];
                [_peripheral setNotifyValue:YES forCharacteristic:c];
            }
            
            if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF04"]]) {
                [_peripheral readValueForCharacteristic:c];
            }
            
            if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF05"]]) {
                [_peripheral readValueForCharacteristic:c];
                [_peripheral setNotifyValue:YES forCharacteristic:c];
            }
            
            if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FFA1"]]) {
                [_peripheral readRSSI];
            }
            
            [_nCharacteristics addObject:c];
        }
    }
    
    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
        [self updateLog:[NSString stringWithFormat:@"已断开与设备:[%@]的连接", peripheral.name]];
    }
    
    • (5)获取外设发来的数据
    //获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取。
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FF02"]]) {
            NSData * data = characteristic.value;
            Byte * resultByte = (Byte *)[data bytes];
            
            for(int i=0;i<[data length];i++)
                printf("testByteFF02[%d] = %d\n",i,resultByte[i]);
            
            if (resultByte[1] == 0) {
                switch (resultByte[0]) {
                    case 3: // 加解锁
                    {
                        if (resultByte[2] == 0) {
                            [self updateLog:@"撤防成功!!!"];
                        }else if (resultByte[2] == 1) {
                            [self updateLog:@"设防成功!!!"];
                        }
                    }
                        break;
                    case 4: // 开坐桶
                    {
                        if (resultByte[2] == 0) {
                            [self updateLog:@"关坐桶成功!!!"];
                        }else if (resultByte[2] == 1) {
                            [self updateLog:@"开坐桶成功!!!"];
                        }
                    }
                        break;
                    case 5: // 锁定电机
                    {
                        if (resultByte[2] == 0) {
                            [self updateLog:@"解锁电机控制器成功!!!"];
                        }else if (resultByte[2] == 1) {
                            [self updateLog:@"锁定电机控制器成功!!!"];
                        }
                    }
                        break;
                    default:
                        break;
                }
            }else if (resultByte[1] == 1) {
                [self updateLog:@"未知错误"];
            }else if (resultByte[1] == 2) {
                [self updateLog:@"鉴权失败"];
            }
        }
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FF04"]]) {
            NSData * data = characteristic.value;
            Byte * resultByte = (Byte *)[data bytes];
            
            for(int i=0;i<[data length];i++)
                printf("testByteFF04[%d] = %d\n",i,resultByte[i]);
            
            if (resultByte[0] == 0) {
                // 未绑定 -》写鉴权码
                [self updateLog:@"当前车辆未绑定,请鉴权"];
                [self authentication];  // 鉴权
                [self writePassword:nil newPw:nil];
            }else if (resultByte[0] == 1) {
                // 已绑定 -》鉴权
                [self updateLog:@"当前车辆已经绑定,请鉴权"];
                [self writePassword:nil newPw:nil];
            }else if (resultByte[0] == 2) {
                // 允许绑定
                [self updateLog:@"当前车辆允许绑定"];
            }
        }
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FF05"]]) {
            NSData * data = characteristic.value;
            Byte * resultByte = (Byte *)[data bytes];
            
            for(int i=0;i<[data length];i++)
                printf("testByteFF05[%d] = %d\n",i,resultByte[i]);
            
            if (resultByte[0] == 0) {
                // 设备加解锁状态 0 撤防     1 设防
                [self updateLog:@"当前车辆撤防状态"];
            }else if (resultByte[0] == 1) {
                // 设备加解锁状态 0 撤防     1 设防
                [self updateLog:@"当前车辆设防状态"];
            }
        }
    }
    
    //中心读取外设实时数据
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
        if (error) {
            NSLog(@"Error changing notification state: %@", error.localizedDescription);
        }
        
        // Notification has started
        if (characteristic.isNotifying) {
            [peripheral readValueForCharacteristic:characteristic];
            
        } else { // Notification has stopped
            // so disconnect from the peripheral
            NSLog(@"Notification stopped on %@.  Disconnecting", characteristic);
            [self updateLog:[NSString stringWithFormat:@"Notification stopped on %@.  Disconnecting", characteristic]];
            [self.manager cancelPeripheralConnection:self.peripheral];
        }
    }
    //用于检测中心向外设写数据是否成功
    -(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        if (error) {
            NSLog(@"=======%@",error.userInfo);
            [self updateLog:[error.userInfo JSONString]];
        }else{
            NSLog(@"发送数据成功");
            [self updateLog:@"发送数据成功"];
        }
        
        /* When a write occurs, need to set off a re-read of the local CBCharacteristic to update its value */
        [peripheral readValueForCharacteristic:characteristic];
    }
    
    • (6)其他辅助性的
    #pragma mark - 蓝牙的相关操作
    - (IBAction)bluetoothAction:(UIButton *)sender {
        switch (sender.tag) {
            case 201:
            {   // 搜索设备
                [self updateLog:@"正在扫描外设..."];
                [_activity startAnimating];
                [_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]]  options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
                
                double delayInSeconds = 30.0;
                dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
                dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
                    [self.manager stopScan];
                    [_activity stopAnimating];
                    [self updateLog:@"扫描超时,停止扫描"];
                });
            }
                break;
            case 202:
            {   // 连接
                if (_peripheral && _cbReady) {
                    [_manager connectPeripheral:_peripheral options:nil];
                    _cbReady = NO;
                }
            }
                break;
            case 203:
            {   // 断开
                if (_peripheral && !_cbReady) {
                    [_manager cancelPeripheralConnection:_peripheral];
                    _cbReady = YES;
                }
            }
                break;
            case 204:
            {   // 暂停搜索
                [self.manager stopScan];
            }
                break;
            case 211:
            {   // 车辆上锁
                [self lock];
            }
                break;
            case 212:
            {   // 车辆解锁
                [self unLock];
            }
                break;
            case 213:
            {   // 开启坐桶
                [self open];
            }
                break;
            case 214:
            {   // 立即寻车
                [self find];
            }
                break;
            default:
                break;
        }
    }
    
    -(NSOperationQueue *)queue {
        if (!_queue) {  // 请求队列
            self.queue = [[NSOperationQueue alloc]init];
            [self.queue setMaxConcurrentOperationCount:1];
        }
        
        return _queue;
    }
    
    #pragma mark - 命令
    #pragma mark - 鉴权
    -(void)authentication {
        Byte byte[] = {1, 1, 2, 3, 4, 5, 6, 7, 8};
        if (_peripheral.state == CBPeripheralStateConnected) {
            [_peripheral writeValue:[NSData dataWithBytes:byte length:9] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
        }
    }
    
    #pragma mark - 写密码 
    -(void)writePassword:(NSString *)initialPw newPw:(NSString *)newPw {
        Byte byte[] = {2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8};
        if (_peripheral.state == CBPeripheralStateConnected) {
            [_peripheral writeValue:[NSData dataWithBytes:byte length:17] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
        }
    }
    
    #pragma mark - 车辆上锁
    -(void)lock {
        Byte byte[] = {3, 1};
        if (_peripheral.state == CBPeripheralStateConnected) {
            [_peripheral writeValue:[NSData dataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
        }
    }
    
    #pragma mark - 车辆解锁
    -(void)unLock {
        Byte byte[] = {3, 0};
        if (_peripheral.state == CBPeripheralStateConnected) {
            [_peripheral writeValue:[NSData dataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
        }
    }
    
    #pragma mark - 开启坐桶
    -(void)open {
        Byte byte[] = {4, 1};
        if (_peripheral.state == CBPeripheralStateConnected) {
            [_peripheral writeValue:[NSData dataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
        }
    }
    
    #pragma mark - 立即寻车
    -(void)find {
        Byte byte[] = {7};
        if (_peripheral.state == CBPeripheralStateConnected) {
            [_peripheral writeValue:[NSData dataWithBytes:byte length:1] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
        }
    }
    
    -(NSData *)hexString:(NSString *)hexString {
        int j=0;
        Byte bytes[20];
        ///3ds key的Byte 数组, 128位
        for(int i=0; i<[hexString length]; i++)
        {
            int int_ch;  /// 两位16进制数转化后的10进制数
            
            unichar hex_char1 = [hexString characterAtIndex:i]; ////两位16进制数中的第一位(高位*16)
            int int_ch1;
            if(hex_char1 >= '0' && hex_char1 <='9')
                int_ch1 = (hex_char1-48)*16;   //// 0 的Ascll - 48
            else if(hex_char1 >= 'A' && hex_char1 <='F')
                int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65
            else
                int_ch1 = (hex_char1-87)*16; //// a 的Ascll - 97
            i++;
            
            unichar hex_char2 = [hexString characterAtIndex:i]; ///两位16进制数中的第二位(低位)
            int int_ch2;
            if(hex_char2 >= '0' && hex_char2 <='9')
                int_ch2 = (hex_char2-48); //// 0 的Ascll - 48
            else if(hex_char1 >= 'A' && hex_char1 <='F')
                int_ch2 = hex_char2-55; //// A 的Ascll - 65
            else
                int_ch2 = hex_char2-87; //// a 的Ascll - 97
            
            int_ch = int_ch1+int_ch2;
            NSLog(@"int_ch=%d",int_ch);
            bytes[j] = int_ch;  ///将转化后的数放入Byte数组里
            j++;
        }
        
        NSData *newData = [[NSData alloc] initWithBytes:bytes length:20];
        
        return newData;
    }
    

    在和硬件之间的数据发送和接受,用的都是byte数组。最后,添加一个存储已连接过得设备

    - (void) addSavedDevice:(CFUUIDRef)uuid {
        NSArray *storedDevices = [[NSUserDefaults standardUserDefaults] arrayForKey:@"StoredDevices"];
         NSMutableArray *newDevices = nil;
        CFStringRef uuidString = NULL;
        if (![storedDevices isKindOfClass:[NSArray class]]) {
            NSLog(@"Can't find/create an array to store the uuid");
            return;
        }
        
        newDevices = [NSMutableArray arrayWithArray:storedDevices];
        uuidString = CFUUIDCreateString(NULL, uuid);
        if (uuidString) {
            [newDevices addObject:(__bridge NSString*)uuidString];
            CFRelease(uuidString);
        }
        
        /* Store */
        [[NSUserDefaults standardUserDefaults] setObject:newDevices forKey:@"StoredDevices"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    
    - (void) loadSavedDevices {
        NSArray *storedDevices = [[NSUserDefaults standardUserDefaults] arrayForKey:@"StoredDevices"];
        if (![storedDevices isKindOfClass:[NSArray class]]) {
             NSLog(@"No stored array to load");
            return;
        }
        
        for (id deviceUUIDString in storedDevices) {
            if (![deviceUUIDString isKindOfClass:[NSString class]]) continue;
            
            CFUUIDRef uuid = CFUUIDCreateFromString(NULL, (CFStringRef)deviceUUIDString);
            if (!uuid) continue;
            [CBCentralManager retrievePeripherals:[NSArray arrayWithObject:(__bridge id)uuid]];
            CFRelease(uuid);
        }
    }
    
    //And the delegate function for the Retrieve Peripheral
    
    - (void) centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals {
        CBPeripheral *peripheral;
        /* Add to list. */
        for (peripheral in peripherals) {
             [central connectPeripheral:peripheral options:nil];
        }
    }
    
     - (void) centralManager:(CBCentralManager *)central didRetrievePeripheral:(CBPeripheral *)peripheral {
        [central connectPeripheral:peripheral options:nil];
    }
    

    后记

    • 最主要是用UUID来确定你要干的事情,特征和服务的UUID都是外设定义好的。我们只需要读取,确定你要读取什么的时候,就去判断UUID是否相符。 一般来说我们使用的iPhone都是做centralManager的,蓝牙模块是peripheral的,所以我们是want datas,需要接受数据。
      1.判断状态为powerOn,然后执行扫描
      2.停止扫描,连接外设
      3.连接成功,寻找服务
      4.在服务里寻找特征
      5.为特征添加通知
      5.通知添加成功,那么就可以实时的读取value[也就是说只要外设发送数据[一般外设的频率为10Hz],代理就会调用此方法]。
      6.处理接收到的value,[hex值,得转换] 之后就自由发挥了,在这期间都是通过代理来实现的,也就是说你只需要处理你想要做的事情,代理会帮你调用方法。[别忘了添加代理]

    2015-07-28 更

    关于write我这里还有些注意的地方要强调!!!!

    并不是每一个Characteristic都可以通过回调函数来查看它写入状态的。就比如针对 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06),就是一个不能有response的Characteristic。刚开始我就一直用CBCharacteristicWriteType.WithResponse来进行写入始终不成功,郁闷坏了,最后看到每个Characteristic还有个属性值是指示这个的,我将每个Characteristic打印出来有如下信息:

    immediateAlertService Discover characteristic <CBCharacteristic: 0x15574d00, UUID = 2A06, properties = 0x4, value = (null), notifying = NO>
    linkLossAlertService Discover characteristic <CBCharacteristic: 0x15671d00, UUID = 2A06, properties = 0xA, value = (null), notifying = NO>
    

    这个的properties是什么刚开始不知道,觉得他没意义,后面才注意到properties是Characteristic的一个参数,具体解释如下:

    Declaration
    SWIFT
    struct CBCharacteristicProperties : RawOptionSetType {
        init(_ value: UInt)
        var value: UInt
        static var Broadcast: CBCharacteristicProperties { get }
        static var Read: CBCharacteristicProperties { get }
        static var WriteWithoutResponse: CBCharacteristicProperties { get }
        static var Write: CBCharacteristicProperties { get }
        static var Notify: CBCharacteristicProperties { get }
        static var Indicate: CBCharacteristicProperties { get }
        static var AuthenticatedSignedWrites: CBCharacteristicProperties { get }
        static var ExtendedProperties: CBCharacteristicProperties { get }
        static var NotifyEncryptionRequired: CBCharacteristicProperties { get }
        static var IndicateEncryptionRequired: CBCharacteristicProperties { get }
    }
    OBJECTIVE-C
    typedef enum {
       CBCharacteristicPropertyBroadcast = 0x01,
       CBCharacteristicPropertyRead = 0x02,
       CBCharacteristicPropertyWriteWithoutResponse = 0x04,
       CBCharacteristicPropertyWrite = 0x08,
       CBCharacteristicPropertyNotify = 0x10,
       CBCharacteristicPropertyIndicate = 0x20,
       CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
       CBCharacteristicPropertyExtendedProperties = 0x80,
       CBCharacteristicPropertyNotifyEncryptionRequired = 0x100,
       CBCharacteristicPropertyIndicateEncryptionRequired = 0x200,
    } CBCharacteristicProperties;
    

    可以看到0x04对应的是CBCharacteristicPropertyWriteWithoutResponse
    0x0A对应的是CBCharacteristicPropertyNotify

    所以 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06)是不能用CBCharacteristicWriteType.WithRespons进行写入,只能用CBCharacteristicWriteType.WithOutRespons。这样在以后的开发中可以对每个Characteristic的这个参数进行检查再进行设置。

    最后讲一下关于蓝牙绑定的过程,在iOS中,没有讲当绑定的过程,直接就是扫描、连接、交互。从而很多人会认为,连接就是绑定了,其实不然。在iOS开发中,连接并没有完成绑定,在网上找到了个很好的解释:

    you cannot initiate pairing from the iOS central side. Instead, you have to read/write a characteristic value,
    and then let your peripheral respond with an "Insufficient Authentication" error.
    iOS will then initiate pairing, will store the keys for later use (bonding) and encrypts the link. As far as I know,
    it also caches discovery information, so that future connections can be set up faster.

    就是当发生读写交互时,系统在会和外设进行绑定操作!!!

    2016-02-20 更

    ios蓝牙如何获取广播包数据

    如题,手机作为主设备,在使用CoreBluetooth时候,想获取蓝牙的数据广播包。在使用

    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)aPeripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
    

    方法时候获取的advertisementData打印出来只有

    ** ****kCBAdvDataIsConnectable****kCBAdvDataLocalName****kCBAdvDataManufacturerData**
    

    三个属性对应的值,但这并非广播包的数据。例如安卓可以通过scandata来获取到广播包的值,那么iOS这边我应该怎么做呢?

    好像苹果这边禁止读取这种广播内容的的,真要的话你可以让硬件那边把数据做到kCBAdvDataManufacturerData这个字段里面。

    Demo地址:一个蓝牙4.0的智能硬件Demo

    进一步交流 QQ群:361736344

    相关文章

      网友评论

      • 25d22145799f:“0x0A对应的是CBCharacteristicPropertyNotify”这句话让我百思不得其解。
        想了半天0x0a怎么对应的会是CBCharacteristicPropertyNotify,写入的时候有没有返回跟notify有什么关系,怎么也得对应write相关的属性啊?

        经过一段时间研究,最后发现0x0a其实是 0000 1010,他不是“对应了某个”属性,而是对应了 0000 1000 (0x8) (CBCharacteristicPropertyWrite) 还有0000 0010(0x2)(CBCharacteristicPropertyRead)这两个属性。
        所以0x0a应该用CBCharacteristicPropertyWrite,这篇文章上写反了。
        煜寒了:@Retr0_b5d8 具体参考官方文档,0x08 对应 CBCharacteristicPropertyWrite

        https://developer.apple.com/documentation/corebluetooth/cbcharacteristicproperties?language=objc
        煜寒了:@Retr0_b5d8 好的,多谢指正
      • 小凡凡520:楼主 安卓可以获取到 蓝牙硬件的mac地址,iOS怎么获取不到呀,,只有获取到UUID
        煜寒了:@小凡凡520 看下官方文档,iOS 是不能直接获取到 外设的 MAC 地址,可以通过让硬件工程师把 MAC 地址 放在广播里,你这边在搜索的时候就能读到
      • AppleTTT:你好,请问你们有处理 writeValue 等待 response 的时候的超时逻辑吗
        煜寒了:@AppleTTT 恩 最好要处理下
      • 阿福lfy:楼主您好,手机app连接好蓝牙模块以后,再给蓝牙发送数据时,错误提示:The specified UUID is not allowed for this operation。请问这个是什么原因啊?您遇到过吗?
      • 时光管理员:我们设备的状态时0X04,我用CBCharacteristicWriteWithoutResponse写入后,没有回调,就完全没有反应了,看了好多也不知道怎么回事,是数据有问题么?
        时光管理员:@煜寒了 嗯嗯 谢谢 这方面弄清楚了,现在主要有个问题,就是我传递的数据是16进制的,需要通过如何转换然后写入,从网上搜的一些方法都没用。
        煜寒了:@时光管理员 WithoutResponse,就是没有回调的,你可以让硬件在收到你的写入之后,通过这个特征广播告诉你也可以判断
      • Kaaaaai:楼主你好,有没有关于蓝牙HFP和A2DP的相关资料,我网上搜了好久都没看到有详细展开的
        煜寒了:@Kaaaaai 这个没有接触,不太了解
      • H5:请问如何判断俩条蓝牙广播是同一个蓝牙外设发出来的。因为蓝牙外设将UUID和MAC地址分别通过广播发出来。现在想要通过UUID筛选自己的设备,然后把自己设备的MAC地址发给服务器。请问有什么好的解决办法嘛?
        H5:@煜寒了 你说是中心设备获取到的UUID? 还是广播包里的UUID?
        煜寒了:@H5 根据uuid判断不可以吗?
      • 码客波锣:咨询个问题:我扫描设备的时候部分设备扫描不到,这是为什么?
        码客波锣:@煜寒了 没加过滤。扫描不到安卓设备
        煜寒了:@码客波L 部分扫描不到,是不是因为加了服务uuid的过滤,或者有些设备处于链接状态
      • 落马洲:您好,我最近遇到一个问题,就是没有封装sdk前,查询数据可以通过didUpdateValueForCharacteristic反馈回来,但是用了sdk后,就没办法回调数据,请问这是什么原因呢?
      • Little_Shaun:我想问下,如果蓝牙设备不发送广播,也就是我手机不扫描周围的设备信息,但是我知道该设备的mac地址,能够连接到这个蓝牙设备么?
        Little_Shaun:@煜寒了 哦,多谢,现在有个设备安卓可以搜到,苹果搜不到,主要是哪里出了问题啊?
        煜寒了:@Little_Shaun 不可以通过mac 地址直连的,需要先扫描
      • d63d3136f13a:求视频 谢谢楼主 629489948@qq.com :smile:
      • ShenYj:其实MAC地址并不一定都是固定的,取决安全策略(Public Device Address和Random Device Address),如果硬件那边比较抠门不舍得花钱,很有可能会搞一个静态的MAC地址,获取到的MAC地址每次都会变化,iOS这边连接设备并不靠MAC,而Android需要根据MAC来配对,估计是iOS底层做了处理,即便是静态MAC地址,iOS端也不会受到影响,猜测是底层直接能获取到IRK并解析到原始MAC,而Android端如果使用这种随机地址,不同设备上就会出现重连失败的问题
      • Clemo:楼主在吗?遇到一个很奇怪的问题. 第一次搜索(加入了的服务UUID筛选)的时候是正常的,但是当连接上设备然后断开之后.再一次去搜索,系统会自动连上很多蓝牙设备. 如果没用筛选就不会.请问知道怎么回事吗?
      • 风情似雪:博主,您好。请教一下,如果我要实现蓝牙设备断开自动重新连接的话,该在哪个方法中实现,可以依据设备的UUID来作为判断条件吗?谢谢~:blush:
      • ShenYj:请教楼主个问题:
        我们项目是基于CoreBluetooth进行开发的,外围设备用来采集心电数据,通过蓝牙实时传输给App,设备的波特率改到115200后,我这每次接收到的数据包就不稳定了,很少的几条能到182Byte,在调整波特率前,一个完整的数据包需要34次接收完,33次182Byte,最后一条数据62Byte,而且速度明显慢下来了,iOS端能否修改ATT_MTU size呢?如果不能如何对速度进行优化呢?
        Moker_C:@ShenYj 接收的包比较大时,比如包有220个字节,我只能收到180个,这种情况
        ShenYj:@Moker_C 波特率问题?
        Moker_C:兄弟解决了吗,有什么方法吗?
      • juvelins: ', reason: 'Invalid parameter not satisfying: characteristic != nil'. 你好 我点击开锁 就崩溃 是不是因为设备权限的问题
      • 独孤伊人_xie:我先问一下 关于蓝牙连接稳定性的问题。在连接成功,发送数据成功后,再静等一会,会遇到蓝牙连接不稳定的情况,再次链接,就会有连接失败的问题,请问您有遇到过这样的情况吗?谢谢:pray:
        煜寒了:@独孤伊人_xie 你在断开连接以后,需要调用connect 系统才会帮你自动连接,你这边杀掉应用就可以连接,那说明外设没问题的
        独孤伊人_xie:@煜寒了 我这也是,断开连接之后,在连接就连接不上了,需要把应用退出后,再次打开,就又可以重新连接 蓝牙了
        煜寒了:@独孤伊人_xie 恩,会遇到这种不稳定的情况,有时候怀疑是蓝牙芯片的问题,之前我们断开连接以后,竟然搜索不到外设,外设重启下就能搜到了,换个好些的芯片就好多了
      • astring:大神,能否加个好友问个问题QQ1051083726
        煜寒了:@astring 你可以加群,里面大部分都是做蓝牙项目的
        煜寒了:@astring 可以加群讨论,361736344
      • 哈扛不住开回家:楼主我最近在开发一款蓝牙公交卡相关的项目, 卡在了 获取 mac地址这边,想问一下 通过什么方式获取呢?
        煜寒了:@哈扛不住开回家 你是要获取外设的mac 地址吗? 外设可以通过广播把地址告诉你的
        煜寒了:@哈扛不住开回家 这边是不能直接获取到mac 地址的,有的办法就是蓝牙设备那边广播自己的mac 地址,APP 这边从广播中拿到
      • Kasign:很详细
      • 记忆淡忘中:您好 我现在能扫描到设备 但是不知道怎么读取设备上的值 有很多的服务和特征 不知道该用哪个怎么办?
        煜寒了:@记忆淡忘中 服务和特征,需要找硬件工程师那边了解,文档上应该有的
        healthbird:和你们硬件工程师调试,一般他们会给你说用哪个特征,如果他们也是不知道,只能试一试了
      • 940ce980cb08:有监控过iOS BLE蓝牙write的内存情况么?
        我们试的在连续发送上百条蓝牙命令时,通过Leak发现,虽然没有报Lead的内存,但是会不断出现postNotification这个Caller占用的内存,并且不会被释放。
        上百条的时候会导致app崩溃。
        healthbird:@RunningEagle 没试过,我是使用原生的API,自己写的工具类,没遇到你说的那种情况
        940ce980cb08:@healthbird ios api里没有留意到有缓存的说明和接口。 你那边连续发100条命令,会不会出现内存增长很多,不释放的情况么
        healthbird:手机写入蓝牙命令会有缓存吗?API中没有释放缓存的方法吧
      • healthbird:app进入后台后,你怎么处理的?支持后台模式吗?
        煜寒了:@healthbird 不是授权那个地方,是在后台模式的value 里加上蓝牙后台的字段
        healthbird:@煜寒了 infoplist 中声明是iOS10之后的特性吧,是为了授权能使用蓝牙,和后台模式有关系吗?
        煜寒了:@healthbird 支持后台模式,你需要先在info.plist 里声明
      • d9431116937e:请问下 获取设备发来的数据,由于Data中是UInt8类型,没有预期的负数(-1,-2)。是用数据开头的几个字节来跟负数做比较。这个有什么其他的办法或者建议吗
      • a31d1e393504:大神 你好!我现在在做蓝牙开锁的项目!别的手机都没问题!只有6P手机链接蓝牙不成功,这是怎么回事!十万火啊!大神!求帮助!
        a31d1e393504:@煜寒了 大神 蓝牙扫描并链接速度慢怎么解决 大概六七秒钟的样子
        a31d1e393504:@煜寒了 不是 偶尔能开 偶尔不能开!同一个手机,同一把锁! 好扯淡的问题,一点思路没有!
        煜寒了:@甲乙兵 没遇到这个问题,不会是所有的6p 都有问题吧
      • 胜利的预言家:请教一下,我在- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
        中取peripheral.name == null,请问是为什么?
      • d19c1fdacbf2:大神,你好,请问你开发蓝牙时候有没有遇到在iPhone 6 和iPhone 6 Plus上无法使用的问题?具体描述就是:开机后第一次打开蓝牙,使用正常,如果把蓝牙关掉在打开,就无法使用!必须重启手机才可以?我试了lightblue这个APP,也是一样的问题。这个问题该你遇到过吗?
        d19c1fdacbf2:@Owl_City_Gx 没有解决,应该是手机的蓝牙模块有问题
        Owl_City_Gx:我也遇到这个问题了,层主有解决了吗?
        煜寒了:@Drunkard 没有遇到过哎,难道蓝牙没有真正的断开连接?
      • H_Cynic:设备mac地址是需要硬件那边设定吧 我这边可以通过发送指令 [self sendCommand:@"RD+MAC"];
        if (peripheral && wirteCharacter) {
        NSLog(@"command = %@",command);
        NSData *commandData = [command dataUsingEncoding:NSASCIIStringEncoding];
        NSMutableData *sendData = [NSMutableData dataWithData:commandData];
        Byte end = 0x0D;
        [sendData appendBytes:&end length:1];
        end = 0x0A;
        [sendData appendBytes:&end length:1];
        [peripheral writeValue:sendData forCharacteristic:wirteCharacter type:CBCharacteristicWriteWithResponse];

        }
        来获取 之后就简单了- (void)peripheral:(CBPeripheral *)args_peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
        在characteristic里面就上报了mac地址给我 讲真 蓝牙app开发不同于那种电商之类的app 有时候和硬件方面协商好 很多事可以轻易解决
      • 雷鸣1010:你好,你们是怎么测试的呀,要是只有一部手机,虚拟peripheral是用什么软件怎么创建的呀
        H_Cynic:可以使用蓝牙串口助手模拟测试
      • 我的大名叫小爱:发现CBCentralManager和CBPeripheral里边都找不到和Mac地址有关的东西,后来发现一般是外设在Device Information服务中的某个特征返回的 这句话 怎么理解呢???是可以找到直接能用来识别设备唯一性的Mac了吗??? 如果是的 那就太好了???望指教 我们公司的蓝牙设备是自己把自己的Mac广播了出来.然后 App这边获取到后使用的
        煜寒了:@我的大名叫小爱 广播出来mac 地址比较好,如果不能广播,就放在特征值里,连接上后读取。不不不可以找到直接能用来识别设备唯一性的Mac
      • HeavyCross:怎么保持蓝牙持续扫描,我们需求需要实时获取蓝牙信号强度
        H_Cynic: 在接收数据的代理里面有持续上报RSSI
        -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)args_peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
        持续扫描的话 应该可以在蓝牙开启状态下 [self.manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @yes}];
        可以试试。。。
      • samlty:很细致,感谢
      • A_sura:楼主遇到过这个问题吗,,连接上之后扫描 service后,就自动断开了,
        Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us." UserInfo={NSLocalizedDescription=The specified device has disconnected from us
      • fuadam1982:请问博主,是否知道如何让蓝牙保持长连接?我们现在连接上蓝牙传输完数据后,过个几个小时连接就断了。需要重新发现设备然后连接才行。
      • Han夜:楼主,现在蓝牙开发遇到了一个问题,如何实时获取没有连接的蓝牙的信号强度。像lightBlue上的一样,信号量会在扫面界面不断更新的那种。
        煜寒了:@c3f789b8a9ee 强度有的啊,扫描的时候一般都会返回的
      • 滚来滚去的桔子:你好,试过调用系统的蓝牙功能吗?或者说,能用系统的蓝牙功能吗?
        煜寒了:@滚来滚去的桔子 没有哎
      • 新地球说着一口陌生腔调:没提到蓝牙怎么发送数据啊?
        煜寒了:@新地球说着一口陌生腔调 发送数据也是同样的方法,只不过需要分包,每次不能超过20个字节
      • 达若漠沙:好吧,看完了所有评论,如果说干货总分有100的话,我觉着楼主可以得200分啦!超赞!!
        另外,求掉线重连和后台重连的资源,楼主,再来点干货吧!3Q3Q3Q~~ :blush:
        煜寒了:@红天石 客气了~
        达若漠沙: @煜寒了 谢谢楼主😬~~
        煜寒了:@红天石 不好意思,很久没上来了,推荐一系列博客,http://www.saitjr.com/ios/core-bluetooth-run-in-background.html
      • 0fa2aefcab9b:你好,我想问一下,我的蓝牙连接成功后,第一次写入数据会发生振动,再次调用不震动是什么原因
        煜寒了:@鲍帥 是不是硬件那边的问题,写入成功了吗
      • 凯文Kevin21:开发一个蓝牙项目,最近在搞蓝牙项目, 这些文档上哪儿去找啊? 楼主指点一下,新手。
        煜寒了:@七秒小鱼人 官方文档比较详细,不过全英文看起来费劲儿
      • 南方小金豆:楼主问个问题:如果周围有多个设备,请求该怎么连接离我位置最近的一台设备?
        煜寒了:@那份牵挂给了谁 我的思路是,5s(这个时间由你来定)时间内,拿到周围所有设备的信息,根据信号强度,选择强度最强的那个连接,最近的不好判断,比如隔了一堵墙的信号就很弱
      • 大萌哥哥:怎么搜不到东西啊,求解
        煜寒了:@大萌哥哥 是不是因为服务的uuid不匹配
      • 廖马儿:请问一下,怎么操作Notify,我想打开或者关闭通知.然后监听通知的内容。怎么操作的
        煜寒了:@廖马儿 有一个方法可以在写入数据后,会有写入结果的回调,好像是writeWithResponse,你可以试试
        廖马儿:@煜寒了 再问一个问题啊,就是我想向设备那边发送很多信息,比如,1000个时间戳,我怎么才能知道回调是成功了的呢?
        煜寒了:@Aircraft Notify 是设备那边发的,手机可以选择监听或不监听,应该是不能操作的
      • coderXiaoBo:关于如何获取蓝牙Mac地址我找到一篇,http://macpu.github.io/2015/11/12/iOS%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E8%93%9D%E7%89%99Mac%E5%9C%B0%E5%9D%80/,
        不知可行不可行,
        达若漠沙:@coderXiaoBo 地址让硬件广播一下或者写一个特征值,读取一下这样就可以获得的!
        煜寒了:@coderXiaoBo 不可的,我试过的,你可以试试看,地址可以让硬件放在广播里
      • 87624b4b9e2f:不错,挺详细
      • 82dea0b2d3cb:你好 我想问下收发数据都是在一个特征里面 然后我判断的时候判断的是一个特征 结果app这边就收不到返回的结果,然而用lightblue是可以的。一个特征中的读和写。
        19e3e699f046:@nocannobibi app端若要被动接受的话,特征必须为通知类型的,APP端需要先订阅通知。要不然每次主动去读取也可以。
      • AlwaysBlue:这个应该就是iOS和蓝牙的通信,那么iBeacon和这个是什么关系呢,iBeacon只是一种特殊的和iOS进行蓝牙通信的协议吗
      • 蚂蚁977:您好,请问一下,连接外设后,任何方法都不调用了?什么问题?
      • b1a6d1095254:你好,打印出来的信息是The characteristic is :<CBCharacteristic: 0x156576570, UUID = 49535343-026E-3A9B-954C-97DAEF17E26E, properties = 0x18, value = (null), notifying = NO> 我使用[_peripheral writeValue:myData forCharacteristic:self.cBCharacteristic type:CBCharacteristicWriteWithoutResponse];方法传值传不进去,是我代码的问题吗
        煜寒了:@b1a6d1095254 data不能超过20个字节,另外是否带回应的传值
      • 啊王王李孟姚:真的很好
        煜寒了:@啊王王李孟姚 谢谢鼓励
      • Scorpio_糖果屋:楼主大神,我在didUpdateValueForCharacteristic这个方法里面读取到characteristic.value的值,怎样把它转成10进制的数
        19e3e699f046:characteristic.value 是NSData类型的, 通过 getBytes:length: 方法 转换成 Byte类型, Byte 就是Uint8、unsigned char , 数值类型,直接使用的不存在进制转换。
        煜寒了:@Scorpio_糖果屋 都是二进制的内容,先转成byte,根据byte算出10进制
      • 38a719111d7d:您好,我现在遇到一个问题,就是打印¥这个符号的时候会出现乱码。。请问遇到过么
        煜寒了:@小白白哒 没有遇到哎,主要是没试过打印这个符号
      • AppleLSY:想问下,该怎样实现与外设的绑定,使下次只能连接到绑定的外设
        煜寒了:@AppleLSY 你的意思就是重连吧?

        连接上以后就保存到本地,下次扫描到这个设备的时候直接连接就可以了
      • AppleLSY:您好,请问下为什么我不能扫描指定UUID的设备,如果scan方法不写nil写确定的UUID的话就不往下走了.我现在需要做的就是第一次连接后绑定外设,以后只会连接这个外设,但我一写上UUID就扫描不了,不知道怎么回事儿
      • 无聊的暧:你好 我想改写 蓝牙设备的特征,一次可以改多个特征吗?
        无聊的暧:@煜寒了 谢谢
        煜寒了:@__暧 这个没有尝试过,看官方文档怎么说,如果不行的话那就不可以了
      • 9939d0a134dd:打开蓝牙后执行 [_manager scanForPeripheralsWithServices:nil options:nil];并没有扫描到设备是怎么回事,没有执行后面的代理方法 获取不到设备信息
        达若漠沙:@强仔1993 首先要判断中心设备的蓝牙是否已经打开,只有在打开的情况下后面的代码才会执行
        煜寒了:@强仔1993 这个不好说,按照步骤来的话是没有问题的
      • 01dd9b08616d:楼主大神,,在这里 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
        {

        报错:Error Domain=CBATTErrorDomain Code=6 "The request is not supported." UserInfo={NSLocalizedDescription=The request is not supported.
        这个是什么原因啊 ,,已经困扰我后几天啦!
        煜寒了:@谁是谁的小心肝 https://github.com/paypal/gatt/issues/16
        参考这个链接,看是否能解决你的问题
      • Scorpio_糖果屋:楼主好,我想请问一下,公司给我一个硬件,叫我把里面的数据读取出来,我就做了一个简单蓝牙Demo,然后放到手机上,打开蓝牙搜索时,一直搜索不到那个硬件。我想咨询一下是什么问题,给个思路
        煜寒了:@Scorpio_糖果屋 不是的,你在discover设备的时候就可以知道他的uuid的
        Scorpio_糖果屋:楼主,如果我想知道硬件设备的UUID,我是否可扫描外设获取它的特征值,然后再打印出来,是在这个方法打印吗-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
        煜寒了:@Scorpio_糖果屋 你要先确保这个设备没有被其他蓝牙连接上,然后就是搜索的时候不要过滤,看看周围的设备
      • 126de1e92f10:连接多个外设怎么实现啊?
        煜寒了:@126de1e92f10 这个没有研究过~
      • Mrlyee_Liu:请问一下 当我的蓝牙设备离手机很远的时候 是怎么判断手机已经和设备断开连接了
        煜寒了:@Mrlyee_Liu 断开连接的时候,系统会有回调的,你仔细看看那些代理方法就明白了
      • cb5815601b11:楼主好!我现在做的一款产品,同时需要连接多台BLE设备,麻烦一下,能提供个思路吗?谢谢啦
        eb59f8b6f06e:@开小飞 iOS允许一个Center连接多个Peripheral的,可以同时向多台设备发送命令
        开小飞:你好,我这也有这个需求,不知道你解决了多设备连接没有。是否可以用同一个中央实例去连接多个周边呢? :stuck_out_tongue_closed_eyes: 我这做安卓的小伙子告诉我,一个中央实例只能连接一个周边,如果想连接多个周边的话,需要在创建几个中央实例。。。不知道iOS这边是否是这样呢?
        煜寒了:@ifziv 不好意思 这个没有做过的,不太清楚
      • 2d3eeca10b49:你好,我想要用设备名与密码解锁,看了你的文档,有个鉴权,但是我这边用了这个会出现这个错误 /SourceCache/CoreBluetooth/CoreBluetooth-109/CBPeripheral.m:348,是什么原因啊
        煜寒了:@hahaku 我们这里的鉴权其实是和蓝牙设备定义好的一套加密机制,对于每台设备初次绑定随意生成密码,然后设备会存起来,下次连接就需要使用这个密码鉴权,然后才可以读取设备里的数据~
      • duanmeng:请问一下,我两台手机链接 UUID 都不一样,您那具体怎么解决的能说明下嘛?
        duanmeng:在此麻烦一下,现在碰到一个问题,是新的手机 搜索周边设备,得到的设备名称会是空,但是蓝牙设备已经设置名字了,我这边链接上设备,然后发送读取设备信息命令以后,在搜索设备的话就能搜索出来名字,不知道您碰到过不
        煜寒了:@duanmeng 这里的问题其实很简单。
        第一,你如果能拿到蓝牙设备的mac地址,或者设备给你提供的任意一个对于设备来讲可以唯一标识的identifier,厂商可以做的,那你就可以将这个标识传给服务器,通过账户体系拿到唯一标识后,再和周边的设备做匹配。
        第二个方法和第一个类似,设备在被你连接以后,可以通过广播,将设备和手机的mac地址发给你,当然你只需要设备的mac地址,后续的操作类似。
        希望对你有所帮助~
      • 会飞的比目鱼:你好,想问一下,正在做蓝牙防丢开发,想问一下,扫描到多个设备并且连接后,怎样分别对他们进行写数据的操作,现在只能对一个设备进行写数据操作,其他的设备没有反应!代码是这样的
        [_nDevices[1] writeValue:[NSData dataWithBytes:&data length:1] forCharacteristic:_nCharacteristics[1] type:CBCharacteristicWriteWithResponse];
        NSLog(@"已经向外设%@的特征值%@写入数据",_peripheral.name,_writeCharacteristic.description);
        是和特征是有关么??
        谢谢解答
        煜寒了:@会飞的比目鱼 如果我没猜错,你的_nDevices[1] 就是你的其中一个外设,向多个外设写数据,就切换这个参数就好了~ 也有可能我误解了你的意思
      • theAnswer_:楼主你好,我想问一下,假如我需要跟一台新打印机连接,但是我不哪一个知道他哪个Characteristic 可以打印出数据,我在写代码的时候,应该怎么处理这种情况,让他来适配所有支持iOS的打印机?
        theAnswer_:@煜寒了 没关系,楼主还有一个问题,ios能搜索到的蓝牙太少了,很多设备都搜索不到,这个有解决办法吗
        煜寒了:@5ab150ac7fc7 感觉好难,这个不了解,不好意思哈
      • 465435c1af8c:请问下我最近遇到个问题,就是在连接上设备后一段时间没数据传输它就自动断开了,这个是什么原因呢?
        煜寒了:@火山骑士 断线重连是可以的,你在disconnect 的回调里再调用connect 的方法就好了,蓝牙这块系统的设计就是这样,只要调用了connect 这个方法,系统会一直帮你连接这个设备,直到连接成功或失败才结束~
        Kaaaaai:@煜寒了 我也遇到这个问题,好像蓝牙连接就是不怎么稳定,你知道,怎么保持稳定的蓝牙连接,或者,断线重连吗?
        煜寒了:@一只练字的程序员 这个不太清楚的,😓
      • 4bc45e0b6853:有没有做过固件升级,有的话分享一下思路,谢谢
        煜寒了:@晨曦888 没有做过的,不好意思哈
      • 静持大师:博主,[peripheral readValueForCharacteristic:<#(nonnull CBCharacteristic *)#>]; 和 [peripheral setNotifyValue:<#(BOOL)#> forCharacteristic:<#(nonnull CBCharacteristic *)#>] 这两个方法有什么不同,他们在获得characteristic后都可以进入didUpdateValueForCharacteristic:代理方法
        静持大师:@煜寒了 多谢博主的点拨!
        煜寒了:@静持大师 第一个是主动读取特征值,第二个是监听,特征值变化的时候会发广播
      • 静持大师:博主,我做的是血糖仪, 我在- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error这个方法获取特征,一些普通特征如设备信息等都能获得,但获取血糖数据都会提示Error Domain=CBATTErrorDomain Code=2 "Reading is not permitted." 请问怎么回事
        3d2745bb1a8f:@静持大师 大兄弟,你的血糖仪蓝牙连接Demo能给我参考参考吗?
        煜寒了:@静持大师 看返回的意思是读取不允许,你们的设备读取有鉴权吗?会不会是设备那边返回的
      • ddee4adf4bb0:您好,我想问您的是,很多一个批次生产出来的的蓝牙设备,是peripheral的identifier不一样,还是服务和特征的uuid不一样?
        leftwater:@煜寒了 你的意思是说 外设id不一样而已,服务和特征是一样的吗?不是uuid生成的吗?
        还有,一般这种类型的项目,我们会提前知道服务id和对应的读写特征id吗? 假如后台只给一个单独的外设id,我们可以过滤扫描的设备,然后服务id和特征id,怎么知道往哪个特征中读和写了??
        煜寒了:@炙热镰仓 peripheral的identifier是不一样的,每个设备都有一个唯一的identifier,服务和特征对于同一款设备来说要一样,不然你怎么知道这是不是你们的设备呢?服务主要为了识别是不是你家的设备,特征主要用于一些读取操作,这是比较简单的解释哈
        ddee4adf4bb0:非常想得到您的指点,我的qq是946567682,望加好友
      • 肖无情:你好,我用蓝牙向打印机发送数据时byte 的个数怎么控制,打印出来的支付宝二维码可以用,微信的总是有问题,扫描的结果总是过期
        煜寒了:@肖无情 不好意思 ,这个不太清楚额
      • 上帝很忙:您好,在NSObject类里面 可以实现蓝牙的功能么? 从初始化开始. 我用NSObject来做 初始化后 不会调用状态判断方法.
        煜寒了:@上帝很忙 我觉得这个和在什么地方初始化是没有关系的,有设置代理应该可以执行
        上帝很忙:是的 初始化后 -(void)centralManagerDidUpdateState:(CBCentralManager *)central
        并没有执行 后续操作也没反应
        煜寒了:@上帝很忙 没看懂你的意思,你是说没有执行代理方法吗?
      • 潘柏信:请问一下,我不知道 我旁边设备的UUID,我能通过什么方式把UUID扫描出来吗?
        煜寒了:@叶舞清风 你搜索的时候不设置服务特征,周围的设备都能扫出来的,不要过滤
        叶舞清风:@煜寒了 我搜不到设备啊,用了很多方法就是搜不到设备,也不知道是不是手机问题
        煜寒了:@潘柏信 扫描周围所有的设备就可以
      • 042a0e1be73f:-(void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error这个方法好像弃用了。。
        煜寒了:@iiOS 恩是的 现在是读取rssi值
      • 042a0e1be73f:谢谢分享!请问@[[CBUUID UUIDWithString:@"FF15"]]中的FF15是什么?
        煜寒了:@iiOS 可以的
        042a0e1be73f:@煜寒了 可以通过中心设备扫描到的信息找到这个值吗?
        煜寒了:@iiOS 这个是服务特征,和设备商定的一个值,就是说周围有这个服务的大部分是我们的设备,当然可能会和别人重复,不过没关系,这已经筛选了绝大多数设备了
      • 0x0F:你好,我要连接iBeacon设备,UUID知道的,请问根据这个库,去做怎么样的修改?谢谢
        H5:请问如何判断俩条蓝牙广播是同一个蓝牙外设发出来的
        0x0F: @煜寒了 谢谢
        煜寒了:@Manloff UUID不同的手机和相同的设备是不同的,对同一个手机来说是唯一识别的。这个项目里,你改一下服务特征什么的就可以搜到你的iBeacon设备了,其他的操作可以按照自己的流程走
      • 我是卖报滴小行家:我的搜索不到周边的iphone设备是怎么回事啊????
        煜寒了:@我不爱白开水 应该是没有连接上吧,可能你的设备被占用了,只能连接一个手机,

        - (void) centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
        {
        NSLog(@"Attempted connection to peripheral %@ failed: %@", [peripheral name], [error localizedDescription]);

        QWSDevControlService *device = [self searchDeviceWithUUID:peripheral.identifier.UUIDString];

        if (device == nil) {
        return;
        }

        [self onConnectionStateChange:peripheral status:device.mState newState:STATE_INVALID];
        }

        看下这个方法会不会走,打印出log看一下error
        我是卖报滴小行家:@煜寒了我在- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI这个代理方法中,执行这个方法后
        [centralManager connectPeripheral:peripheral options:nil],却没有走后面的任何一个代理方法- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral?也没有走链接不上的代理方法, 你知道为什么吗? 我用的lightblue 做的测试,让5s座位中心设备!
        煜寒了:@我不爱白开水 这个不太清楚耶 这篇只是做了ble智能设备的链接,iphone之间没有尝试
      • bc998161500d:我是一个小菜鸟,正在做pos机刷卡支付,请问下如何写入数据让pos机能进入到提示刷卡界面?
        煜寒了:@老婆爱吃臭豆腐 参考一下,原理上应该差不多 http://www.jianshu.com/p/2d624044a27b
      • Faith_K:在苹果设备里面生成一个二维码 通过蓝牙打印机打印出来 应该怎么做啊.
        煜寒了:@93e7222c496f 参考一些这位朋友的文章 http://www.jianshu.com/p/2d624044a27b
      • 743a704f9327:如何给两个不同的特征值写入数据
        煜寒了:@贺子东V

        - (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;

        煜寒了::type:
        煜寒了::type:
      • Han夜:你好,我想问一下蓝牙notify,read,write作用是什么?
        煜寒了:@柏斯特湾 其实就是A和B连接上以后,扫描到B的服务,然后读取到服务中的特征值,然后监听这个特征值的变化,B的特征值发生变化的时候,A就能收到通知
        柏斯特湾:我刚开始做这块,还请发个连接,详细看看notify和read的区别。不太明白上边你说的“当蓝牙A注册了蓝牙B中的某个服务(service)的某个特征(character)的notify的时候”这句话。
        煜寒了:@c3f789b8a9ee 具体作用请谷歌。我只是简单说一下我的想法,
        notify是BLE 协议中的,当蓝牙A注册了蓝牙B中的某个服务(service)的某个特征(character)的notify的时候,B的这个character特征值变化的时候发出的通知都会被A接收到,然后从read的代理方法中读取收到的值,至于是什么值就要根据character来判断了。
        所以read主要就读特征值了。
        write主要就是给B的某个character写了。

        了解更多更深入还是谷歌看看吧!
      • 388cbd108a72:请问 我开发的app 在iphone 5s 上面能够连接到外设, 但是在iphone 6 上面却一直刚连上就断开,回来的错误是 “Error Domain=CBErrorDomain Code=6 "The connection has timed out unexpectedly."
        请问这是为什么呢?
        煜寒了:@kuangkuang 具体什么原因我也不太清楚,不过我看到一个提问上的答复,你看看是不是和这个有关系
        http://stackoverflow.com/questions/32588325/ios-bluetooth-le-code-6-the-connection-has-timed-out-unexpectedly/33542133#33542133
      • d275c374cdb7:我想知道怎么自动连接某个设备(其实就是如何获取某个设备的唯一标识,如蓝牙mac地址,设备id等),楼主的这些类似功能我也开发过,Android平台可以直接获取外设的蓝牙mac地址所以没问题,iOS遇到的问题是mac地址无法获取,设备id在不同的手机上获取到同一个外设的id是不同的,所以非常无助
        煜寒了:@铁城子 这个是不可以的,加密算法是未知的
        d275c374cdb7:@煜寒了 如果知道外设id与本机蓝牙mac地址,能算出外设mac地址吗?
        煜寒了:@铁城子 嗯这个问题我也考虑过,我是这样处理的,因为设备的id是由双方mac地址加密唯一确定的,所以对于手机来说的话,保存下来的id对应的设备也是唯一的。因为蓝牙外设也是我们在开发,所以,我是在连接成功之后,外设能获取到手机的mac地址,外设会将这个地址在发给手机端,此时,我将获取到的手机mac地址,外设mac地址和用户账户绑定,同时传给服务器,当用户更换手机登录的时候,打开蓝牙,手机会遍历周边的设备,根据用户账户提供的外设mac地址,先尝试连接,再由外设确认匹配,以达到不同手机和同一外设的自动连接效果,希望对你有所帮助
      • 我是谁_你是谁:你好,你的文章开头说 我的iPhone手机的蓝牙Mac地址 , 我想问一下 手机的 蓝牙Mac 地址是如何获取的? 如果能获取 手机的 蓝牙设备名称是不是也可以获取得到? 谢谢
        煜寒了:@munger 获取本机的蓝牙信息到不清楚怎么做,不过蓝牙设备名称和设备名称都是一样的。不知道这个能不能帮助到你,别的信息指的是什么?
        我是谁_你是谁:@煜寒了 扫描周边设备确实都可以找到,但是我现在遇到一个问题就是要使用自己本机的蓝牙设备名称或别的信息,有没有方法可以解决这个问题??有没有什么见解?
        煜寒了:@munger 现在只能通过uuid来唯一识别设备,mac地址已经被苹果加密,无法获取到
      • 煜寒了:蓝牙搜索完成之后

        - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI

        在这个函数里调用 [centralManager connectPeripheral:peripheral options:nil];

        到此就开始连接这个蓝牙设备了

        接下来就是这个代理方法 - (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral

        在这些方法中,都会存在一个peripheral实例

        你可以实例化一个CBPeripheral的对象在第一步搜到设备的时候就保存起来,方便后面使用
      • 549b6fe2394d:谢谢,Jianer. but 我想在应用启动后,即蓝牙已经搜索完成的时候调用 peripheral 该如何做?
      • 549b6fe2394d:hi,Jianer 如何获取peripheral?
        叶舞清风:大哥啊,我为什么搜不到设备啊
        煜寒了:@亚历山大吉吉
        //查到外设后,停止扫描,连接设备
        -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
        {
        [self updateLog:[NSString stringWithFormat:@"已发现 peripheral: %@ rssi: %@, UUID: %@ advertisementData: %@ ", peripheral, RSSI, peripheral.identifier, advertisementData]];

        _peripheral = peripheral;
        [_manager connectPeripheral:_peripheral options:nil];

        [self.manager stopScan];
        [_activity stopAnimating];

        BOOL replace = NO;
        // Match if we have this device from before
        for (int i=0; i < _nDevices.count; i++) {
        CBPeripheral *p = [_nDevices objectAtIndex:i];
        if ([p isEqual:peripheral]) {
        [_nDevices replaceObjectAtIndex:i withObject:peripheral];
        replace = YES;
        }
        }
        if (!replace) {
        [_nDevices addObject:peripheral];
        [_bluetoothTable reloadData];
        }
        }

        这个协议方法里返回的就是peripheral

      本文标题:iOS-BLE蓝牙开发持续更新

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