ios蓝牙开发

首先先明白几个名词:Central(中心设备)、Peripheral(外围设备)、advertising(广告)、Services(服务)、Characteristic(特征)

新建Central Manager实例进行蓝牙管理
1、搜索外围设备
2、连接外围设备
3、获得外围设备的服务
4、获得服务的特征
5、给外围设备发送数据
6、从外围设备读数据 

CoreBluetooth介绍
在CoreBluetooth中有两个主要的部分,Central和Peripheral,CBPeripheralManager 作为周边设备。CBCentralManager作为中心设备。所有可用的iOS设备可以作为周边(Peripheral)也可以作为中央(Central),但不可以同时既是周边也是中央。

周边设备(Peripheral)设备是广播设备的数据,中央设备(Central)是管理并且使用这些数据的设备。
也就是说周边(Peripheral)向周围发送广播,告诉周围的中央设备(Central)它(周边(Peripheral)这里有数据,并且说明了能提供的服务和特征值(连接之后才能获取),
其实蓝牙传值相当于网络接口,硬件的service的UUID加上characteristic的UUID,
打一个比喻:service的UUID相当于主地址,characteristic的UUID相当于短链接,短链接必须是主地址的分支,拼在一起的是接口,你和硬件设定的蓝牙传输格式类似于json,双方可识别的数据,因为蓝牙只能支持16进制,而且每次传输只能20个字节,所以要把信息流转成双方可识别的16进制
实现方法介绍

引用框架、设置枚举

#import <CoreBluetooth/CoreBluetooth.h>
自定义设置枚举状态

typedef NS_ENUM(NSInteger, BluetoothState){
    BluetoothStateDisconnect = 0,
    BluetoothStateScanSuccess,
    BluetoothStateScaning,
    BluetoothStateConnected,
    BluetoothStateConnecting
};

typedef NS_ENUM(NSInteger, BluetoothFailState){
    BluetoothFailStateUnExit = 0,
    BluetoothFailStateUnKnow,
    BluetoothFailStateByHW,
    BluetoothFailStateByOff,
    BluetoothFailStateUnauthorized,
    BluetoothFailStateByTimeout
};

设置代理、设置变量

@interface TableViewController () <CBCentralManagerDelegate,CBPeripheralDelegate>

@property (strong , nonatomic) CBCentralManager *manager;//中央设备
@property (assign , nonatomic) BluetoothFailState bluetoothFailState;
@property (assign , nonatomic) BluetoothState bluetoothState;
@property (strong , nonatomic) CBPeripheral * discoveredPeripheral;//周边设备
@property (strong , nonatomic) CBCharacteristic * characteristic1;//周边设备服务特性
@property (strong , nonatomic) NSMutableArray * deviceMutableArray;

@end

显示蓝牙设备

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    
    // 将蓝牙外设对象接出,取出name,显示
    //蓝牙对象在下面环节会查找出来,被放进BleViewPerArr数组里面,是CBPeripheral对象
    CBPeripheral *per=(CBPeripheral *)self.deviceMutableArray[indexPath.row];
    cell.textLabel.text = per.name;
    return cell;
}

创建实例,设置代理,创建数组管理外设,

- (NSMutableArray *)deviceMutableArray {
    if (!_deviceMutableArray) {
        _deviceMutableArray = [NSMutableArray arrayWithCapacity:10];
    }
    return _deviceMutableArray;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.tableFooterView = [[UIView alloc] init];
    self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}

开始扫描

- (IBAction)scanBluetoothDeviceAction:(id)sender {
    [self scan];
}

- (void)scan { 
    //判断状态开始扫瞄周围设备 第一个参数为空则会扫瞄所有的可连接设备  你可以
    //指定一个CBUUID对象 从而只扫瞄注册用指定服务的设备
    [self.manager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@NO}];
    //记录目前是扫描状态
    self.bluetoothState = BluetoothStateScaning;
    //清空所有外设数组
    [self.deviceMutableArray removeAllObjects];
    //如果蓝牙状态未开启,提示开启蓝牙
    if(_bluetoothFailState == BluetoothFailStateByOff) {
        NSLog(@"%@",@"检查您的蓝牙是否开启后重试");
    }
}

pragma mark - 检测蓝牙状态

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    
    if (central.state != CBCentralManagerStatePoweredOn) {
        NSLog(@"fail, state is off.");
        switch (central.state) {
            case CBCentralManagerStatePoweredOff:
                NSLog(@"连接失败了\n请您再检查一下您的手机蓝牙是否开启,\n然后再试一次吧");
                _bluetoothFailState = BluetoothFailStateByOff;
                break;
            case CBCentralManagerStateResetting:
                _bluetoothFailState=BluetoothFailStateByTimeout;
                break;
            case CBCentralManagerStateUnsupported:
                NSLog(@"检测到您的手机不支持蓝牙4.0\n所以建立不了连接.建议更换您\n的手机再试试。");
                _bluetoothFailState = BluetoothFailStateByHW;
                break;
            case CBCentralManagerStateUnauthorized:
                NSLog(@"连接失败了\n请您再检查一下您的手机蓝牙是否开启,\n然后再试一次吧");
                _bluetoothFailState = BluetoothFailStateUnauthorized;
                break;
            case CBCentralManagerStateUnknown:
                _bluetoothFailState = BluetoothFailStateUnKnow;
                break;
            default:
                break;
        }
        return;
    }
    _bluetoothFailState = BluetoothFailStateUnExit;
}

中央设备开始扫描之后,我们需要实现 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 通过该回调来获取发现设备。
这个回调说明着广播数据和信号质量(RSSI-Received Signal Strength Indicator)的周边设备被发现。通过信号质量,可以用判断周边设备离中央设备的远近。

- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI
{
    if (peripheral == nil||peripheral.identifier == nil/*||peripheral.name == nil*/)
    {
        return;
    }
    NSString *pername=[NSString stringWithFormat:@"%@",peripheral.name];
    //判断是否存在@"你的设备名"
    NSRange range=[pername rangeOfString:@"你的设备名"];
    //如果从搜索到的设备中找到指定设备名,和BleViewPerArr数组没有它的地址
    //加入BleViewPerArr数组
    if(range.location!=NSNotFound&&[self.deviceMutableArray containsObject:peripheral]==NO){
        [self.deviceMutableArray addObject:peripheral];
    }
    _bluetoothFailState = BluetoothFailStateUnExit;
    _bluetoothState = BluetoothStateScanSuccess;
    [_tableView reloadData];
}

扫描设备输出台log:

<CBPeripheral: 0x14e625f80, identifier = 954DBF72-104A-E041-19F8-D9538DBA7C23, name = brand, state = disconnected>
蓝牙广播中可以携带一些信息,最好将mac 地址也一并在这里广播出来!!!
扫描设备advertisementData 输出台log :

Printing description of advertisementData:
{
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = "brand";
    kCBAdvDataTxPowerLevel = 2;
}

扫描到设备之后当然是链接设备了,这里会有一个UITableView,在tableview点击方法里写连接方法

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    CBPeripheral *peripheral=(CBPeripheral *)self.deviceMutableArray[indexPath.row];
    //设定周边设备,指定代理者
    _discoveredPeripheral = peripheral;
    _discoveredPeripheral.delegate = self;
    //连接设备
    [_manager connectPeripheral:peripheral
                        options:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@YES}];
}

说明 : 点击单元格连接对应的设备,连接该设备 调用完该方法后会调用代理- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral表示连接上了设备
连接失败会调用

已经连接上该设备,就可以获取当前设备的信息

// 获取当前设备
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"%@",peripheral);
    
    // 设置设备代理
    [peripheral setDelegate:self];
    // 大概获取服务和特征
    [peripheral discoverServices:nil];
    
    //或许只获取你的设备蓝牙服务的uuid数组,一个或者多个
    //[peripheral discoverServices:@[[CBUUID UUIDWithString:@""],[CBUUID UUIDWithString:@""]]];
    
    
    NSLog(@"Peripheral Connected");
    
    [_manager stopScan];
    
    NSLog(@"Scanning stopped");
    
    _bluetoothState=BluetoothStateConnected;
    
}

各种服务
说明:在这个方法中我们要查找到我们需要的服务 然后调用discoverCharacteristics方法查找我们需要的特性
该discoverCharacteristics方法调用完后会调用代理CBPeripheralDelegate的- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error

// 获取当前设备服务services
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error) {
        NSLog(@"Error discovering services: %@", [error localizedDescription]);
        return;
    }
    
    NSLog(@"所有的servicesUUID%@",peripheral.services);

    //遍历所有service
    for (CBService *service in peripheral.services)
    {
        
        NSLog(@"服务%@",service.UUID);
        
        //找到你需要的servicesuuid
        if ([service.UUID isEqual:[CBUUID UUIDWithString:@"你的设备服务的uuid"]])
        {
            //监听它
            [peripheral discoverCharacteristics:nil forService:service];
        } 
    }
    NSLog(@"此时链接的peripheral:%@",peripheral); 
}

特征获取
说明:在这个方法中我们要找到我们所需的服务的特性 然后调用setNotifyValue方法告知我们要监测这个服务特性的状态变化 当setNotifyValue方法调用后调用代理CBPeripheralDelegate的

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    
    if (error)
    {
        NSLog(@"Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
        return;
    }
    NSLog(@"服务:%@",service.UUID);
    // 特征
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        NSLog(@"%@",characteristic.UUID);
        //发现特征
        //注意:uuid 分为可读,可写,要区别对待!!!
        
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]])
        {
            NSLog(@"监听:%@",characteristic);//监听特征
            //保存characteristic特征值对象
            //以后发信息也是用这个uuid
            _characteristic1 = characteristic;
            
            [_discoveredPeripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
        
        //当然,你也可以监听多个characteristic特征值对象
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]])
        {
            //同样用一个变量保存,demo里面没有声明变量,要去声明
//            _characteristic2 = characteristic;
//            [peripheral setNotifyValue:YES forCharacteristic:_characteristic2];
//            NSLog(@"监听:%@",characteristic);//监听特征
        }
    }
}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error)
    {
        NSLog(@"Error updating value for characteristic %@ error: %@", characteristic.UUID, [error localizedDescription]);
        return;
    }
    
    NSLog(@"收到的数据:%@",characteristic.value);
}

蓝牙传值
蓝牙写到这里,基本用法已经说明,代码千变万变,思路不变,接下来介绍传值,因为我的项目是手环,我就以我们硬件工程师的蓝牙接口文档来介绍如何和硬件交互,
其实蓝牙传值相当于网络接口,硬件的service的UUID加上characteristic的UUID,
打一个比喻:service的UUID相当于主地址,characteristic的UUID相当于短链接,短链接必须是主地址的分支,拼在一起的是接口,你和硬件设定的蓝牙传输格式类似于json,双方可识别的数据,因为蓝牙只能支持16进制,而且每次传输只能20个字节,所以要把信息流转成双方可识别的16进制

下面是手环的接口文档
APP请求运动模式基础数据传输时拆分的总包数

字节序号 参数值
0 0xa5
1 0x06
2 0x03
3~4 2字节的时间,如1月2日用0x0102表示。
5 异或校验和
由此看出:0、1、2字节都是固定的,3字节是月(16进制),4字节是日(16进制),5字节是异或校验和

那么:

// APP请求运动模式基础数据传输时拆分的总包数
- (NSData *)sportBao
{
    Byte reg[6];
    reg[0]=0xa5;
    reg[1]=0x06;
    reg[2]=0x03;
    reg[3]=0x01;
    reg[4]=0x02;
    reg[5]=(Byte)(reg[0]^reg[1]^reg[2]^reg[3]^reg[4]);
    NSData *data=[NSData dataWithBytes:reg length:6];
    return data;
}

这时候,要把请求命令发送给手环,发送给刚才纪录的discoveredPeripheral的蓝牙设备的characteristic1的特征值

// 获取总包数
- (void)getAltogether
{

    //生成总包数data
    NSData *d1 = [self sportBao];
    NSLog(@"写%@",d1);
    NSLog(@"%@",discoveredPeripheral);
  [discoveredPeripheral writeValue:d1 forCharacteristic:characteristic1 type:CBCharacteristicWriteWithResponse];
  
}

发送完成,手环会返回数据
数据是以下格式:

手环返回运动模式基础数据传输时拆分的总包数

字节序号 参数值
0 0xa5
1 0x06
2 0x83
3~4 该运动模式数据传输时拆分的总包数
5 异或校验和
由此看出:0、1、2字节都是固定的,3-4字节是总包数(16进制),5字节是异或校验和

那么:我们需要转换3-4字节的16进制,得到总包数

- (void)SetAltogether:(NSData *)DayData
{
    Byte *testByte = (Byte *)[DayData bytes];
    if (DayData.length == 6) {
        //收到数据之后,要异或校验,看数据是否完整以及正确
        if (testByte[5]==(testByte[0]^testByte[1]^testByte[2]^testByte[3]^testByte[4]))
        {
            // 这里记录总包数
            int totalBao = 0;
            totalBao = testByte[4]*256+testByte[3];
        }
    }
}

解析数据的格式已经有了,那么在接收数据的时候

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (error)
    {
        NSLog(@"读失败");
        
        return;
    }
    NSLog(@"收到的数据 :%@",characteristic.value);

    NSString *str = [NSString stringWithFormat:@"%@",characteristic.value];
    
    
    // 运动总包数
    if (str.length>7&&[[str substringWithRange:NSMakeRange(1, 2)]isEqualToString:@"a5"]&&[[str substringWithRange:NSMakeRange(5, 2)]isEqualToString:@"83"]) {
        //调用解析总包数方法
        [self SetAltogether:characteristic.value]; 
    } 
}

数据传输介绍完毕,基本用法已经说明

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

推荐阅读更多精彩内容

  • 首先进一则广告: 蓝牙技术联盟(Bluetooth SIG)2010年7月7日宣布,正式采纳蓝牙4.0核心规范(B...
    L泽阅读 1,426评论 3 4
  • 这里我们具体说明一下中心模式的应用场景。主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般...
    丶逝水流年阅读 2,234评论 3 4
  • iOS的蓝牙数据接收以及发送 名词:Central(中心设备)、Peripheral(外围设备)、advertis...
    TianBai阅读 22,592评论 54 304
  • 这几天又异常兴奋,尤其是入睡困难,你一会儿就醒一会儿就醒的导致我太困,两天没有写简书,不过,算了,反正拿你没办法…...
    Denisezhao阅读 102评论 0 0
  • 如今回顾写作的过程,我发现自己始终在思考一个问题:站在人生的岔路口,人究竟应该怎么做?我希望读者能在掩卷时喃喃自语...
    小熙看世界阅读 1,065评论 0 50