ios蓝牙开发
Posted ios-mt
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ios蓝牙开发相关的知识,希望对你有一定的参考价值。
#import "BuildClientVC.h"
@interface BuildClientVC ()
// 被扫描的蓝牙设备,比如我们上面所说的用我们的手机连接小米手环,这时候小米手环就是外设。
@property (nonatomic,strong) CBPeripheral * peripheral; //链接的外设
@property (nonatomic,strong) CBCharacteristic * writeCharacteristic;//链接外设的写服务特征
@property (nonatomic,strong) CBCharacteristic * notifyCharacteristic;//订阅服务特征
@property (nonatomic,strong) CBCharacteristic * readCharacteristic;//读取服务特征
@property (nonatomic,copy) NSMutableString * baoWenString;//报文
@property (nonatomic,assign) NSInteger baowenLen;//报文长度
@property (weak, nonatomic) IBOutlet UITextField *snTF;
@property (weak, nonatomic) IBOutlet UITextField *recommonTF;
@property (weak, nonatomic) IBOutlet UIButton *buildBtn;
@property (weak, nonatomic) IBOutlet UIButton *freeBtn;
@property (weak, nonatomic) IBOutlet UIButton *connectBtn;
@end
@implementation BuildClientVC
- (NSMutableString *)baoWenString
{
if (_baoWenString == nil) {
_baoWenString = [NSMutableString string];
}
return _baoWenString;
}
- (NSMutableArray *)clients{
if (_clients == nil) {
_clients = [NSMutableArray array];
}
return _clients;
}
#pragma mark -- 懒加载
- (CBCentralManager *)centralMgr
{
if (_centralMgr == nil) {
_centralMgr = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
}
return _centralMgr;
}
#pragma mark -- 生命周期
- (void)viewDidLoad {
[super viewDidLoad];
/*
* 1, 建立中心设备 ---用于扫描周边蓝牙外设的设备,比如我们上面所说的中心者模式,此时我们的手机就是中心设备
*/
[self centralMgr];
[self setUI];
}
#pragma mark -- 私有方法
- (void)setUI{
__weak typeof(self) this = self;
[SVProgressUtil showWait:@"正在获取设备信息" dely:60];
NSDictionary * userDic = [UserDefaultUtil readValueForKey:@"user"];
[SetNetWork getClientWithPhone:userDic[@"phone"] Success:^(NSURLSessionDataTask *task, id responseObject) {
[SVProgressHUD dismiss];
if ([responseObject[@"result"] isEqualToString:@"success"]) {//有终端
this.snTF.text = responseObject[@"pos"][@"pos"];
this.recommonTF.text = [NSString stringWithFormat:@"%@", responseObject[@"pos"][@"agid"]];
this.snTF.enabled = NO;
this.recommonTF.enabled = NO;
this.buildBtn.enabled = NO;
this.freeBtn.enabled = YES;
this.connectBtn.enabled = NO;
[this.buildBtn setBackgroundColor:[UIColor lightGrayColor]];
[this.connectBtn setBackgroundColor:[UIColor lightGrayColor]];
[this.freeBtn setBackgroundColor:LHRGBColor(78, 150, 212)];
}else{//无终端
this.snTF.enabled = NO;
this.recommonTF.enabled = YES;
this.buildBtn.enabled = NO;
this.freeBtn.enabled = NO;
this.connectBtn.enabled = YES;
[this.freeBtn setBackgroundColor:[UIColor lightGrayColor]];
[this.buildBtn setBackgroundColor:[UIColor lightGrayColor]];
[this.connectBtn setBackgroundColor:LHRGBColor(78, 150, 212)];
}
} Fail:^(NSURLSessionDataTask *task, NSError *error) {
[SVProgressHUD dismiss];
[PublicUtil showAlertMSGWithVC:this Title:@"错误" Msg:@"网络错误,请重试" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:^(UIAlertAction *action) {
[this.navigationController popViewControllerAnimated:YES];
}];
LHLog(@"error:%@",error);
}];
}
- (void)showAlert{
__weak typeof(self) this = self;
[PublicUtil showAlertMSGWithVC:this Title:@"提示" Msg:@"请打开蓝牙连接设备,用于绑定终端" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:^(UIAlertAction *action) {
if (this.centralMgr.state == CBManagerStatePoweredOff) {
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"提示" message:@"请打开蓝牙" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
[alertVC addAction:action];
[this presentViewController:alertVC animated:YES completion:^{
}];
}else if (this.centralMgr.state == CBManagerStatePoweredOn){
UIStoryboard * sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
SelectClientTVC * selectClient = [sb instantiateViewControllerWithIdentifier:@"selectClientTVC"];
selectClient.clients = self.clients;
selectClient.blueToolVC = (BlueToolVC *) self;
[selectClient setConnectClient:^(CBPeripheral *peripheral) {
[this.centralMgr connectPeripheral:peripheral options:nil];
}];
[this.navigationController pushViewController:selectClient animated:YES];
}
}];
}
//绑定设备
- (IBAction)buildBtn:(UIButton *)sender {
// __weak typeof(self) this = self;
if ([sender.currentTitle isEqualToString:@"绑定设备"]) {
__weak typeof(self) this = self;
if (self.peripheral != nil) {
[SVProgressUtil showWait:@"正在绑定..." dely:30];
NSDictionary * userDic = [UserDefaultUtil readValueForKey:@"user"];
[SetNetWork buildClientWithPhone:userDic[@"phone"] SN:this.snTF.text AgId:this.recommonTF.text Success:^(NSURLSessionDataTask *task, id responseObject) {
[SVProgressHUD dismiss];
if ([responseObject[@"result"] isEqualToString:@"success"]) {
this.snTF.enabled = NO;
this.recommonTF.enabled = NO;
this.buildBtn.enabled = NO;
this.freeBtn.enabled = YES;
[this.buildBtn setBackgroundColor:[UIColor lightGrayColor]];
[this.freeBtn setBackgroundColor:LHRGBColor(78, 150, 212)];
[UserDefaultUtil removeAttr:@"mainKey"];
[PublicUtil showAlertMSGWithVC:this Title:@"提示" Msg:@"绑定成功" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:^(UIAlertAction *action) {
[this.navigationController popViewControllerAnimated:YES];
}];
}else{
[PublicUtil showAlertMSGWithVC:this Title:@"错误" Msg:responseObject[@"error"] SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:^(UIAlertAction *action) {
[this.navigationController popViewControllerAnimated:YES];
}];
}
} Fail:^(NSURLSessionDataTask *task, NSError *error) {
[SVProgressHUD dismiss];
[PublicUtil showAlertMSGWithVC:this Title:@"错误" Msg:@"网络连接错误" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:^(UIAlertAction *action) {
[this.navigationController popViewControllerAnimated:YES];
}];
}];
}else{
[PublicUtil showAlertMSGWithVC:this Title:@"提示" Msg:@"请先链接终端设备" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:nil];
}
}else{
D180BaoWen * d180 = [[D180BaoWen alloc]init];
[d180 setPATH:@"02"];
[d180 setTYPE:@"01"];
[d180 setSESSIONID:@"144548000001"];
[d180 setDOWNLOAD:@"00"];
[d180 setRSPCODE:@"3030"];
[d180 setCONT:@"ff500730323030303430ff7900"];
[d180 getBaoWenStr];
NSString * string = [d180 getBaoWenStr];;
LHLog(@"app发送:%@",string);
[self writeChar:string];
}
}
- (IBAction)freeBtn:(UIButton *)sender {
__weak typeof(self) this = self;
[SVProgressUtil showWait:@"正在解绑..." dely:30];
NSDictionary * userDic = [UserDefaultUtil readValueForKey:@"user"];
[SetNetWork freeClientWithPhone:userDic[@"phone"] clientSN:self.snTF.text Success:^(NSURLSessionDataTask *task, id responseObject) {
[SVProgressHUD dismiss];
if ([responseObject[@"result"] isEqualToString:@"success"]) {
[SVProgressUtil showSuccessMsg:@"解绑成功" dealy:1];
this.snTF.enabled = YES;
this.recommonTF.enabled = YES;
this.buildBtn.enabled = YES;
this.freeBtn.enabled = NO;
[this.freeBtn setBackgroundColor:[UIColor lightGrayColor]];
[this.buildBtn setBackgroundColor:LHRGBColor(78, 150, 212)];
[UserDefaultUtil removeAttr:@"mainKey"];
[PublicUtil showAlertMSGWithVC:this Title:@"提示" Msg:@"解绑成功" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:^(UIAlertAction *action) {
[this.navigationController popViewControllerAnimated:YES];
}];
}else{
[SVProgressUtil showErrorMsg:responseObject[@"error"] dealy:1];
}
} Fail:^(NSURLSessionDataTask *task, NSError *error) {
[SVProgressHUD dismiss];
[PublicUtil showAlertMSGWithVC:this Title:@"错误" Msg:@"网络连接错误" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:^(UIAlertAction *action) {
[this.navigationController popViewControllerAnimated:YES];
}];
}];
}
//写数据
-(void)writeChar:(NSString *)string
{
switch (self.writeCharacteristic.properties) {
case CBCharacteristicPropertyBroadcast:
LHLog(@"CBCharacteristicPropertyBroadcast");
break;
case CBCharacteristicPropertyRead:
LHLog(@"CBCharacteristicPropertyRead");
break;
case CBCharacteristicPropertyWriteWithoutResponse:
LHLog(@"CBCharacteristicPropertyWriteWithoutResponse");
break;
case CBCharacteristicPropertyWrite:
LHLog(@"CBCharacteristicPropertyWrite");
break;
case CBCharacteristicPropertyNotify:
LHLog(@"CBCharacteristicPropertyNotify");
break;
case CBCharacteristicPropertyIndicate:
LHLog(@"CBCharacteristicPropertyIndicate");
break;
case CBCharacteristicPropertyAuthenticatedSignedWrites:
LHLog(@"CBCharacteristicPropertyAuthenticatedSignedWrites");
break;
case CBCharacteristicPropertyExtendedProperties:
LHLog(@"CBCharacteristicPropertyExtendedProperties");
break;
case CBCharacteristicPropertyNotifyEncryptionRequired:
LHLog(@"CBCharacteristicPropertyNotifyEncryptionRequired");
break;
case CBCharacteristicPropertyIndicateEncryptionRequired:
LHLog(@"CBCharacteristicPropertyIndicateEncryptionRequired");
break;
default:
break;
}
//将字符 140位为一个组 通过蓝牙传输到外设
NSInteger strNum = string.length % 140 == 0 ? string.length / 140 : string.length / 140 +1;
//将大量的数据 转换成部分数据向蓝牙传输
NSMutableArray * strArr = [NSMutableArray new];
for (int i = 0 ; i < strNum ; i ++) {
NSInteger len = (string.length - i* 140) >140 ? 140 : string.length - i* 140;
[strArr addObject:[string substringWithRange:NSMakeRange(i * 140 , len)] ];
}
//向蓝牙传输
for (NSString * str in strArr) {
[self.peripheral writeValue:[str stringToHexData] forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithResponse];
}
}
//解析TLV
- (NSDictionary *)getTLVWithTag:(NSString *)tag
CONT:(NSString *)cont
{
NSMutableDictionary * dict = nil;
NSRange TagRange = [cont rangeOfString:tag];
if (TagRange.location < 900000 && TagRange.length < 900000) {
NSRange LenRange = NSMakeRange(TagRange.location+TagRange.length, 1 *2);
NSString * len = [cont substringWithRange:LenRange];
dict = [NSMutableDictionary new];
dict[@"tag"] = tag;
if ([[len substringWithRange:NSMakeRange(0, 1)] integerValue] < 8) {
dict[@"len"] = len;
}else {
NSString * tempLen = [len substringWithRange:NSMakeRange(1, 1)];
NSRange ValueRange = NSMakeRange(LenRange.location + LenRange.length , [NSString hexToTen:tempLen ] * 2);
len = [cont substringWithRange:ValueRange];
dict[@"len"] = len;
LenRange = ValueRange;
}
NSRange ValueRange = NSMakeRange(LenRange.location + LenRange.length , [NSString hexToTen:len ] * 2);
NSString * value = [cont substringWithRange:ValueRange];
dict[@"value"] = value;
}
return dict;
}
#pragma mark -- CBCentralManagerDelegate
//监听蓝牙的状态变化
//1.1 中心角色一旦创建,蓝牙状态改变的代理方法就会自动执行,返回当前蓝牙状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state) {
case CBManagerStateUnknown:
{
LHLog(@"state:CBManagerStateUnknown");
}
break;
case CBManagerStateResetting:
{
LHLog(@"state:CBManagerStateResetting");
}
break;
case CBManagerStateUnsupported:
{
LHLog(@"state:CBManagerStateUnsupported");
}
break;
case CBManagerStateUnauthorized:
{
LHLog(@"state:CBManagerStateUnauthorized");
}
break;
case CBManagerStatePoweredOff:
{
LHLog(@"state:CBManagerStatePoweredOff");
}
break;
case CBManagerStatePoweredOn:
{
LHLog(@"state:CBManagerStatePoweredOn");
//中心管理者开启后
/*
* 2, 扫描外设
*/
[self.centralMgr scanForPeripheralsWithServices:nil // 通过某些服务筛选外设
options:nil]; // dict,条件
}
break;
default:
{
LHLog(@"state:未找到蓝牙状态");
}
break;
}
}
//
/*
* 3,扫描到外设
*/
/*
central // 中心管理者
peripheral
advertisementData // 外设携带的数据
RSSI // 外设发出的蓝牙信号强度
if ([peripheral.name hasPrefix:@"HT"]) {
NSLog(@"%@",peripheral.name);//能够进来的 都是我们想要的设备了
//我们的逻辑是,搜索到一个设备(peripheral)放到一个集合,然后给用户进行选择
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
LHLog(@"发现一个外设:%@",peripheral);
LHLog(@"UUID:%@", peripheral.identifier.UUIDString);
BOOL haveClient = NO;
for (CBPeripheral * theperipheral in self.clients) {
//是否包含这个uuid
if ([theperipheral.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) {
haveClient = YES;
}
}
if (! haveClient) { //记录外设
[self.clients addObject:peripheral];
}
}
- (IBAction)connectClient:(id)sender {
__weak typeof(self) this = self;
if (self.peripheral != nil) {
[self.centralMgr cancelPeripheralConnection:self.peripheral];
}
UIStoryboard * sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
SelectClientTVC * selectClient = [sb instantiateViewControllerWithIdentifier:@"selectClientTVC"];
selectClient.clients = self.clients;
selectClient.blueToolVC = (BlueToolVC *) self;
[selectClient setConnectClient:^(CBPeripheral *peripheral) {
//4,连接设备
[this.centralMgr connectPeripheral:peripheral options:nil];
}];
[this.navigationController pushViewController:selectClient animated:YES];
}
//4.1 外设链接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
LHLog(@"已经链接的name:%@",peripheral.name);
//连接成功,将外设对象赋值给此对象,
self.peripheral = peripheral;
//设置外设的代理
self.peripheral.delegate = self;
//外设发现服务,传nil代表不过滤
//调用该方法后,将会自动调用didDiscoverServices()代理方法,用于发现服务
//外设链接成功后 搜索服务
[self.peripheral discoverServices:nil];//搜索服务调用方法peripheral:didDiscoverServices:
self.buildBtn.enabled = YES;
[self.buildBtn setBackgroundColor:LHRGBColor(78, 150, 212)];
}
//外设链接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
LHLog(@"外设链接失败:%@",peripheral);
LHLog(@"error:%@",error);
}
#pragma mark -- CBPeripheralDelegate
//当调用[self.peripheral discoverServices:nil];方法之后,
//当发现服务后就会自动调用以下的didDiscoverServices()代理方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (error)
{
LHLog(@"didDiscoverServices : %@", [error localizedDescription]);
return;
}
//服务并不是我们的目标,也没有实际意义。我们需要用的是服务下的特征,查询(每一个服务下的若干)特征
//获取外设服务
for (CBService *service in peripheral.services)
{
LHLog(@"Service found with UUID : %@", service.UUID);
//好,发现了服务 接下来就是最重要的特征了
//调用该方法后,当发现了service服务中的特征后,将会自动触发didDiscoverCharact
[service.peripheral discoverCharacteristics:nil forService:service];
}
}
//检测发送数据的错误
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error != nil) { //如果又错误就打印错误
LHLog(@"%@",[error localizedDescription]);
}
}
//1, 当发现到特征的时候会调用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
{
if (error)
{
LHLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
return;
}
for (CBCharacteristic *c in service.characteristics)
{
switch (c.properties) {
case CBCharacteristicPropertyBroadcast:
LHLog(@"CBCharacteristicPropertyBroadcast");
break;
case CBCharacteristicPropertyRead:
LHLog(@"CBCharacteristicPropertyRead");
self.readCharacteristic = c;
[peripheral readValueForCharacteristic:c];
break;
case CBCharacteristicPropertyWriteWithoutResponse:
LHLog(@"CBCharacteristicPropertyWriteWithoutResponse");
self.writeCharacteristic = c;
break;
case CBCharacteristicPropertyWrite:
LHLog(@"CBCharacteristicPropertyWrite");
self.writeCharacteristic = c;
break;
case CBCharacteristicPropertyNotify:
LHLog(@"CBCharacteristicPropertyNotify");
[peripheral setNotifyValue:YES forCharacteristic:c];
self.notifyCharacteristic = c;
break;
case CBCharacteristicPropertyIndicate:
[peripheral setNotifyValue:YES forCharacteristic:c];
self.notifyCharacteristic = c;
LHLog(@"CBCharacteristicPropertyIndicate");
break;
case CBCharacteristicPropertyAuthenticatedSignedWrites:
self.writeCharacteristic = c;
LHLog(@"CBCharacteristicPropertyAuthenticatedSignedWrites");
break;
case CBCharacteristicPropertyExtendedProperties:
LHLog(@"CBCharacteristicPropertyExtendedProperties");
break;
case CBCharacteristicPropertyNotifyEncryptionRequired:
LHLog(@"CBCharacteristicPropertyNotifyEncryptionRequired");
break;
case CBCharacteristicPropertyIndicateEncryptionRequired:
LHLog(@"CBCharacteristicPropertyIndicateEncryptionRequired");
break;
default:
break;
}
}
}
// 更新特征的value的时候会调用 (凡是从蓝牙传过来的数据都要经过这个回调,简单的说这个方法就是你拿数据的唯一方法) 你可以判断是否
//读取到特征发送的数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
__weak typeof(self) this = self;
if (error) {
LHLog(@"error:%@",[error localizedDescription]);
return;
}
NSData *data = characteristic.value;
NSString * result = [NSString dataToHexString:data];
[self.baoWenString appendString:result];
if ([self.baoWenString length] == 6) {
NSString * lenStr = [self.baoWenString substringWithRange:NSMakeRange(2, 4)];
//获取到除去 stx-1 len-2 etx-1 lrc-1 的字节长度
NSInteger len = [NSString hexToTen:lenStr];
//得到总的报文长度(位数=字节数*2)
self.baowenLen = (len +5)*2;
}
if ([self.baoWenString length] == self.baowenLen) {
LHLog(@"从d180接收到:%@",self.baoWenString);
D180BaoWen * d180BaoWen = [D180BaoWen baoWenWithBaoWenStr:self.baoWenString];
NSDictionary * ff91 = [self getTLVWithTag:@"ff91" CONT:d180BaoWen.CONT];
//01-签到 07-下载公钥or参数 03-消费 06-冲正
//签到 下载公钥 下参数 直接透传报
if ([ff91[@"value"] isEqualToString:@"08"] )//获取终端参数
{
NSDictionary * ff74 =[self getTLVWithTag:@"ff74" CONT:d180BaoWen.CONT];
LHLog(@"ff74:%@",ff74);
NSString * ff74Value = ff74[@"value"];
NSString * tempStr = [NSString hexStringToGBK:ff74Value];
self.snTF.text = tempStr;
[self.buildBtn setTitle:@"绑定设备" forState:UIControlStateNormal];
}else{
[PublicUtil showAlertMSGWithVC:this Title:@"提示" Msg:@"交易类型错误" SureBtnTitle:@"确定" SureBtnStyle:UIAlertActionStyleDestructive Action:nil];
[self.buildBtn setTitle:@"获取终端S/N" forState:UIControlStateNormal];
}
self.baoWenString = nil;
self.baowenLen = 0;
}
}
//当订阅的特征发生改变
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error) {
LHLog(@"Error changing notification state: %@",
[error localizedDescription]);
return;
}
LHLog(@"characteristic.value:%@",characteristic.value);
LHLog(@"characteristic.description:%@",characteristic.description);
//调用回调方法
[peripheral readValueForCharacteristic:characteristic];
}
#pragma mark -- 内存检测
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
LHLog(@" BuildClientVC didReceiveMemoryWarning");
}
- (void)dealloc{
[SVProgressHUD dismiss];
if (self.peripheral != nil) {
[self.centralMgr cancelPeripheralConnection:self.peripheral];
}
LHLog(@" BuildClientVC dealloc");
}
@end
以上是关于ios蓝牙开发的主要内容,如果未能解决你的问题,请参考以下文章