ios藍牙開發
阿新 • • 發佈:2018-07-14
角色 nsrange crypt ive nonatomic nonnull handler cbc werror
#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藍牙開發