iOS 蓝牙开发(4.0)

一、关于蓝牙开发的一些重要的理论概念

1.当前蓝牙开发所用的系统库为<CoreBluetooth/CoreBluetooth.h>
2.蓝牙外设必须为4.0及以上(2.0需要MFI认证),否则无法开发,蓝牙4.0设备因为低耗电,所以也叫做BLE。
3.CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心,就是你的苹果手机就是中心,外部蓝牙称为外设。
4.服务和特征(service and characteristic):简而言之,外部蓝牙中它有若干个服务service(服务你可以理解为蓝牙所拥有的能力),而每个服务service下拥有若干个特征characteristic(特征你可以理解为解释这个服务的属性)。
5.Descriptor(描述)用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的单位。

二、蓝牙连接的主要步骤

1、创建一个CBCentralManager实例来进行蓝牙管理;
2、搜索扫描外围设备;
3、连接外围设备;
4、获得外围设备的服务;
5、获得服务的特征;
6、从外围设备读取数据;
7、给外围设备发送(写入)数据。

三、代码

//在Info.plist里加入访问蓝牙权限,否则上传AppStore会因为权限不足失败
//iOS13之前的蓝牙权限为Privacy - Bluetooth Peripheral Usage Description
//iOS13废弃了之前的蓝牙的权限,新加Privacy - Bluetooth Always Usage Description权限


image.png

1.初始化CBCentralManager实例

- (void)initCenterManager {
    if (self.centralManager == nil) {
        self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    }
}
#pragma mark - CBCentralManagerDelegate
/// 初始化成功后自动调用
/// 必须实现的代理,用来返回创建的centralManager的状态。
/// 通常在CBManagerStatePoweredOn状态下直接调用扫描外设的方法:
/// @param central 中心
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    self.isBlueToothPowerOn = NO;
    
    switch (central.state) {
        case CBManagerStateUnknown:
            NSLog(@">>>CBCentralManagerStateUnknown");
            break;
            
        case CBManagerStateResetting:
            NSLog(@">>>CBCentralManagerStateResetting");
            break;
            
        case CBManagerStateUnsupported:
            NSLog(@">>>CBCentralManagerStateUnsupported");
            break;
            
        case CBManagerStateUnauthorized:
            NSLog(@">>>CBCentralManagerStateUnauthorized");
            break;
            
        case CBManagerStatePoweredOff:
            NSLog(@">>>CBCentralManagerStatePoweredOff");
            break;
            
        case CBManagerStatePoweredOn:
            NSLog(@">>>CBCentralManagerStatePoweredOn");
            self.isBlueToothPowerOn = YES;
            // 搜索扫描外围设备
            [self scanDevices];
            break;
            
        default:
            break;
    }
}

2.搜索扫描外围设备

- (void)scanDevices {
    [self.scanDeviceArr removeAllObjects];
    
    if (self.isBlueToothPowerOn) {
        // 开始扫描周围的外设。
        /*
         -- 两个参数为nil表示默认扫描所有可见蓝牙设备。
         -- 注意:第一个参数是用来扫描有指定服务的外设。然后有些外设的服务是相同的,比如都有FFF5服务,那么都会发现;而有些外设的服务是不可见的,就会扫描不到设备。
         -- 成功扫描到外设后调用didDiscoverPeripheral
         */
        [self.centralManager scanForPeripheralsWithServices:nil options:nil];
    } else {
        NSLog(@">>>请到系统设置中打开蓝牙");
    }
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
    NSLog(@"%@",peripheral.name);
    if (!peripheral.name || !peripheral) {
        return;
    }
    
    //针对设备进行过滤(根据设备名称进行过滤,具体过滤规则看需求)
    if (![peripheral.name hasPrefix:@"xxx_xxx"]) {
        return;
    }
    
    if (self.scanDeviceArr.count == 0) {
        NSLog(@"Find New Device:%@", peripheral.description);
        [self.scanDeviceArr addObject:peripheral];
    } else {
        for (int i = 0 ; i < self.scanDeviceArr.count ; i ++) {
            CBPeripheral *peri = [self.scanDeviceArr objectAtIndex:i];
            if ([peri.name isEqualToString:peripheral.name]) {
                break;
            } else if (i == self.scanDeviceArr.count - 1) {
                NSLog(@"Find New Device:%@", peripheral.description);
                [self.scanDeviceArr addObject:peripheral];
            }
        }
    }

    //将设备信息传到外面的页面(VC), 构成扫描到的设备列表
    if (self.delegate && [self.delegate respondsToSelector:@selector(updateDeviceDataWithBluetoothArr:)]) {
        [self.delegate updateDeviceDataWithBluetoothArr:self.scanDeviceArr];
    }
}

3.连接外围设备

// 连接设备(.h中声明出去的接口, 一般在点击设备列表连接时调用)
- (void)connectPeripheral: (CBPeripheral *)peripheral {
    //连接成功后回调didConnectPeripheral
    //连接失败后回调didFailToConnectPeripheral
    [self.centralManager connectPeripheral:peripheral options:nil];
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@">>>连接外设成功");
    
    //连接成功后停止扫描,节省内存
    [central stopScan];
    
    peripheral.delegate = self;
    self.peripheral = peripheral;

    //获取外围设备服务
    [peripheral discoverServices:nil];
    
    if ([self.delegate respondsToSelector:@selector(didConnectBleDevice)]) {
        [self.delegate didConnectBleDevice];
    }
}

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@">>>连接外设失败");
    NSLog(@"%@", error);
    
    self.peripheral = nil;
}

4.取消连接外围设备

// 断开设备连接
- (void)disconnectPeripheral: (CBPeripheral *)peripheral {
    //断开连接后回调didDisconnectPeripheral
    [self.centralManager cancelPeripheralConnection:peripheral];
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@">>>取消与外设的连接回调");
    self.peripheral = nil;

    if (self.delegate && [self.delegate respondsToSelector:@selector(didDisconnectBleDevice)]) {
        [self.delegate didDisconnectBleDevice];
    }
}

5.获得外围设备的服务

- (void)getPeripheralServices: (CBPeripheral *)peripheral {
    //扫描外设的服务
    /**
     --外设的服务、特征、描述等方法是CBPeripheralDelegate的内容,所以要先设置代理peripheral.delegate = self
     --参数表示你关心的服务的UUID,比如我关心的是"FFE0",参数就可以为@[[CBUUID UUIDWithString:@"FFE0"]].那么didDiscoverServices方法回调内容就只有这两个UUID的服务,不会有其他多余的内容,提高效率。nil表示扫描所有服务
     --成功发现服务,回调didDiscoverServices
     */
    [peripheral discoverServices:nil];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    NSLog(@">>>发现服务回调");
    
    CBService * __nullable findService = nil;
    //遍历服务
    for (CBService *service in peripheral.services) {
        NSLog(@"UUID:%@",service.UUID.UUIDString);
        if ([service.UUID.UUIDString isEqualToString:@"xxx_xxx"]) {
            findService = service;
            break;
        }
    }
    
    if (findService) {
        // 获取服务下的特征
        [peripheral discoverCharacteristics:NULL forService:findService];
    }
}

6.获得服务下的特征

- (void)getCharacteristicsForService:(CBService *)service peripheral:(CBPeripheral *)peripheral {
    // 成功发现特征,回调didDiscoverCharacteristicsForService
    [peripheral discoverCharacteristics:NULL forService:service];   
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    NSLog(@">>>发现特征回调");
    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"UUID:%@",characteristic.UUID.UUIDString);
        if ([characteristic.UUID.UUIDString isEqualToString:@"xxx_xxx"]) {
            //特征
            self.character = characteristic;
            //回调didUpdateNotificationStateForCharacteristic
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            //回调didUpdateValueForCharacteristic
            [peripheral readValueForCharacteristic:characteristic];
            //当发现characteristic有descriptor,回调didDiscoverDescriptorsForCharacteristic
            [peripheral discoverDescriptorsForCharacteristic:characteristic];
        }
    }
}
  1. 获取特征下的描述
- (void)getDescriptorsForCharacteristics:(CBCharacteristic *)characteristic peripheral:(CBPeripheral *)peripheral {
    // 成功发现描述,回调didDiscoverDescriptorsForCharacteristic
    [peripheral discoverDescriptorsForCharacteristic:characteristic];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    NSLog(@">>>发现特征描述回调");
}

8.从外围设备读取数据

#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    NSLog(@">>>读取setNotifyValue成功");
}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    NSLog(@">>>读取setNotifyValue成功");
}

9.给外围设备发送(写入)数据。

- (void)writeData: (NSData *)data {
    //data必须经过16进制数字符串转换
    if (data == nil) {
        return ;
    }
    
    // 将指令写入蓝牙
    if (self.writeCharacter == nil) {
        return ;
    }

    //写入数据成功后回调didWriteValueForCharacteristic
    [self.peripheral writeValue:data forCharacteristic:self.writeCharacter type:CBCharacteristicWriteWithResponse];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    NSLog(@">>>数据写入外围设备成功回调");
    NSLog(@"----------%@",characteristic);
}

参考文献://www.greatytc.com/p/0c31487bb7c5

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352