開發流程:
1. 建立中心管家
2. 掃描外部設備
3. 獲取掃描的 外部設備,獲取外部設備 , 連接外部設備
4. 連接外設 成功,獲取 發現服務
5. 發現服務 uuid, 發現 服務下 特徵值
6. 讀取 特徵值 ,訂閱 ble->app 通道 獲取app->ble 特徵值
7. 接收 讀取的特徵值
8. 鎖入網,傳遞psw2 給app
9. p1+p2 加密 systemId 鑑權
10. 鎖回覆 psw3
11.發送 開鎖 確認幀
代碼實現:
//
// ViewController.m
// mutipeerConnectivityTest
//
// Created by 鄧安置 on 2020/6/11.
// Copyright © 2020 鄧安置. All rights reserved.
//
#import "ViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import "CBPeripheral+Extension.h"
#import "KDSBleOptions.h"
#import "NSData+JKEncrypt.h"
#import "KDSBleAssistant.h"
#import "NSTimer+KDSBlock.h"
@interface ViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate>
@property(nonatomic,strong) CBCentralManager* mgr;
@property(nonatomic,strong) CBPeripheral* peripheral;
@property(nonatomic,copy,nullable) void(^myBlock)(CBPeripheral* cbP);
@property (nonatomic, strong) CBCharacteristic *batteryCharacteristic; //電量
@property (nonatomic, strong) CBCharacteristic *systemIDCharacteristic; //系統ID
@property (nonatomic, strong) CBCharacteristic *modelNumCharacteristic; //
@property (nonatomic, strong) CBCharacteristic *seriaNumCharacteristic; //
///要寫入到哪個特徵
@property (nonatomic, strong) CBCharacteristic *writeCharacteristic;
@property (nonatomic, strong) CBCharacteristic *functionSetCharacteristic;//功能集
@property (nonatomic, strong, nullable) CBPeripheral *connectedPeripheral;
@property (nonatomic, strong) NSData *systemID;
///連接外設綁定時,收到SN後,從服務器請求回來。綁定後從綁定設備列表獲得。測試沒有上市的藍牙時,服務器沒有值返回,這是使用mac提取。
@property (nonatomic, copy, nullable) NSString *pwd1;
///用戶入網(綁定)的時候生成,鎖藍牙返回的payload數據的1~4字節。
@property (nonatomic, strong, nullable) NSData *pwd2;
///鑑權成功時,鎖藍牙返回的payload數據的1~4字節。
@property (nonatomic, strong, nullable) NSData *pwd3;
/**傳輸序號,每次傳輸+1,到了255置爲50 (爲了區分心跳包 設置範圍爲50-255)*/
@property (nonatomic, assign) int tsn;
///心跳包tsn 1 - 49
@property (nonatomic, assign) NSInteger heartbeatTsn;
@property (nonatomic, weak) NSTimer *heartbeatTimer;
@end
@implementation ViewController
- (IBAction)searchShow:(id)sender {
// 1. 建立中心管家
// nil: 在主線程中掃描
CBCentralManager *centralManager= [[CBCentralManager alloc] initWithDelegate:self queue:nil];
_mgr = centralManager;
NSLog(@"%@",@"開始掃描");
//
}
- (void)viewDidLoad {
[super viewDidLoad];
self.myBlock= ^(CBPeripheral* cbP){
NSLog(@"block回調--->%@",cbP.advDataLocalName);
};
self.pwd1=@"62375ea279e77356aa0a77f9";
}
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
NSLog(@"%ld",central.state);
// 開機狀態 5
if(central.state== CBManagerStatePoweredOn ){
// 2. 掃描外部設備 CBPeripheral表示外部設備
[self.mgr scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
}
__weak typeof(self) weakSelf = self;
dispatch_block_t block = dispatch_block_create(DISPATCH_BLOCK_ASSIGN_CURRENT, ^{
if (weakSelf.mgr.isScanning) {
[weakSelf stopScan];
}
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
block();
});
}
- (void)stopScan
{
NSLog(@"%@",@"停止掃描");
[self.mgr stopScan];
}
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
/**
===peripheral名:Kaadas DoorLock
===廣播名:KDSF0F8F2BFB618
===設備uuid:22F116CE-D746-16A4-8408-BE1476E11B0C
===peripheral:<CBPeripheral: 0x2822b4fa0, identifier = 22F116CE-D746-16A4-8408-BE1476E11B0C, name = Kaadas DoorLock, state = disconnected>
===設備廣播數據:{
kCBAdvDataIsConnectable = 1;
kCBAdvDataLocalName = KDSF0F8F2BFB618;
kCBAdvDataRxPrimaryPHY = 0;
kCBAdvDataRxSecondaryPHY = 0;
kCBAdvDataServiceUUIDs = (
FFD0,
FFF0
);
kCBAdvDataTimestamp = "614075187.3347189";
kCBAdvDataTxPowerLevel = 0;
}
*/
// NSLog(@"\n\n===peripheral名:%@ \n===廣播名:%@\n ===設備uuid:%@\n ===peripheral:%@\n ===設備廣播數據:%@\n\n",peripheral.name,advertisementData[@"kCBAdvDataLocalName"],peripheral.identifier.UUIDString,peripheral,advertisementData);
//解析廣播包中的kCBAdvDataLocalName 區分凱迪仕的設備
NSString *key = CBAdvertisementDataLocalNameKey;
// 3. 獲取掃描的 外部設備,獲取外部設備 , 連接外部設備
if (peripheral==nil || peripheral.name==nil || ![advertisementData[key] containsString:@"KDSA434F1CC143A"]) return;
[self stopScan];
NSLog(@"peripheral==%@",advertisementData[key]);
peripheral.advDataLocalName = advertisementData[key];
self.myBlock(peripheral);
self.peripheral = peripheral;
// 連接外部設備
[self.mgr connectPeripheral:peripheral options:nil];
}
// 4. 連接外設 成功,獲取 發現服務
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
// 通過連接的外部設置保存掃描到的
NSLog(@"連接成功:%@",peripheral.advDataLocalName);
_connectedPeripheral = peripheral;
NSInteger allLenth =peripheral.advDataLocalName.length;
if (allLenth >= 12) {
NSString *orgStr = [peripheral.advDataLocalName substringFromIndex:allLenth-12];
NSData *password1 = [orgStr dataUsingEncoding:NSASCIIStringEncoding];
}
peripheral.delegate =self;
// 掃描服務 根據服務 掃描特徵值
[self.peripheral discoverServices:nil];
}
// 5. 發現服務 uuid, 發現 服務下 特徵值
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
NSArray* services= peripheral.services;
NSLog(@"設備服務值:%@",services);
for (CBService *sevice in peripheral.services) {
/**
設備服務值:(
"<CBService: 0x2803c6800, isPrimary = YES, UUID = Device Information>",
"<CBService: 0x2803c5840, isPrimary = YES, UUID = FFF0>",
"<CBService: 0x2803c66c0, isPrimary = YES, UUID = FFE0>",
"<CBService: 0x2803c6740, isPrimary = YES, UUID = FFE5>",
"<CBService: 0x2803c6640, isPrimary = YES, UUID = FFB0>",
"<CBService: 0x2803c60c0, isPrimary = YES, UUID = F000FFD0-0451-4000-B000-000000000000>"
)
*/
// NSLog(@"%@",sevice.UUID.UUIDString);
if ([sevice.UUID.UUIDString isEqualToString:OADResetServiceUUID]
||[sevice.UUID.UUIDString isEqualToString:DFUResetServiceUUID]) {
//存在FFD0或1802服務,OAD復位服務
//存在FFD0或1802服務,OAD復位服務
peripheral.isNewDevice = YES;
peripheral.bleVersion = 2;
NSLog(@"--{Kaadas}--bleVersion:%d",peripheral.bleVersion);
}
[peripheral discoverCharacteristics:nil forService:sevice];//發現服務下面所有的特徵
}
}
// 6. 讀取 特徵值 ,訂閱 ble->app 通道 獲取app->ble 特徵值,發送 心跳包
// 發現特徵值就會調用
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
if (error){
NSLog(@"獲取服務:%@的特徵失敗: %@", service.UUID, error);
return;
}
UInt64 uuid = strtoul(service.UUID.UUIDString.UTF8String, 0, 16);
KDSBleService serviceType = (KDSBleService)uuid;
NSLog(@"讀的服務-service===%@",service.characteristics);
switch (serviceType)
{
case KDSBleServiceModule:
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"讀的服務-模塊信息 (電量 pwd3):severUUId:%@====charUUId:%@",service.UUID.UUIDString,characteristic.UUID.UUIDString);
// [peripheral setNotifyValue:YES forCharacteristic:characteristic];
if ([characteristic.UUID.UUIDString isEqualToString:batteryDUUID]) {
[peripheral readValueForCharacteristic:characteristic];
//電量特徵值
_batteryCharacteristic = characteristic;
}
}
break;
case KDSBleServiceDevice:
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"讀的服務-BLE設備信息參數:severUUId:%@====charUUId:%@",service.UUID.UUIDString,characteristic.UUID.UUIDString);
//監聽characteristic值變化
// [peripheral setNotifyValue:YES forCharacteristic:characteristic];
if ([characteristic.UUID.UUIDString isEqualToString:systemIDUUID]) {
_systemIDCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
else if ([characteristic.UUID.UUIDString isEqualToString:modelNumUUID]) {
_modelNumCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
else if ([characteristic.UUID.UUIDString isEqualToString:seriaLNumUUID]) {
_seriaNumCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
}
break;
case KDSBleServiceLock:
break;
case KDSBleServiceApp2BleTunnel: // app->藍牙 0xFFE5 ,下只有一個特徵值,app->ble
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"讀的服務-APP-->BLE數據通道severUUId:%@====charUUId:%@",service.UUID.UUIDString,characteristic.UUID.UUIDString);
//主動去讀取一次外圍設備的消息
[peripheral readValueForCharacteristic:characteristic];
// [peripheral setNotifyValue:YES forCharacteristic:characteristic];
self.writeCharacteristic = characteristic;
}
if(self.connectedPeripheral.isNewDevice){
[self createHeartbeatTimer];
}
break;
case KDSBleServiceBle2AppTunnel: //0xFFE0
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"讀的服務-BLE-->APP數據通道:severUUId:%@====charUUId:%@",service.UUID.UUIDString,characteristic.UUID.UUIDString);
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
// ffe0 下功能集 ffe1
if ([characteristic.UUID.UUIDString isEqualToString:kFFE1])
{
peripheral.bleVersion = 3;
NSLog(@"--{Kaadas}--bleVersion:%d",peripheral.bleVersion);
_functionSetCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
}
break;
case KDSApp2BleDisNetworkTunnel:
break;
case KDSBle2AppDisNetworkTunnel:
break;
}
}
// 7. 接收 讀取的特徵值 ,
#pragma mark 接收特徵的數據
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) return;
NSString *uuid = characteristic.UUID.UUIDString;
NSLog(@"讀取所有特徵值的時候藍牙UUID的值:%@",uuid);
if ([uuid isEqualToString:batteryDUUID]) {//電量信息
NSData *batteryData = characteristic.value;
u_int8_t tt;
[batteryData getBytes:&tt length:sizeof(tt)];
int elct = tt;//0-100
peripheral.power = elct;
NSLog(@"電量值是=%d",elct);
}
else if ([uuid isEqualToString:seriaLNumUUID]) {//SN
peripheral.serialNumber = characteristic.value.jk_UTF8String;
NSLog(@"sn是=%@",peripheral.serialNumber);
}
else if ([uuid isEqualToString:systemIDUUID]){//System ID
//獲取systemId
_systemID = characteristic.value;
NSString *systemIDStr = [KDSBleAssistant convertDataToHexStr:_systemID];
if (!systemIDStr || strtol(systemIDStr.UTF8String, NULL, 16) == 0) {
_systemID = [KDSBleAssistant extractSystemIDFromAdvName:peripheral.advDataLocalName];
}
NSLog(@"systemID是=%@",_systemID);
}
else if ([uuid isEqualToString:modelNumUUID]) // modulenum
{
self.connectedPeripheral.lockModelNumber = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
NSLog(@"modulenum是=%@",self.connectedPeripheral.lockModelNumber);
}
else if ([uuid isEqualToString:bleToAppDUUID]){//模塊向App發送數據通道
if (characteristic.value.length == 16) { //沒用,都是20個字節
NSData *pwd1Data = [characteristic.value subdataWithRange:NSMakeRange(0, 12)];
NSData *pwd = [KDSBleAssistant convertHexStrToData:self.pwd1];
NSString* pwdS= [KDSBleAssistant convertDataToHexStr:pwd];
NSLog(@"%@",pwdS);
return;
}
if (characteristic.value.length < 20) {return;/**數據丟包了 */ }
//新設備數據的處理
NSData *data = characteristic.value;
NSLog(@"--{Kaadas}--藍牙--處理模塊發來的數據==%@",data);
const unsigned char* bytes = (const unsigned char*)data.bytes;
u_int8_t control = bytes[0];
u_int8_t check1 = bytes[2];
NSData *decryptData = data;
if (control != 0) {//心跳包不加密
//解密數據
decryptData = [self getAes256_decryptDataWithOriginData:data];
NSLog(@"解密以後的數據:%@",[KDSBleAssistant convertDataToHexStr:decryptData]);
}
u_int8_t check2 = [KDSBleAssistant sumOfDataThroughoutBytes:[decryptData subdataWithRange:NSMakeRange(4, 16)]];
if (check1 == check2) {
// KDSLog(@"校驗正確=%@",decryptData)
NSLog(@"處理校驗正確之後的數據------%@",decryptData);
const unsigned char *bytes = (const unsigned char *)decryptData.bytes;
unsigned char transferSerialNumber = bytes[1]; //tsn
unsigned char cmd = bytes[3];
unsigned char status = bytes[4];
if (cmd==0 && transferSerialNumber<50) return;//心跳包確認幀。
NSString *receipt = @(transferSerialNumber).stringValue;
//如果是app主動發送命令,那麼隊列裏會有記錄,根據tsn和命令值執行相應的命令就行。
//執行app主動發送命令隊列中不存在的上報命令
KDSBleTunnelOrder order = (KDSBleTunnelOrder)cmd;
switch (order) {
case KDSBleTunnelOrderEncrypt:
// 8. 鎖入網,傳遞psw2 給app
if (status == 1)//收到鎖發送過來的入網(綁定)指令,可以在返回數據中提取pwd2
{
self.pwd2 = [decryptData subdataWithRange:NSMakeRange(5, 4)];
// 9. p1+p2 加密 systemId 鑑權
[self sendResponseInOrOutNet:(int)transferSerialNumber];
NSLog(@"~~~~~收到入網命令self.pwd2=%@",self.pwd2);
[self authenticationWithPwd1:self.pwd1 pwd2:self.pwd2];
}
else if (status == 2)//收到pwd3,綁定成功。
{
// 10. 鎖回覆 psw3
self.pwd3 = [decryptData subdataWithRange:NSMakeRange(5, 4)];
NSLog(@"~~~~~收到self.pwd3,綁定成功=%@",self.pwd3);
[self sendResponseInOrOutNet:(int)transferSerialNumber];
}
break;
case KDSBleTunnelOrderLockOperate:
NSLog(@"%@",@"鎖操作上報");
break;
};
}else{
NSLog(@"--{Kaadas}--校驗出錯--check1=%d--check2=%d",check1,check2);
}
}
else if ([uuid isEqualToString:kFFE1])//功能集
{
NSString *FunctionSetKey = [NSString stringWithFormat:@"0x%@",[KDSBleAssistant convertDataToHexStr:characteristic.value]];
NSLog(@"--{Kaadas}--功能集=鎖上=%@",FunctionSetKey);
}
}
#pragma mark - 獲取解密數據
///解密數據,綁定/重置時只能使用密碼1+4字節都爲0的NSData解密(即密碼2必須爲nil);鑑權時只能使用密碼1+密碼2解密(即密碼3必須爲nil);鑑權成功獲取密碼3後只能使用密碼1+密碼3解密;否則解密的數據都不正確。
- (NSData *)getAes256_decryptDataWithOriginData:(NSData *)data{
NSMutableData *keyData = [NSMutableData data];
NSData *key1Data = [KDSBleAssistant convertHexStrToData:_pwd1]; //12個字節
[keyData appendData:key1Data];
if (self.pwd2 == nil) {//解密pwd2 4個字節
Byte behindByte[] = {0x00,0x00,0x00,0x00};
NSData *behindData= [NSData dataWithBytes:behindByte length:sizeof(behindByte)];
[keyData appendData:behindData];
NSLog(@"keyData:%@",keyData);
}else if(self.pwd2 && self.pwd3 == nil){//解密pwd3
[keyData appendData:self.pwd2];
NSLog(@"keyData2:%@",keyData);
}
else if(self.pwd2 && self.pwd3){//建立通道以後 解密數據
[keyData appendData:self.pwd3];
// KDSLog(@"keyData3:%@",keyData);
}
// KDSLog(@"收到的原始數據:%@",data);
NSData *resultData = [[data subdataWithRange:NSMakeRange(4, 16)] aes256_decryptData:keyData];
NSMutableData *decryptData = [NSMutableData data];
[decryptData appendData:[data subdataWithRange:NSMakeRange(0, 4)]];
[decryptData appendData:resultData];
// KDSLog(@"--{Kaadas}--藍牙--收到的解密後數據%@",decryptData);
return decryptData.copy;
}
#pragma mark - 發送收到 入網/退網 確認幀
- (void)sendResponseInOrOutNet:(int)tsn{
if (_connectedPeripheral.isNewDevice) {
[self sendConfirmDataToBleDevice:tsn];
return;
}
}
#pragma mark - 新模塊發送確認幀
- (void)sendConfirmDataToBleDevice:(NSInteger)tsn{
if (tsn != 0) {
// [self pauseTimer];
Byte conformByte[] = {0x00,tsn,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};
NSData *conformData = [[NSData alloc] initWithBytes:conformByte length:sizeof(conformByte)];
if (self.writeCharacteristic && self.connectedPeripheral ) {///設備已經連接且要寫入特徵
// KDSLog(@"發送了確認幀:%@ %@",conformData,getcurretenDate);
[self.connectedPeripheral writeValue:conformData forCharacteristic:self.writeCharacteristic type:0];
}
return;
}
}
- (NSString *)authenticationWithPwd1:(NSString *)pwd1 pwd2:(id)pwd2
{
Byte payload[16] = {0};
for (int i = 0; i < self.systemID.length; ++i)
{
// 把NSData 轉化爲 Byte
payload[i] = ((Byte *)self.systemID.bytes)[i];
}
int sum = [KDSBleAssistant sumOfDataThroughoutBytes:[[NSData alloc] initWithBytes:payload length:16]];
self.tsn++;
unsigned char* bytes = (unsigned char*)malloc(20);
bytes[0] = 1; bytes[1] = self.tsn; bytes[2] = sum; bytes[3] = KDSBleTunnelOrderAuth;
memcpy(bytes + 4, payload, 16);
NSData* sendData= [[NSData alloc] initWithBytesNoCopy:bytes length:20];
NSMutableData *key = [NSMutableData dataWithData:[KDSBleAssistant convertHexStrToData:self.pwd1]]; //12個字節
[key appendData:self.pwd2]; //4個字節
NSData *encryptData = [[sendData subdataWithRange:NSMakeRange(4, 16)] aes256_encryptData:key];
NSLog(@"--{Kaadas}--encryptData=key=%@",key);
NSLog(@"--{Kaadas}--encryptData====%@",encryptData);
[key setData:[sendData subdataWithRange:NSMakeRange(0, 4)]];
[key appendData:encryptData];
[self.connectedPeripheral writeValue:key forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithResponse];
return nil;
}
#pragma mark - 發送心跳包
- (void)createHeartbeatTimer{
if (self.heartbeatTimer==nil) {
//重複每3秒發送一次心跳包
__weak typeof(self) weakSelf = self;
NSTimer *timer = [NSTimer kdsScheduledTimerWithTimeInterval:3 repeats:YES block:^(NSTimer * _Nonnull timer) {
self.heartbeatTsn ++;
Byte bytePayload[16] = {0xFF, 0};
NSData *payloadData = [[NSData alloc] initWithBytes:bytePayload length:sizeof(bytePayload)];
Byte byteHeader[] = {0x00,self.heartbeatTsn,0xff,0xAA};
NSData *headerData = [[NSData alloc] initWithBytes:byteHeader length:sizeof(byteHeader)];
NSMutableData *senderData = [NSMutableData data];
[senderData appendData:headerData];
[senderData appendData:payloadData];
NSLog(@"發送心跳:%@",[KDSBleAssistant convertDataToHexStr:senderData]);
[self.connectedPeripheral writeValue:senderData forCharacteristic:self.writeCharacteristic type:0];
}];
self.heartbeatTimer = timer;
[timer fire];
}
}
// 如果發送開鎖命令,暫停3s發送心跳包
- (void)pauseTimer{
self.heartbeatTimer.fireDate = [NSDate dateWithTimeIntervalSinceNow:3];
}
- (IBAction)openLock:(id)sender {
[self operateLockWithPwd:@"147258" actionType:KDSBleLockControlActionUnlock keyType:KDSBleLockControlKeyPIN
completion:^(KDSBleError error, CBPeripheral * _Nullable peripheral) {
}];
}
// 11.發送 開鎖 確認幀
- (NSString *)operateLockWithPwd:(NSString *)pwd actionType:(KDSBleLockControl)action keyType:(KDSBleLockControl)key completion:(nullable void(^)(KDSBleError error, CBPeripheral * __nullable peripheral))completion{
Byte payload[16] = {(char)action, (char)key, 0, 0};
payload[3] = pwd.length;//密碼長度
//第4個字節開始是密碼,每一個字節保存一位密碼。
for (int i = 0; i< pwd.length; i++)
{
payload[i + 4] = pwd.UTF8String[i];
}
int sum = [KDSBleAssistant sumOfDataThroughoutBytes:[[NSData alloc] initWithBytes:payload length:16]];
self.tsn++;
unsigned char* bytes = (unsigned char*)malloc(20);
bytes[0] = 1; bytes[1] = self.tsn; bytes[2] = sum; bytes[3] = KDSBleTunnelOrderControl;
memcpy(bytes + 4, payload, 16);
NSData* sendData= [[NSData alloc] initWithBytesNoCopy:bytes length:20];
NSMutableData *key13 = [NSMutableData dataWithData:[KDSBleAssistant convertHexStrToData:self.pwd1]];
if (self.pwd3)//鑑權後+密碼3加密
{
[key13 appendData:self.pwd3];
}
NSData *encryptData = [[sendData subdataWithRange:NSMakeRange(4, 16)] aes256_encryptData:key13];
[key13 setData:[sendData subdataWithRange:NSMakeRange(0, 4)]];
[key13 appendData:encryptData];
[self.connectedPeripheral writeValue:key13 forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithResponse];
return nil;
}
@end
源碼下載 : https://download.csdn.net/download/dreams_deng/12576424