iOS蓝牙之扫描、链接、读写数据(二)

接上篇iOS蓝牙之扫描、链接、读写数据(一)

一、关于蓝牙从连接到读发数据

在这篇文章中,我会按照上篇文章中介绍的流程,来代码实现:(由于自己也是第一次做蓝牙模块内容,有不对的地方还希望大神指正,小弟不胜感激......)

#import "BLEController.h"
//需要遵守蓝牙协议
#import <CoreBluetooth/CoreBluetooth.h>

#define kPeripheralName            @"B10001"//硬件设备蓝牙名称
#define kServiceUUID               @"0003CDD0-0000-1000-8000-00805F9B0131" //服务的UUID
#define kWriteCharacteristicUUID   @"0003CDD2-0000-1000-8000-00805F9B0131" //特征的UUID
#define kNotifyCharacteristicUUID  @"0003CDD1-0000-1000-8000-00805F9B0131" //特征的UUID

@interface BLEController ()<CBCentralManagerDelegate,CBPeripheralDelegate>
@property (nonatomic, strong) CBCentralManager *manager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBCharacteristic *character;
@end

1、创建蓝牙管理者对象

//第二个参数:nil默认为主线程
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

2、该方法当蓝牙状态改变(打开或者关闭)的时候就会调用

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state) {
        case CBManagerStateUnknown:{
            NSLog(@"未知蓝牙状态");
        }
            break;
        case CBManagerStateResetting:{
            NSLog(@"系统服务连接暂时丢失");
        }
            break;
        case CBManagerStateUnsupported:{
            NSLog(@"该设备不支持蓝牙");
        }
            break;
        case CBManagerStateUnauthorized:{
            NSLog(@"该设备蓝牙为被授权");
        }
            break;
        case CBManagerStatePoweredOn:{
            NSLog(@"该设备蓝牙已打开");
            //打开后,开始扫描
            [self scanBlueTooth];
        }
            break;
        case CBManagerStatePoweredOff:{
            NSLog(@"该设备蓝牙没有打开");
        }
            break;
    }
}

3、发现外设后调用的方法

//查到外设后,连接设备,停止扫描
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
    //可在该方法内部区分扫描到的蓝牙设备
    NSLog(@"发现外设peripheral:%@ =====RSSI:%@ =====UUID:%@", peripheral, RSSI, peripheral.identifier);
    
    //判断是否为你要连接的设备(我这里用设备名称判断的,扫描到我要的外设蓝牙名称后,停止扫描、连接外设)
    if ([peripheral.name isEqualToString:kPeripheralName]) {
        //扫描到设备之后停止扫描
        [_manager stopScan];
         //开始连接外设
        [_manager connectPeripheral:peripheral options:nil];
        _peripheral = peripheral;
    }
}

3.1、连接成功会被调用

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"=====>%@连接成功 =====>UUID: %@",peripheral.name,peripheral.identifier);
    //连接设备之后设置蓝牙对象的代理,扫描服务
    [self.peripheral setDelegate:self];

    // 外设发现服务,传nil代表不过滤
    // 这里会触发外设的代理 didDiscoverServices 方法
    [self.peripheral discoverServices:nil];

    //这是我们业务需求:0.2s给我们的采集卡设备发一次命令来读数
    [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(timered:) userInfo:nil repeats:YES];
}
//返回的蓝牙服务通知通过代理实现(已经发现服务)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    NSLog(@"=====>发现服务");
    //一个设备可能为多个服务,所以要取你需要读写的那个服务(我们的外设就一个服务)
    for (CBService *service in peripheral.services) {
        if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
            
            //根据你要的那个服务去发现特性
            [self.peripheral discoverCharacteristics:nil forService:service];
        }
    }
}

3.2、连接失败会被调用

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"=====>连接失败");
}

3.3、断开连接会被调用

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"=====>断开连接");
    //断开连接后,可能是因为信号不好,所以我们的需求是继续连接我们的设备
    [self scanBlueTooth];
}

4、获得外围设备的服务、获得服务的特征

//已经发现特性
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    //首先读到外设特征服务,写到宏定义
    NSLog(@"发现特征服务:%@",service.UUID);
    
    for (CBCharacteristic *c in service.characteristics) {
        //其次读到两个特征值,写到宏定义
        NSLog(@"特征UUID: %@",c.UUID);
        
        //哪个特征值是读的,哪个特征值是写的,一般硬件兄弟都会告诉你的
        //(即使没有告诉你,也可以自己在AppStore下载LightBlue或UsrBleAssistent蓝牙调试工具看到你需要读写的特征值)
        if ([c.UUID.UUIDString isEqualToString:kWriteCharacteristicUUID]) {
            //找到可写特征值D2
            _character = c;
            
        }else if ([c.UUID.UUIDString isEqualToString:kNotifyCharacteristicUUID]){
            //设置通知
            [_peripheral setNotifyValue:YES forCharacteristic:c];
        }
    }
}

5、从外围设备读数据

//获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (!error) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyCharacteristicUUID]]) {
            // 此处的byte数组就是接收到的数据
            NSString *str = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
            NSLog(@"原始数据:%@===>字符串:%@",characteristic.value,str);
        }
    }else{
        NSLog(@"error:%@", error);
    }
}

6、给蓝牙发送数据(及前边定时器的实现)

#pragma mark - 定时器时间事件
- (void)Timered:(NSTimer*)timer {
    //读信号强度
    [_peripheral readRSSI];
    //实时读数
    [self writeDataToBle:@"*APP.VALUE#$"];
}

//向蓝牙写入数
- (void)writeDataToBle:(NSString *)aStr{
    if (_character.properties & CBCharacteristicPropertyWrite) {
        //BLE设备接收的数据需要转为NSData类型
        NSData *data = [aStr dataUsingEncoding:NSUTF8StringEncoding];
        //第一个参数:你要写入的数据,为data类型。
        //第二个参数:你要向哪个特征写入,一般就是属性为写入的那个特征。
        //第三个参数:你写入的方式,这个参数有两个枚举分别是:
        //CBCharacteristicWriteWithResponse//写入之后有回应
        //CBCharacteristicWriteWithoutResponse//写入之后不需要回应
        //使用WithResponse的成功后就会调用:-(void)peripheral:(CBPeripheral )peripheral didWriteValueForCharacteristic:(CBCharacteristic )characteristic error:(NSError )error;
        [_peripheral writeValue:data forCharacteristic:_character type:CBCharacteristicWriteWithResponse];
    }else{
        NSLog(@"该字段不可写!");
    }
}

到此BLE4.0从扫描到读数,已经实现

二、关于蓝牙你需要知道

1、一般蓝牙的读写通道(Characteristics)都是分开的,少数蓝牙会读写用同一个通道,在发现目标通道后,我们要利用写通道来向蓝牙写入数据,用读通道来读取蓝牙发来的包。

2、对于外设的BLE的唯一标识,亲测同一设备的 UUID 对于每台iOS设备都不一样,只能尽量保证设备的唯一性;特别是自动重连的过程,让用户没有感知
(在此工程中,我是根据外设的名字来判断链接的!因为我们的BLE外设名称都是以公司名称字母开头的!)

3、关于BLE的Mac:在开发过程中发现CBCentralManager 和 CBPeripheral 里边都找不到和Mac地址相关的字段,所以如果领导要外设的Mac,你大胆的告诉他:你无能为力!
(但有个解决办法:你可以与硬件工程师商量,能否将BLE的Mac地址写在服务里,这样可以为自动连接的唯一性做准备)

4、关于Read还是Notify的区别:
比如说我现在连接的是一个骑行设备上的蓝牙,此设备上有一个照明灯,现在APP可以控制这个灯,我发送开启的命令之后,如果开启成功那么设备会返回给我一个开启成功的信息,此时就是读取。
而通知呢就是:APP需要实时显示该设备的速度,我不需要向设备发送任何的指令,只要它的速度发生了变化,就会向APP发送此时的速度信息(关于区别这块内容是百度其他作者的,不知对不对)

当然关于BLE的实现也有好多优秀的三方库实现:比如 BabyBluetooth
BabyBluetooth是一个最简单易用的蓝牙库,基于CoreBluetooth的封装,并兼容iOS和Mac OS X。
BabyBluetooth的优点:
1、基于原生CoreBluetooth框架封装的轻量级的开源库,可以帮你更简单地使用CoreBluetooth API。
2、CoreBluetooth所有方法都是通过委托完成,代码冗余且顺序凌乱。BabyBluetooth使用block方法,可以重新按照功能和顺序组织代码,并提供许多方法减少蓝牙开发过程中的代码量。
3、链式方法体,代码更简洁、优雅。
4、通过channel切换区分委托调用,并方便切换
5、完善的文档,且项目处于活跃状态,不断的更新中
6、github上star最多的纯Bluetooch类库(非PhoneGap和SensorTag项目)

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