欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS蓝牙开发(4.0)详解

程序员文章站 2022-07-05 14:54:18
最近由于项目需要, 一直在研究蓝牙4.0,在这儿分享给大家, 望共同进步. 一、关于蓝牙开发的一些重要的理论概念: 1.当前ios中开发蓝牙所运用的系统库是。 2.蓝牙外设必须为4.0及以上(2.0需要MFI认证),否则无法开发,蓝牙4. ......

最近由于项目需要, 一直在研究蓝牙4.0,在这儿分享给大家, 望共同进步.

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

1.当前ios中开发蓝牙所运用的系统库是<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变量特定的单位。

6.我们使用的蓝牙模块是在淘宝买的, 大概十多元一个, ios大概每次可以接受90个字节, 安卓大概每次可以接收20个字节, 具体数字可能会浮动, 应该是与蓝牙模块有关。

二、蓝牙连接的主要步骤

     1、创建一个cbcentralmanager实例来进行蓝牙管理;

     2、搜索扫描外围设备;

     3、连接外围设备;

     4、获得外围设备的服务;

     5、获得服务的特征;

     6、从外围设备读取数据;

     7、给外围设备发送(写入)数据。

 

三、代码

// 加入权限访问, 否则上传appstore会因为权限不足失败

iOS蓝牙开发(4.0)详解

1. 初始化

#import <corebluetooth/corebluetooth.h>
self.centralmanager = [[cbcentralmanager alloc] initwithdelegate:self queue:nil];

2. 搜索扫描外围设备

/**
 *  --  初始化成功自动调用
 *  --  必须实现的代理,用来返回创建的centralmanager的状态。
 *  --  注意:必须确认当前是cbcentralmanagerstatepoweredon状态才可以调用扫描外设的方法:
 scanforperipheralswithservices
 */
- (void)centralmanagerdidupdatestate:(cbcentralmanager *)central{
    switch (central.state) {
        case cbcentralmanagerstateunknown:
            nslog(@">>>cbcentralmanagerstateunknown");
            break;
        case cbcentralmanagerstateresetting:
            nslog(@">>>cbcentralmanagerstateresetting");
            break;
        case cbcentralmanagerstateunsupported:
            nslog(@">>>cbcentralmanagerstateunsupported");
            break;
        case cbcentralmanagerstateunauthorized:
            nslog(@">>>cbcentralmanagerstateunauthorized");
            break;
        case cbcentralmanagerstatepoweredoff:
            nslog(@">>>cbcentralmanagerstatepoweredoff");
            break;
        case cbcentralmanagerstatepoweredon:
        {
            nslog(@">>>cbcentralmanagerstatepoweredon");
            // 开始扫描周围的外设。
            /*
             -- 两个参数为nil表示默认扫描所有可见蓝牙设备。
             -- 注意:第一个参数是用来扫描有指定服务的外设。然后有些外设的服务是相同的,比如都有fff5服务,那么都会发现;而有些外设的服务是不可见的,就会扫描不到设备。
             -- 成功扫描到外设后调用diddiscoverperipheral
             */
            [self.centralmanager scanforperipheralswithservices:nil options:nil];
        }
            break;
        default:
            break;
    }
}
#pragma mark 发现外设
- (void)centralmanager:(cbcentralmanager *)central diddiscoverperipheral:(cbperipheral *)peripheral advertisementdata:(nsdictionary<nsstring *,id> *)advertisementdata rssi:(nsnumber *)rssi{
    nslog(@"find device:%@", [peripheral name]);
    if (![_devicedic objectforkey:[peripheral name]]) {
        nslog(@"find device:%@", [peripheral name]);
        if (peripheral!=nil) {
            if ([peripheral name]!=nil) {
                if ([[peripheral name] hasprefix:@"根据设备名过滤"]) {
                    [_devicedic setobject:peripheral forkey:[peripheral name]];
                     // 停止扫描, 看需求决定要不要加
//                    [_centralmanager stopscan];
                    // 将设备信息传到外面的页面(vc), 构成扫描到的设备列表
                    if ([self.delegate respondstoselector:@selector(datawithbluetoothdic:)]) {
                        [self.delegate datawithbluetoothdic:_devicedic];
                    }
                }
            }
        }
    }
}

3.连接外围设备

// 连接设备(.h中声明出去的接口, 一般在点击设备列表连接时调用)
- (void)connectdevicewithperipheral:(cbperipheral *)peripheral
{
    [self.centralmanager connectperipheral:peripheral options:nil];
}
#pragma mark 连接外设--成功
- (void)centralmanager:(cbcentralmanager *)central didconnectperipheral:(cbperipheral *)peripheral{
    //连接成功后停止扫描,节省内存
    [central stopscan];
    peripheral.delegate = self;
    self.peripheral = peripheral;
    //4.扫描外设的服务
    /**
     --     外设的服务、特征、描述等方法是cbperipheraldelegate的内容,所以要先设置代理peripheral.delegate = self
     --     参数表示你关心的服务的uuid,比如我关心的是"ffe0",参数就可以为@[[cbuuid uuidwithstring:@"ffe0"]].那么diddiscoverservices方法回调内容就只有这两个uuid的服务,不会有其他多余的内容,提高效率。nil表示扫描所有服务
     --     成功发现服务,回调diddiscoverservices
     */
    [peripheral discoverservices:@[[cbuuid uuidwithstring:@"你要用的服务uuid"]]];
    if ([self.delegate respondstoselector:@selector(didconnectble)]) {
       // 已经连接
        [self.delegate didconnectble];
    }
}
#pragma mark 连接外设——失败
- (void)centralmanager:(cbcentralmanager *)central didfailtoconnectperipheral:(cbperipheral *)peripheral error:(nserror *)error{
    nslog(@"%@", error);
}
#pragma mark 取消与外设的连接回调
- (void)centralmanager:(cbcentralmanager *)central diddisconnectperipheral:(cbperipheral *)peripheral error:(nserror *)error{
    nslog(@"%@", peripheral);
}

4. 获得外围设备的服务

#pragma mark 发现服务回调
- (void)peripheral:(cbperipheral *)peripheral diddiscoverservices:(nserror *)error{
    
    //nslog(@"diddiscoverservices,error:%@",error);
    cbservice * __nullable findservice = nil;
    // 遍历服务
    for (cbservice *service in peripheral.services)
    {
        //nslog(@"uuid:%@",service.uuid);
        if ([[service uuid] isequal:[cbuuid uuidwithstring:@"你要用的服务uuid"]])
        {
            findservice = service;
        }
    }
    nslog(@"find service:%@",findservice);
    if (findservice)
        [peripheral discovercharacteristics:null forservice:findservice];
}

5、获得服务的特征

#pragma mark 发现特征回调
/**
 --  发现特征后,可以根据特征的properties进行:读readvalueforcharacteristic、写writevalue、订阅通知setnotifyvalue、扫描特征的描述discoverdescriptorsforcharacteristic。
 **/
- (void)peripheral:(cbperipheral *)peripheral diddiscovercharacteristicsforservice:(cbservice *)service error:(nserror *)error{
    for (cbcharacteristic *characteristic in service.characteristics) {
        if ([characteristic.uuid isequal:[cbuuid uuidwithstring:@"你要用的特征uuid"]]) {
            
            /**
             -- 读取成功回调didupdatevalueforcharacteristic
             */
            self.characteristic = characteristic;
            // 接收一次(是读一次信息还是数据经常变实时接收视情况而定, 再决定使用哪个)
//            [peripheral readvalueforcharacteristic:characteristic];
            // 订阅, 实时接收
            [peripheral setnotifyvalue:yes forcharacteristic:characteristic];
            
            // 发送下行指令(发送一条)
            nsdata *data = [@"硬件工程师给我的指令, 发送给蓝牙该指令, 蓝牙会给我返回一条数据" datausingencoding:nsutf8stringencoding];
            // 将指令写入蓝牙
                [self.peripheral writevalue:data forcharacteristic:characteristic type:cbcharacteristicwritewithresponse];
        }

        /**
         -- 当发现characteristic有descriptor,回调diddiscoverdescriptorsforcharacteristic
         */
        [peripheral discoverdescriptorsforcharacteristic:characteristic];
    }
}

6.从外围设备读取数据

#pragma mark - 获取值
- (void)peripheral:(cbperipheral *)peripheral didupdatevalueforcharacteristic:(cbcharacteristic *)characteristic error:(nserror *)error{
    // characteristic.value就是蓝牙给我们的值(我这里是json格式字符串)
    nsdata *jsondata = [characteristic.value datausingencoding:nsutf8stringencoding];
        nsdictionary *datadic = [nsjsonserialization jsonobjectwithdata:jsondata options:nsjsonreadingmutablecontainers error:nil];
    // 将字典传出去就可以使用了
}

#pragma mark - 中心读取外设实时数据
- (void)peripheral:(cbperipheral *)peripheral didupdatenotificationstateforcharacteristic:(cbcharacteristic *)characteristic error:(nserror *)error{
    if (characteristic.isnotifying) {
        [peripheral readvalueforcharacteristic:characteristic];
    } else { 
        nslog(@"notification stopped on %@.  disconnecting", characteristic);
        nslog(@"%@", characteristic);
        [self.centralmanager cancelperipheralconnection:peripheral];
    }
}

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

// 上文中发现特征之后, 发送下行指令的时候其实就是向蓝牙中写入数据
// 例:
// 发送检查蓝牙命令
- (void)writecheckblewithble
{
    _style = 1;
    // 发送下行指令(发送一条)
    nsdata *data = [@"硬件工程师提供给你的指令, 类似于5e16010203...这种很长一串" datausingencoding:nsutf8stringencoding];
    [self.peripheral writevalue:data forcharacteristic:self.characteristic type:cbcharacteristicwritewithresponse];
}
#pragma mark 数据写入成功回调
- (void)peripheral:(cbperipheral *)peripheral didwritevalueforcharacteristic:(cbcharacteristic *)characteristic error:(nserror *)error{
    nslog(@"写入成功");
    if ([self.delegate respondstoselector:@selector(didwritesucesswithstyle:)]) {
        [self.delegate didwritesucesswithstyle:_style];
    }
}

8. 另外

// 扫描设备
- (void)scandevice
{
    if (_centralmanager == nil) {
    self.centralmanager = [[cbcentralmanager alloc] initwithdelegate:self queue:nil];
    [_devicedic removeallobjects];
     }
}
#pragma mark - 断开连接
- (void)disconnectperipheral{
    /**
     -- 断开连接后回调diddisconnectperipheral
     -- 注意断开后如果要重新扫描这个外设,需要重新调用[self.centralmanager scanforperipheralswithservices:nil options:nil];
     */
    [self.centralmanager cancelperipheralconnection:self.peripheral];
}
#pragma mark - 停止扫描外设
- (void)stopscanperipheral{
    [self.centralmanager stopscan];
}

由于硬件方面刚开始用蓝牙2.0跟我对接, 导致程序一直搜索不到设备.希望小伙伴们注意一下这个问题. 如果有不清楚的地方欢迎留言探讨.