这段时间一直在做关于蓝牙的项目,有点心得,拿出来和大家分享下。希望对大家有帮助,废话不多说。直接上代码
首先,我在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攻城狮。如果喜欢请不吝点击下喜欢。
这是本人第一次写文章,如有哪里不对请给我宝贵的意见。