IOS 链接多个蓝牙设备,收发数据

这段时间一直在做关于蓝牙的项目,有点心得,拿出来和大家分享下。希望对大家有帮助,废话不多说。直接上代码

屏幕快照 2016-08-05 下午8.11.17.png

首先,我在ViewController这个类中主要做的是蓝牙的搜索,链接,及断开。在LinkViewController这个类中,设置蓝牙外设的代理人,打开订阅的通道,给外设发数据。(我这个蓝牙是一问一答模式)leftView和rightView分别是作为两个蓝牙的代理人,来设置写入特征和订阅特征并且接收蓝牙数据

下面代码中的全局实例对象cell仅是为了在tableView中展示的时候区别链接与未链接。
#import "ViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import "LinkViewController.h"

@interface ViewController ()<CBCentralManagerDelegate,UITableViewDelegate,UITableViewDataSource>
{
UITableViewCell *cell;
}

@property (nonatomic,strong ) CBCentralManager *manager;// 中心设备
@property (nonatomic,strong ) NSMutableArray   *devices;// 扫描到的外围设备
@property (nonatomic, strong) NSMutableArray   *connectSuccess;//链接成功的的外设
@property (weak, nonatomic  ) IBOutlet UITableView      *tableView;

@end

在ViewDidLoad中对中心设备及可变数据进行初始化。
- (void)viewDidLoad {
cell = [[UITableViewCell alloc] init];
[super viewDidLoad];
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
self.devices = [NSMutableArray array];
self.connectSuccess = [NSMutableArray array];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
下面代码是扫描外设以及停止扫描
- (IBAction)startScan:(id)sender {
//扫描外设
[self.manager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];
//3秒后停止。(开启扫描后会不停的扫描)
[self performSelector:@selector(stopScan) withObject:nil afterDelay:3];
}
/**
* 停止扫描
*/
-(void)stopScan{
[self.manager stopScan];
}
蓝牙的代理(蓝牙的状态改变的代理,扫描外设的代理,连接成功,失败,以及断开蓝牙的代理)
#pragma mark - 蓝牙的代理事件
//中心设备状态改变的代理必须实现
- (void)centralManagerDidUpdateState: (CBCentralManager *)central
{
switch (central.state)
{
case CBCentralManagerStatePoweredOn:
// [self showInfo:@"蓝牙已打开"];
break;
case CBCentralManagerStateUnknown:
// [self showInfo:@"蓝牙 状态位置"];
break;
case CBCentralManagerStatePoweredOff:
// [self showInfo:@"蓝牙未打开"];
break;
case CBCentralManagerStateResetting:
// [self showInfo:@"蓝牙初始化中"];
break;
case CBCentralManagerStateUnsupported:
// [self showInfo:@"蓝牙不支持状态"];
break;
case CBCentralManagerStateUnauthorized:
// [self showInfo:@"蓝牙设备未授权"];
break;
default:
break;
}
}

/**
 *  扫描蓝牙的代理
 *
 *  @param central           中心设备
 *  @param peripheral        扫描到的蓝牙
 *  @param advertisementData 在ios中蓝牙广播信息中通常会包含以下4种类型的信息。ios的蓝牙通信协议中不接受其他类型的广播信息。
                         因此需要注意的是,如果需要在扫描设备时,通过蓝牙设备的Mac地址来唯一辨别设备,那么需要与蓝牙设备的硬件工程师沟通好:将所需要的Mac地址放到一下几种类型的广播信息中。
                         通常放到kCBAdvDataManufacturerData这个字段中。
        kCBAdvDataIsConnectable = 1;
        kCBAdvDataLocalName = XXXXXX;
        kCBAdvDataManufacturerData = <XXXXXXXX>;
        kCBAdvDataTxPowerLevel = 0;
 *  @param RSSI              信号强度
 */
//扫描到的蓝牙设备添加到devices数组中,刷新列表
-(void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary<NSString *,id> *)advertisementData
             RSSI:(NSNumber *)RSSI{

if (![self.devices containsObject:peripheral]) {
    [self.devices addObject:peripheral];
    [self.tableView reloadData];
    NSLog(@"发现外围设备:%@---RSSI:%@---advertisementData:%@",peripheral,RSSI,advertisementData);
}
}

在表格的点击代理中去连接外设

-(void)tableView:(UITableView *)tableView       didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.manager connectPeripheral:self.devices[indexPath.row] options:nil];
cell = [tableView cellForRowAtIndexPath:indexPath];
}

蓝牙连接的代理

/**
 *  蓝牙连接成功时候的代理
 *
 *  @param central    中心设备
 *  @param peripheral 当前连接的设备
 */
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"%@名字的蓝牙连接成功",peripheral.name);
cell.detailTextLabel.text = @"已连接";
[self.connectSuccess addObject:peripheral];
}
/**
 *  蓝牙链接失败的代理
 *
 *  @param central    中心设备
 *  @param peripheral 当前连接的外设
 *  @param error      错误信息
 */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;{
   NSLog(@"%@名字的蓝牙连接失败",peripheral.name); 
}

cell左划断开蓝牙连接,这里的cell.detailTextLabel.text在连接成功,和断开成功的时候进行设置,区分。

-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell2 = [tableView cellForRowAtIndexPath:indexPath];
if ([cell2.detailTextLabel.text isEqualToString:@"已连接"]) {
    return YES;
}
return NO;
}

- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
cell = [tableView cellForRowAtIndexPath:indexPath];

UITableViewRowAction *disconnect = [UITableViewRowAction rowActionWithStyle:1 title:@"断开连接" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
    //断开蓝牙连接
    CBPeripheral *peripheral = self.devices[indexPath.row];
    [self.manager cancelPeripheralConnection:peripheral];
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}];
return @[disconnect];
}

蓝牙断开连接的代理

/**
 *  蓝牙断开连接的代理
 *
 *  @param central    中心设备
 *  @param peripheral 当前需要断开连接的外设
 *  @param error      错误信息
 */
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"%@名字的蓝牙断开链接",peripheral.name);
cell.detailTextLabel.text = @"";
for (CBPeripheral *p in self.connectSuccess) {
    if ([p.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) {
        [self.connectSuccess removeObject:p];
    }
}
}

以上就是蓝牙的扫描,连接,断开

最后跳转到LinkViewController页面,并且把连接成功的外设传递过去,这里我就不把外设一一对应了。
#pragma mark - 跳转
- (IBAction)gotoLink:(id)sender {
LinkViewController *link = [[LinkViewController alloc] init];
if (self.connectSuccess.count > 0) {
for (int i = 0; i < self.connectSuccess.count; i++) {
CBPeripheral *p = self.connectSuccess[i];
if (i == 0) {
link.leftperipheral = p;
}
if (i == 1) {
link.rightperipheral = p;
}
}
[self.navigationController pushViewController:link animated:YES];
}
}

这里我们将进行的是蓝牙的收发数据

leftView和rightView分别对应一个蓝牙的代理人。这里我就展示一个。另外一个也是同样的操作

leftView.h文件中。textView只是用来展示接收到的蓝牙数据,mutStr存放接收到蓝牙数据
#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
@interface leftView : UIView<CBPeripheralDelegate>
@property (nonatomic, strong) UITextView *textView;
@property (nonatomic, strong) CBCharacteristic *leftNotiC, *leftWriteC;
@property (nonatomic, strong) NSMutableString *mutStr;
@end
leftView.m文件

#import "leftView.h"
//这里是和硬件工程师沟通好订阅与写入的服务通道
#define leftNotiService @"FFE0"
#define leftWriteService @"FFE5"

@implementation leftView
//重写init方法
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
    self.mutStr = [NSMutableString string];
    [self addTextView];
}
return self;
}
//添加textView
-(void)addTextView{
self.textView = [[UITextView alloc] initWithFrame:self.bounds];
self.textView.backgroundColor = [UIColor lightGrayColor];
[self addSubview:self.textView];
}


#pragma mark - 外围设备服务的代理
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{

for (CBService *s in peripheral.services) {
    NSLog(@"%@",[s.UUID UUIDString]);
    if ([[s.UUID UUIDString] isEqualToString:@"FFE0"]) {
        [peripheral discoverCharacteristics:nil forService:s];
    }
    if ([[s.UUID UUIDString] isEqualToString:@"FFE5"]) {
        [peripheral discoverCharacteristics:nil forService:s];
    }
}
}
#pragma mark 2. 外围设备 扫描到服务下有哪些特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(nonnull CBService *)service error:(nullable NSError *)error
{
if ([[service.UUID UUIDString] isEqualToString:@"FFE0"]) {
    //这里也需要个硬件工程师沟通好。
    self.leftNotiC = service.characteristics[0];
}
if ([[service.UUID UUIDString] isEqualToString:@"FFE5"]) {
    self.leftWriteC = service.characteristics[0];
}
}

#pragma mark 3.接收外围设备的数据(这里就能接到蓝牙发送过来的数据,具体协议就要看你们是怎么定义的)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSData *testData           = characteristic.value;
Byte *testByte             = (Byte *)[testData bytes];
NSMutableArray *midArray   = [NSMutableArray array];
for(int i                  = 0;i<[testData length];i++){
    [midArray addObject:[NSString stringWithFormat:@"%d",testByte[i]]];
    }

for (NSString *str in midArray) {
    [_mutStr appendString:str];
    [_mutStr appendString:@","];
}
self.textView.text = self.mutStr;

}
LinkViewController.m文件中

- (void)viewDidLoad {
[super viewDidLoad];
[self setUI];
//发现左边外设的服务 (运行到这个方法会进入leftView.m文件中的外围设备服务的代理)
[self.leftperipheral discoverServices:nil];
//发现右边外设的服务
[self.rightperipheral discoverServices:nil];
}
-(void)setUI{
self.l = [[leftView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, 200)];
[self.view addSubview:_l];
//设置代理人。这个很重要
self.leftperipheral.delegate = _l;

self.r = [[rightView alloc] initWithFrame:CGRectMake(0, 284, [UIScreen mainScreen].bounds.size.width, 200)];
[self.view addSubview:_r];
//设置代理人。这个很重要
self.rightperipheral.delegate = _r;
}
- (IBAction)writeData:(id)sender {
//打开通道
[self.leftperipheral setNotifyValue:YES forCharacteristic:_l.leftNotiC];
[self.leftperipheral writeValue:[self hexToBytes:@"a55a03010003"] forCharacteristic:_l.leftWriteC type:1];
//打开通道
[self.rightperipheral setNotifyValue:YES forCharacteristic:_r.rightNotiC];
[self.rightperipheral writeValue:[self hexToBytes:@"a55a03010003"] forCharacteristic:_r.rightWriteC type:1];
//这里我们的工程师定义为 给外设发送@"a55a03010003"的字节数组。蓝牙就会给我发送对应的数据。
_l.mutStr = [NSMutableString string];
_r.mutStr = [NSMutableString string];
}

//字符串改为字节数组,需要转换的字符串也是与硬件工程师沟通好的
- (NSData *)hexToBytes:(NSString *)str{
NSMutableData* data = [NSMutableData data];
int idx;
for (idx = 0; idx+2 <= str.length; idx+=2) {
    NSRange range = NSMakeRange(idx, 2);
    NSString* hexStr = [str substringWithRange:range];
    NSScanner* scanner = [NSScanner scannerWithString:hexStr];
    unsigned int intValue;
    [scanner scanHexInt:&intValue];
    [data appendBytes:&intValue length:1];
}
return data;
}

以上就是IOS 链接两个设备的操作。希望可以帮助到刚接触到蓝牙的IOS攻城狮。如果喜欢请不吝点击下喜欢。
这是本人第一次写文章,如有哪里不对请给我宝贵的意见。

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

推荐阅读更多精彩内容

  • 原文:http://www.myexception.cn/operating-system/2052286.htm...
    KYM1988阅读 1,926评论 2 2
  • 备注:下面说到的内容都基于蓝牙4.0标准以上,主要以实践为主。 ~ CoreBluetooth.framework...
    未_漆小七阅读 1,599评论 1 8
  • 蓝牙 蓝牙的波段为2400-2483.5MHz(包括防护频带)。这是全球范围内无需取得执照(但定不是无管制的)的工...
    苏永茂阅读 6,114评论 0 11
  • 本文主要以蓝牙4.0做介绍,因为现在iOS能用的蓝牙也就是只仅仅4.0的设备 用的库就是core bluetoot...
    暮雨飞烟阅读 828评论 0 2
  • 每天打给妻子的电话,基本上是努尔接听。昨晚突发奇想,十点估计她睡着了,再打,妻子顺利接听。 今晨,想到白天给努尔打...
    两个女儿的爸爸阅读 680评论 7 2