1. 程式人生 > >ios藍牙開發

ios藍牙開發

角色 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藍牙開發