步骤:
- 在info.plist中加入蓝牙的权限 NSBluetoothAlwaysUsageDescription :
- 创建蓝牙管理者对象,创建后,首先会执行系统蓝牙是否打开的协议方法centralManagerDidUpdateState,如果系统蓝牙未打开,会有系统的弹框提示打开蓝牙,如下:
- 打开系统蓝牙后,开始扫描设备,扫描到设备后会执行didDiscoverPeripheral这个发现外设的方法;在这里判断是自己需要连接的设备后,停止设备扫描并进行外设的连接,如下:
- 外设连接成功后,会执行didConnectPeripheral方法,在该方法中设置外设的代理并设置发现服务的方法:
- 发现服务之后会执行didDiscoverServices方法,这里可能会有多个服务,针对自己要进行读写的服务进行判断,当是自己要进行操作的服务后,执行发现特征的方法:
- 发现特征后会执行didUpdateValueForCharacteristic方法,有特征的结果如下:
- 当该特征是可读的特征时,即可进行读操作,成功之后会走didUpdateValueForCharcteristic方法,如下:
- 当该特征是可写的时候,写入成功会执行didWriteValueForCharcteristic方法,如下:
- 可以主动断开设备的连接,如下:
源码如下:
- EasyWriteViewController.h 这里用到了:pod 'QMUIKit'
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface EasyWriteViewController : QMUICommonViewController
@end
NS_ASSUME_NONNULL_END
- EasyWriteViewController.m
#import "EasyWriteViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>
#define kPeripheralName @"Hi-PMA58-12HOL22"//硬件设备蓝牙名称
#define kServiceUUID @"0000FFE0-3C17-D293-8E48-14FE2E4DA212" //服务的UUID
#define kWriteCharacteristicUUID @"FFE1" //特征的UUID
#define kNotifyCharacteristicUUID @"FFE2" //特征的UUID
#define kReadCharacteristicUUID @"FFE3" //特征的UUID
// 输出语句
#ifdef DEBUG
#define DCLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
#else
#define DCLog(...) do { } while (0)
#endif
@interface EasyWriteViewController ()<CBCentralManagerDelegate, CBPeripheralDelegate>
@property (nonatomic, strong) CBCentralManager *manager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBCharacteristic *character;
@property (nonatomic, strong) CBCharacteristic *characterRead;
@property (nonatomic, strong) CBCharacteristic *characterNofity;
@end
@implementation EasyWriteViewController
- (void)setupNavigationItems {
[super setupNavigationItems];
self.title = @"蓝牙数据读写";
self.navigationItem.rightBarButtonItem = [UIBarButtonItem qmui_itemWithTitle:@"断开" target:self action:@selector(disConnectClick)];
}
- (void)viewDidLoad {
[super viewDidLoad];
// 创建蓝牙管理者对象 第二个参数:nil默认为主线程
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
QMUIButton *blueWriteBtn = [DCUIHelper generateDarkFilledButton];
blueWriteBtn.frame = CGRectMake(20, DC_STATUS_NAV_HEIGHT + 10, SCREEN_WIDTH - 40, 40);
[blueWriteBtn setTitle:@"蓝牙写数据" forState:UIControlStateNormal];
blueWriteBtn.tag = 1;
[blueWriteBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:blueWriteBtn];
QMUIButton *blueReadBtn = [DCUIHelper generateDarkFilledButton];
blueReadBtn.frame = CGRectMake(20, DC_STATUS_NAV_HEIGHT + 60, SCREEN_WIDTH - 40, 40);
[blueReadBtn setTitle:@"蓝牙读数据" forState:UIControlStateNormal];
blueReadBtn.tag = 2;
[blueReadBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:blueReadBtn];
QMUIButton *blueNotifyBtn = [DCUIHelper generateDarkFilledButton];
blueNotifyBtn.frame = CGRectMake(20, DC_STATUS_NAV_HEIGHT + 110, SCREEN_WIDTH - 40, 40);
[blueNotifyBtn setTitle:@"蓝牙通知数据" forState:UIControlStateNormal];
blueNotifyBtn.tag = 3;
[blueNotifyBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:blueNotifyBtn];
}
- (void)btnClick:(UIButton *)btn {
switch (btn.tag) {
case 1: {
[_peripheral readRSSI];
[self writeDataToBle:@"ling123"];
}
break;
case 2: {
if (_characterRead.properties & CBCharacteristicPropertyRead) {
[_peripheral readValueForCharacteristic:_characterRead];
} else {
DCLog(@"该字段不可读");
}
}
break;
case 3: {
if (_characterNofity.properties & CBCharacteristicPropertyNotify) {
[_peripheral setNotifyValue:YES forCharacteristic:_characterNofity];
} else {
DCLog(@"该字段不可通知");
}
}
break;
}
}
- (void)disConnectClick {
[_manager cancelPeripheralConnection:self.peripheral]; // 主动断开连接
}
#pragma mark - 定时器时间事件
- (void)timered:(NSTimer *)timer {
// 读信号强度
[_peripheral readRSSI];
// 实时读数
// [self writeDataToBle:@"*APP.VALUE#$"];
[self writeDataToBle:@"ling123"];
}
// 向蓝牙写入数
- (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 {
DCLog(@"该字段不可写!");
}
}
- (void)scanBlueTooth {
// [self.manager scanForPeripheralsWithServices:nil options:nil]; // 扫描所有蓝牙设备
[self.manager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}]; // 搜索附近设备
// [self.manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"D3A97ECF-C939-C85C-9923-BA2E9684E6B6"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}]; // 扫描具体蓝牙设备
}
#pragma mark - CBCentralManagerDelegate
// 该方法当蓝牙状态改变(打开或者关闭)的时候就会调用
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBManagerStateUnknown: {
DCLog(@"未知蓝牙状态");
} break;
case CBManagerStateResetting: {
DCLog(@"系统服务连接暂时丢失");
} break;
case CBManagerStateUnsupported: {
DCLog(@"该设备不支持蓝牙");
} break;
case CBManagerStateUnauthorized: {
DCLog(@"该设备蓝牙为被授权");
} break;
case CBManagerStatePoweredOn: {
DCLog(@"该设备蓝牙已打开");
[QMUITips showSucceed:@"该设备蓝牙已打开"];
// 打开后,开始扫描
[self scanBlueTooth];
} break;
case CBManagerStatePoweredOff: {
DCLog(@"该设备蓝牙没有打开");
} break;
}
}
/**
发现外设后调用的方法 查到外设后,连接设备,停止扫描
param:peripheral 外设(即蓝牙设备),连接,写入数据的时候需要用到。
param:advertisementData 蓝牙传输过来的数据,包括:设备名称,自定义数据等。 自定义数据有长度限制,自己想要什么数据要和嵌入式商量好,例如MAC地址,传感器数据等
param:RSSI 侧面反映距离的远近
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
// 可在该方法内部区分扫描到的蓝牙设备
DCLog(@"发现外设peripheral:%@ =====RSSI:%@ =====UUID:%@", peripheral, RSSI, peripheral.identifier);
// 判断是否为你要连接的设备(我这里用设备名称判断的,扫描到我要的外设蓝牙名称后,停止扫描、连接外设)
if ([peripheral.name isEqualToString:kPeripheralName]) {
// 扫描到设备之后停止扫描
[_manager stopScan];
// 开始连接外设
[_manager connectPeripheral:peripheral options:nil];
_peripheral = peripheral;
}
}
// 连接失败会被调用
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
DCLog(@"=====>连接失败");
}
// 断开连接会被调用
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
DCLog(@"=====>断开连接");
// 断开连接后,可能是因为信号不好,所以我们的需求是继续连接我们的设备
[QMUITips showSucceed:@"设备已断开"];
// [self scanBlueTooth];
}
// 连接成功会被调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
DCLog(@"=====>%@连接成功 =====>UUID: %@", peripheral.name, peripheral.identifier);
[QMUITips showSucceed:@"设备连接成功"];
// 连接设备之后设置蓝牙对象的代理,扫描服务
[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];
}
#pragma mark - CBPeripheralDelegate
// 返回的蓝牙服务通知通过代理实现(已经发现服务)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
DCLog(@"=====>发现服务");
// 一个设备可能为多个服务,所以要取你需要读写的那个服务(我们的外设就一个服务)
for (CBService *service in peripheral.services) {
if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
// 根据你要的那个服务去发现特性
[self.peripheral discoverCharacteristics:nil forService:service];
}
}
}
// 读取每个特征值 获得外围设备的服务、获得服务的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
// 首先读到外设特征服务,写到宏定义
DCLog(@"发现特征服务:%@", service.UUID);
for (CBCharacteristic *c in service.characteristics) {
// 其次读到两个特征值,写到宏定义
DCLog(@"特征UUID: %@", c.UUID);
// 哪个特征值是读的,哪个特征值是写的,一般硬件兄弟都会告诉你的
// (即使没有告诉你,也可以自己在AppStore下载LightBlue或UsrBleAssistent蓝牙调试工具看到你需要读写的特征值)
if ([c.UUID.UUIDString isEqualToString:kWriteCharacteristicUUID]) {
// 找到可写特征值D2
_character = c;
} else if ([c.UUID.UUIDString isEqualToString:kNotifyCharacteristicUUID]) {
// 设置通知
_characterNofity = c;
// [_peripheral setNotifyValue:YES forCharacteristic:c];
// [QMUITips showSucceed:@"设置通知"];
} else if ([c.UUID.UUIDString isEqualToString:kReadCharacteristicUUID]) {
_characterRead = c;
// [_peripheral readValueForCharacteristic:c];
// [QMUITips showSucceed:@"读取数据"];
}
}
}
// 读取指定特征的特征值 获取外设发来的数据,不论是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];
DCLog(@"原始数据: %@ ===> 字符串: %@ ===>uuid: %@", characteristic.value, str, characteristic.UUID);
// }
} else {
DCLog(@"error:%@", error);
}
}
// 写入数据成功后回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
DCLog(@"characteristic===>%@ value===>%@",characteristic, characteristic.value);
}
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error {
DCLog(@"peripheral==%@ RSSI==%@ error==%@",peripheral, RSSI, error);
}
@end
标签:DCLog,蓝牙,读写,扫描,self,void,error,peripheral
From: https://www.cnblogs.com/styCy/p/17359038.html