Commit 211744a9b91fa58963fc94d39326ab902d28c10c
1 parent
b867ff0e
feat:ota import
Showing
10 changed files
with
1187 additions
and
0 deletions
HDFwear.xcodeproj/project.pbxproj
@@ -280,6 +280,7 @@ | @@ -280,6 +280,7 @@ | ||
280 | 6CFEEBC02773353700621863 /* BOReferenceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFEEBBF2773353700621863 /* BOReferenceVC.swift */; }; | 280 | 6CFEEBC02773353700621863 /* BOReferenceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFEEBBF2773353700621863 /* BOReferenceVC.swift */; }; |
281 | 6CFEEBC22773354500621863 /* HRReferenceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFEEBC12773354500621863 /* HRReferenceVC.swift */; }; | 281 | 6CFEEBC22773354500621863 /* HRReferenceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFEEBC12773354500621863 /* HRReferenceVC.swift */; }; |
282 | 6CFEEBC42773355700621863 /* SleepReferenceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFEEBC32773355700621863 /* SleepReferenceVC.swift */; }; | 282 | 6CFEEBC42773355700621863 /* SleepReferenceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFEEBC32773355700621863 /* SleepReferenceVC.swift */; }; |
283 | + 84067DB52B5E67DC0030E30E /* OTA in Resources */ = {isa = PBXBuildFile; fileRef = 84067DB42B5E67DC0030E30E /* OTA */; }; | ||
283 | 844959782B48EF900029E2E0 /* BluetoothManager+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844959772B48EF900029E2E0 /* BluetoothManager+Deprecated.swift */; }; | 284 | 844959782B48EF900029E2E0 /* BluetoothManager+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844959772B48EF900029E2E0 /* BluetoothManager+Deprecated.swift */; }; |
284 | 845ADEA12B551C8E00C3AD73 /* NewStepModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845ADEA02B551C8E00C3AD73 /* NewStepModel.swift */; }; | 285 | 845ADEA12B551C8E00C3AD73 /* NewStepModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845ADEA02B551C8E00C3AD73 /* NewStepModel.swift */; }; |
285 | 8473FB622B4BF1A200409148 /* TaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8473FB612B4BF1A200409148 /* TaskManager.swift */; }; | 286 | 8473FB622B4BF1A200409148 /* TaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8473FB612B4BF1A200409148 /* TaskManager.swift */; }; |
@@ -662,6 +663,7 @@ | @@ -662,6 +663,7 @@ | ||
662 | 6CFEEBBF2773353700621863 /* BOReferenceVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BOReferenceVC.swift; sourceTree = "<group>"; }; | 663 | 6CFEEBBF2773353700621863 /* BOReferenceVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BOReferenceVC.swift; sourceTree = "<group>"; }; |
663 | 6CFEEBC12773354500621863 /* HRReferenceVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HRReferenceVC.swift; sourceTree = "<group>"; }; | 664 | 6CFEEBC12773354500621863 /* HRReferenceVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HRReferenceVC.swift; sourceTree = "<group>"; }; |
664 | 6CFEEBC32773355700621863 /* SleepReferenceVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SleepReferenceVC.swift; sourceTree = "<group>"; }; | 665 | 6CFEEBC32773355700621863 /* SleepReferenceVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SleepReferenceVC.swift; sourceTree = "<group>"; }; |
666 | + 84067DB42B5E67DC0030E30E /* OTA */ = {isa = PBXFileReference; lastKnownFileType = folder; path = OTA; sourceTree = "<group>"; }; | ||
665 | 844959772B48EF900029E2E0 /* BluetoothManager+Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BluetoothManager+Deprecated.swift"; sourceTree = "<group>"; }; | 667 | 844959772B48EF900029E2E0 /* BluetoothManager+Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BluetoothManager+Deprecated.swift"; sourceTree = "<group>"; }; |
666 | 845ADE9F2B5507FA00C3AD73 /* 20240126ReadMe.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = 20240126ReadMe.md; sourceTree = "<group>"; }; | 668 | 845ADE9F2B5507FA00C3AD73 /* 20240126ReadMe.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = 20240126ReadMe.md; sourceTree = "<group>"; }; |
667 | 845ADEA02B551C8E00C3AD73 /* NewStepModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStepModel.swift; sourceTree = "<group>"; }; | 669 | 845ADEA02B551C8E00C3AD73 /* NewStepModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStepModel.swift; sourceTree = "<group>"; }; |
@@ -1125,6 +1127,7 @@ | @@ -1125,6 +1127,7 @@ | ||
1125 | 6C6F405E27434B8D00F9473C /* Tools */ = { | 1127 | 6C6F405E27434B8D00F9473C /* Tools */ = { |
1126 | isa = PBXGroup; | 1128 | isa = PBXGroup; |
1127 | children = ( | 1129 | children = ( |
1130 | + 84067DB42B5E67DC0030E30E /* OTA */, | ||
1128 | 6C411EDE275DE737009B2E02 /* BluetoothManager.swift */, | 1131 | 6C411EDE275DE737009B2E02 /* BluetoothManager.swift */, |
1129 | 844959772B48EF900029E2E0 /* BluetoothManager+Deprecated.swift */, | 1132 | 844959772B48EF900029E2E0 /* BluetoothManager+Deprecated.swift */, |
1130 | 6C1F987E279BF0AA00C0C3BA /* BluetoothManager+Set.swift */, | 1133 | 6C1F987E279BF0AA00C0C3BA /* BluetoothManager+Set.swift */, |
@@ -1464,6 +1467,7 @@ | @@ -1464,6 +1467,7 @@ | ||
1464 | 6C34958827A12DB000F792AA /* DailCell.xib in Resources */, | 1467 | 6C34958827A12DB000F792AA /* DailCell.xib in Resources */, |
1465 | 6CE7E3E62782AA8300D6B374 /* Localizable.strings in Resources */, | 1468 | 6CE7E3E62782AA8300D6B374 /* Localizable.strings in Resources */, |
1466 | 6C228BB3279261340078B22E /* ContactCell.xib in Resources */, | 1469 | 6C228BB3279261340078B22E /* ContactCell.xib in Resources */, |
1470 | + 84067DB52B5E67DC0030E30E /* OTA in Resources */, | ||
1467 | 6C9CAC3A278C2A4D00787CA6 /* Motion.storyboard in Resources */, | 1471 | 6C9CAC3A278C2A4D00787CA6 /* Motion.storyboard in Resources */, |
1468 | 6CC6E57727716BA9006C01A7 /* CustomProgress.xib in Resources */, | 1472 | 6CC6E57727716BA9006C01A7 /* CustomProgress.xib in Resources */, |
1469 | 6C1B7E9927B794B300DB9D1C /* NFCCell.xib in Resources */, | 1473 | 6C1B7E9927B794B300DB9D1C /* NFCCell.xib in Resources */, |
HDFwear/HDFwear-Bridging-Header.h
@@ -16,3 +16,4 @@ | @@ -16,3 +16,4 @@ | ||
16 | #import <ShareSDKExtension/ShareSDK+Extension.h> | 16 | #import <ShareSDKExtension/ShareSDK+Extension.h> |
17 | //#import "MASmoothPathTool.h" | 17 | //#import "MASmoothPathTool.h" |
18 | //#import "LxxPlaySound.h" | 18 | //#import "LxxPlaySound.h" |
19 | +#import "Tools/OTA/OTAManager.h" |
HDFwear/Tools/OTA/LETransceiver.h
0 → 100644
1 | +// | ||
2 | +// LETransceiver.h | ||
3 | +// ActsBluetoothSpeed | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/3/15. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import <Foundation/Foundation.h> | ||
10 | +#import <CoreBluetooth/CoreBluetooth.h> | ||
11 | + | ||
12 | +NS_ASSUME_NONNULL_BEGIN | ||
13 | + | ||
14 | +#define KeyWriteServiceUUID @"kWriteServiceUUID" | ||
15 | +#define KeyIndicateServiceUUID @"kIndicateServiceUUID" | ||
16 | +#define KeyWriteCharacteristicUUID @"kWriteCharacteristicUUID" | ||
17 | +#define KeyIndicateCharacteristicUUID @"kIndicateCharacteristicUUID" | ||
18 | + | ||
19 | +@protocol LEConnctionDelegate <NSObject> | ||
20 | + | ||
21 | +@required | ||
22 | +- (void)onFoundPeripheral:(CBPeripheral *) peripheral advertisementData:(NSDictionary *) advertisementData; | ||
23 | + | ||
24 | +@optional | ||
25 | +- (void)onConnectedPeripheral:(CBPeripheral *) peripheral; | ||
26 | +- (void)onDisconnectedPeripheral:(CBPeripheral *) peripheral; | ||
27 | + | ||
28 | +@end | ||
29 | + | ||
30 | +@protocol LEDataDelegate <NSObject> | ||
31 | + | ||
32 | +@optional | ||
33 | + | ||
34 | +- (void)onDataReceive:(NSData *) data; | ||
35 | + | ||
36 | +@end | ||
37 | + | ||
38 | +@interface LETransceiver : NSObject<CBPeripheralDelegate, CBCentralManagerDelegate> | ||
39 | + | ||
40 | +@property (weak, nonatomic) id<LEConnctionDelegate> connectionDelegate; | ||
41 | +@property (weak, nonatomic) id<LEDataDelegate> dataDelegate; | ||
42 | + | ||
43 | +- (void)setUUIDs:(NSDictionary *) uuids; | ||
44 | + | ||
45 | +- (void)scanStart; | ||
46 | +- (void)scanStop; | ||
47 | + | ||
48 | +- (void)connect:(CBPeripheral *)peripheral; | ||
49 | +- (void)disconnect:(CBPeripheral *)peripheral; | ||
50 | + | ||
51 | +- (void)write:(NSData *) data; | ||
52 | +- (void)write:(NSData *) data index:(int) i; | ||
53 | + | ||
54 | +@end | ||
55 | + | ||
56 | +NS_ASSUME_NONNULL_END |
HDFwear/Tools/OTA/LETransceiver.m
0 → 100644
1 | +// | ||
2 | +// LETransceiver.m | ||
3 | +// ActsBluetoothSpeed | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/3/15. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import "LETransceiver.h" | ||
10 | +#import <UIKit/UIKit.h> | ||
11 | + | ||
12 | + | ||
13 | +@implementation LETransceiver { | ||
14 | + NSString *writeServiceUUID; | ||
15 | + NSString *writeCharacteristicUUID; | ||
16 | + NSString *indicateServiceUUID; | ||
17 | + NSString *indicateCharacteristicUUID; | ||
18 | + | ||
19 | + CBCharacteristicWriteType writeType; | ||
20 | + CBCentralManager *centralManager; | ||
21 | + | ||
22 | + CBPeripheral *connectedPeripheral; | ||
23 | + CBCharacteristic *characteristicWrite; | ||
24 | + CBCharacteristic *characteristicIndicate; | ||
25 | + | ||
26 | + NSTimer *connectTimer; | ||
27 | + BOOL isReadyToSendWrite; | ||
28 | + | ||
29 | + | ||
30 | +} | ||
31 | + | ||
32 | +- (id) init { | ||
33 | + self = [super init]; | ||
34 | + | ||
35 | + writeType = CBCharacteristicWriteWithoutResponse; | ||
36 | + | ||
37 | + centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; | ||
38 | + | ||
39 | + return self; | ||
40 | +} | ||
41 | + | ||
42 | +- (void)setUUIDs:(NSDictionary *) uuids { | ||
43 | + NSArray *keys = [uuids allKeys]; | ||
44 | + for (NSString *key in keys) { | ||
45 | + if ([key isEqualToString:KeyWriteServiceUUID]) { | ||
46 | + writeServiceUUID = [[uuids objectForKey:KeyWriteServiceUUID] uppercaseString]; | ||
47 | + } else if ([key isEqualToString:KeyIndicateServiceUUID]) { | ||
48 | + indicateServiceUUID = [[uuids objectForKey:KeyIndicateServiceUUID] uppercaseString]; | ||
49 | + } else if ([key isEqualToString:KeyWriteCharacteristicUUID]) { | ||
50 | + writeCharacteristicUUID = [[uuids objectForKey:KeyWriteCharacteristicUUID] uppercaseString]; | ||
51 | + } else if ([key isEqualToString:KeyIndicateCharacteristicUUID]) { | ||
52 | + indicateCharacteristicUUID = [[uuids objectForKey:KeyIndicateCharacteristicUUID] uppercaseString]; | ||
53 | + } | ||
54 | + } | ||
55 | +} | ||
56 | + | ||
57 | +- (void)scanStart { | ||
58 | + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], CBCentralManagerScanOptionAllowDuplicatesKey, nil]; | ||
59 | + /* | ||
60 | + NSMutableArray *servicesUUID = [[NSMutableArray alloc] init]; | ||
61 | + if (writeServiceUUID) | ||
62 | + [servicesUUID addObject:writeServiceUUID]; | ||
63 | + if (indicateServiceUUID) | ||
64 | + [servicesUUID addObject:indicateServiceUUID]; | ||
65 | + */ | ||
66 | + [centralManager scanForPeripheralsWithServices:nil options:options]; | ||
67 | +} | ||
68 | + | ||
69 | +- (void)scanStop { | ||
70 | + [centralManager stopScan]; | ||
71 | +} | ||
72 | + | ||
73 | +- (void)connect:(CBPeripheral *)peripheral { | ||
74 | + [self scanStop]; | ||
75 | + | ||
76 | + [centralManager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@YES}]; | ||
77 | + | ||
78 | + if (connectTimer) { | ||
79 | + [connectTimer invalidate]; | ||
80 | + } | ||
81 | + connectTimer = [NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:@selector(connectTimeout:) userInfo:peripheral repeats:NO]; | ||
82 | +} | ||
83 | + | ||
84 | +- (void)disconnect:(CBPeripheral *)peripheral { | ||
85 | + if (![self isPeripheralConnected:peripheral]) { | ||
86 | + return; | ||
87 | + } | ||
88 | + | ||
89 | + NSLog(@"disconnect %@", peripheral.name); | ||
90 | + [centralManager cancelPeripheralConnection:peripheral]; | ||
91 | +} | ||
92 | + | ||
93 | +- (BOOL) isPeripheralConnected:(CBPeripheral *) peripheral { | ||
94 | + if (IOS_VERSION >= 7.0) { | ||
95 | + if (peripheral.state == CBPeripheralStateConnected) { | ||
96 | + return YES; | ||
97 | + } | ||
98 | + } else { | ||
99 | +#ifndef __IPHONE_7_0 | ||
100 | + if (peripheral.isConnected) { | ||
101 | + return YES; | ||
102 | + } | ||
103 | +#endif | ||
104 | + } | ||
105 | + return NO; | ||
106 | +} | ||
107 | + | ||
108 | +- (NSUInteger) getMtuForType:(CBCharacteristicWriteType) type { | ||
109 | + return [connectedPeripheral maximumWriteValueLengthForType:type]; | ||
110 | +} | ||
111 | + | ||
112 | +- (void)write:(NSData *) data { | ||
113 | + if (characteristicWrite) { | ||
114 | + NSUInteger mtu = [self getMtuForType:CBCharacteristicWriteWithoutResponse]; | ||
115 | + NSLog(@"writeData mtu: %ld, %@", mtu, data); | ||
116 | + for (NSUInteger index = 0; index < data.length; index += mtu) { | ||
117 | + NSUInteger len = (data.length - index) > mtu ? mtu : (data.length - index); | ||
118 | + NSData *value = [data subdataWithRange:NSMakeRange(index, len)]; | ||
119 | + [connectedPeripheral writeValue:value forCharacteristic:characteristicWrite type:CBCharacteristicWriteWithoutResponse]; | ||
120 | + NSLog(@"writeValue: %@", value); | ||
121 | + } | ||
122 | + } | ||
123 | +} | ||
124 | + | ||
125 | +- (void)write:(NSData *) data index:(int) i { | ||
126 | + if (characteristicWrite) { | ||
127 | + NSUInteger mtu = [self getMtuForType:CBCharacteristicWriteWithoutResponse]; | ||
128 | + NSLog(@"i writeData mtu: %ld, %@", mtu, data); | ||
129 | + | ||
130 | + for (NSUInteger index = 0; index < data.length; index += mtu) { | ||
131 | + NSUInteger len = (data.length - index) > mtu ? mtu : (data.length - index); | ||
132 | + NSData *value = [data subdataWithRange:NSMakeRange(index, len)]; | ||
133 | + if(i != 0 && i % 62 == 0) { | ||
134 | + NSData *value2 = [value subdataWithRange:NSMakeRange(0, 1)]; | ||
135 | + NSData *value3 = [value subdataWithRange:NSMakeRange(1, value.length - 1)]; | ||
136 | + [connectedPeripheral writeValue:value2 forCharacteristic:characteristicWrite type:CBCharacteristicWriteWithResponse]; | ||
137 | + [connectedPeripheral writeValue:value3 forCharacteristic:characteristicWrite type:CBCharacteristicWriteWithoutResponse]; | ||
138 | + } else { | ||
139 | + [connectedPeripheral writeValue:value forCharacteristic:characteristicWrite type:CBCharacteristicWriteWithoutResponse]; | ||
140 | + } | ||
141 | + NSLog(@"writeValue: %@", value); | ||
142 | + } | ||
143 | + } | ||
144 | +} | ||
145 | + | ||
146 | +- (void)connectTimeout:(NSTimer *)timer { | ||
147 | + CBPeripheral *peripheral = (CBPeripheral *)[timer userInfo]; | ||
148 | + [self disconnect:peripheral]; | ||
149 | +} | ||
150 | + | ||
151 | +#pragma mark -- CBCentralManagerDelegate | ||
152 | +- (void)centralManagerDidUpdateState:(CBCentralManager *)central { | ||
153 | + NSString *messtoshow; | ||
154 | + switch (central.state) { | ||
155 | + case CBManagerStatePoweredOn:{ | ||
156 | + messtoshow=[NSString stringWithFormat:@"CBCentralManagerStatePoweredOn"]; | ||
157 | + [self scanStart]; | ||
158 | + break; | ||
159 | + } | ||
160 | + case CBManagerStateUnknown: | ||
161 | + { | ||
162 | + messtoshow=[NSString stringWithFormat:@"State unknown, update imminent"]; | ||
163 | + break; | ||
164 | + } | ||
165 | + case CBManagerStateResetting: | ||
166 | + { | ||
167 | + messtoshow=[NSString stringWithFormat:@"The connection with the system service was momentarily lost, update imminent."]; | ||
168 | + break; | ||
169 | + } | ||
170 | + case CBManagerStateUnsupported: | ||
171 | + { | ||
172 | + messtoshow=[NSString stringWithFormat:@"The platform doesn't support Bluetooth Low Energy"]; | ||
173 | + break; | ||
174 | + } | ||
175 | + case CBManagerStateUnauthorized: | ||
176 | + { | ||
177 | + messtoshow=[NSString stringWithFormat:@"The app is not authorized to use Bluetooth Low Energy"]; | ||
178 | + break; | ||
179 | + } | ||
180 | + case CBManagerStatePoweredOff: | ||
181 | + { | ||
182 | + [self disconnect:connectedPeripheral]; | ||
183 | + | ||
184 | + messtoshow=[NSString stringWithFormat:@"Bluetooth is currently powered off"]; | ||
185 | + break; | ||
186 | + } | ||
187 | + default: | ||
188 | + break; | ||
189 | + } | ||
190 | + NSLog(@"%@",messtoshow); | ||
191 | +} | ||
192 | + | ||
193 | +- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ | ||
194 | +// NSLog(@"didDiscoverPeripheral: %@", peripheral); | ||
195 | + | ||
196 | + if(_connectionDelegate && [_connectionDelegate respondsToSelector:@selector(onFoundPeripheral:advertisementData:)]) { | ||
197 | + [_connectionDelegate onFoundPeripheral:peripheral advertisementData:advertisementData]; | ||
198 | + } | ||
199 | +} | ||
200 | + | ||
201 | +- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ | ||
202 | + NSLog(@"didConnectPeripheral: %@", peripheral.name); | ||
203 | + | ||
204 | + peripheral.delegate = self; | ||
205 | + [peripheral discoverServices:nil]; | ||
206 | +} | ||
207 | + | ||
208 | +- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ | ||
209 | + NSLog(@"Fail to connect: %@", peripheral.name); | ||
210 | +} | ||
211 | + | ||
212 | +- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ | ||
213 | + NSLog(@"didDisconnectPeripheral %@, error: %@",peripheral, error); | ||
214 | + | ||
215 | + connectedPeripheral = nil; | ||
216 | + characteristicWrite = nil; | ||
217 | + characteristicIndicate = nil; | ||
218 | + if(_connectionDelegate && [_connectionDelegate respondsToSelector:@selector(onDisconnectedPeripheral:)]) { | ||
219 | + [_connectionDelegate onDisconnectedPeripheral:peripheral]; | ||
220 | + } | ||
221 | +} | ||
222 | + | ||
223 | +#pragma mark -- CBPeripheralDelegate | ||
224 | +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ | ||
225 | + NSLog(@"didDiscoverServices: %@", peripheral.name); | ||
226 | + if (error) { | ||
227 | + NSLog(@"Error discovering service:%@", [error localizedDescription]); | ||
228 | + return; | ||
229 | + } | ||
230 | + | ||
231 | + for (CBService *service in peripheral.services) { | ||
232 | + | ||
233 | + NSLog(@"service: %@", service); | ||
234 | + [peripheral discoverCharacteristics:nil forService:service]; | ||
235 | + } | ||
236 | + | ||
237 | +} | ||
238 | + | ||
239 | +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{ | ||
240 | + NSLog(@"didDiscoverCharacteristicsForService: %@, %@", peripheral.name, service.UUID.UUIDString); | ||
241 | + if (error) { | ||
242 | + NSLog(@"Error didDiscoverCharacteristicsForService:%@",[error localizedDescription]); | ||
243 | + return; | ||
244 | + } | ||
245 | + | ||
246 | + NSLog(@"UUID: %@, %@, %@", service.UUID.UUIDString, writeServiceUUID, indicateServiceUUID); | ||
247 | + if ([service.UUID.UUIDString isEqualToString:writeServiceUUID]) { | ||
248 | + for (CBCharacteristic *characteristic in service.characteristics) { | ||
249 | + NSLog(@"DiscoverCharacteristics: %@", characteristic.UUID); | ||
250 | + | ||
251 | + if ([characteristic.UUID.UUIDString isEqualToString:writeCharacteristicUUID]) | ||
252 | + characteristicWrite = characteristic; | ||
253 | + } | ||
254 | + } | ||
255 | + | ||
256 | + if ([service.UUID.UUIDString isEqualToString:indicateServiceUUID]) { | ||
257 | + for (CBCharacteristic *characteristic in service.characteristics) { | ||
258 | + NSLog(@"DiscoverCharacteristics: %@", characteristic.UUID); | ||
259 | + | ||
260 | + if ([characteristic.UUID.UUIDString isEqualToString:indicateCharacteristicUUID]) { | ||
261 | + [peripheral setNotifyValue:YES forCharacteristic:characteristic]; | ||
262 | + characteristicIndicate = characteristic; | ||
263 | + } | ||
264 | + } | ||
265 | + } | ||
266 | + | ||
267 | + | ||
268 | + if (characteristicWrite && characteristicIndicate) { | ||
269 | + connectedPeripheral = peripheral; | ||
270 | + [connectTimer invalidate]; | ||
271 | + | ||
272 | + if(_connectionDelegate && [_connectionDelegate respondsToSelector:@selector(onConnectedPeripheral:)]) { | ||
273 | + [_connectionDelegate onConnectedPeripheral:peripheral]; | ||
274 | + } | ||
275 | + } | ||
276 | +} | ||
277 | + | ||
278 | +- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error { | ||
279 | + | ||
280 | +} | ||
281 | + | ||
282 | +- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { | ||
283 | + | ||
284 | + if (error) { | ||
285 | + NSLog(@"%@,%@,Error didWriteValueForCharacteristic:%@",characteristic.UUID,[characteristic value], [error localizedDescription]); | ||
286 | + return; | ||
287 | + } | ||
288 | + | ||
289 | +} | ||
290 | + | ||
291 | +- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { | ||
292 | + if (error) { | ||
293 | + NSLog(@"Error didUpdateValueForCharacteristic:%@", [error localizedDescription]); | ||
294 | + return; | ||
295 | + } | ||
296 | + NSLog(@"didUpdateValueForCharacteristic: %@ %@", characteristic.UUID, characteristic.value); | ||
297 | + | ||
298 | + if (_dataDelegate && [_dataDelegate respondsToSelector:@selector(onDataReceive:)]) { | ||
299 | + [_dataDelegate onDataReceive:[characteristic value]]; | ||
300 | + } | ||
301 | +} | ||
302 | + | ||
303 | +@end |
HDFwear/Tools/OTA/OTAManager.h
0 → 100644
1 | +// | ||
2 | +// OTAManager.h | ||
3 | +// ActsBluetoothOTA | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/5/20. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import <Foundation/Foundation.h> | ||
10 | +#import "RemoteStatus.h" | ||
11 | + | ||
12 | +NS_ASSUME_NONNULL_BEGIN | ||
13 | + | ||
14 | +typedef enum{ | ||
15 | + STATE_UNKNOWN = 0, | ||
16 | + STATE_IDLE = 1, | ||
17 | + STATE_PREPARING = 2, | ||
18 | + STATE_PREPARED = 3, | ||
19 | + STATE_TRANSFERRING = 4, | ||
20 | + STATE_TRANSFERRED = 5, | ||
21 | +} OTAStatus; | ||
22 | + | ||
23 | + | ||
24 | +@protocol OTAManagerDelegate <NSObject> | ||
25 | + | ||
26 | +@optional | ||
27 | + | ||
28 | +- (void)sendData:(NSData *) data; | ||
29 | +- (void)sendData:(NSData *)data index:(int) i; | ||
30 | +- (void)audioDataReceive:(NSData *) data; | ||
31 | +- (void)receiveAudioPSN:(NSInteger) psn data:(NSData *) data; | ||
32 | +- (void)receiveSpeed:(NSInteger) speed; | ||
33 | +- (void)receiveRemoteStatus:(RemoteStatus *) status; | ||
34 | +- (void)onStatus:(OTAStatus) state; | ||
35 | +- (void)onError:(NSInteger) errCode; | ||
36 | + | ||
37 | +@end | ||
38 | + | ||
39 | +@interface OTAManager : NSObject | ||
40 | + | ||
41 | +@property (weak, nonatomic) id<OTAManagerDelegate> delegate; | ||
42 | + | ||
43 | +- (BOOL)setOTAFile:(NSString *) path; | ||
44 | +- (NSString *)getOTAVersion; | ||
45 | +- (void)prepare; | ||
46 | +- (void)upgrade; | ||
47 | +- (void)confirmUpdateAndReboot; | ||
48 | +- (void)setMaxMtu:(NSInteger)maxMtu; | ||
49 | + | ||
50 | +- (BOOL)receiveData:(NSData *) data; | ||
51 | + | ||
52 | + | ||
53 | +@end | ||
54 | + | ||
55 | +NS_ASSUME_NONNULL_END |
HDFwear/Tools/OTA/OTAManager.m
0 → 100644
1 | +// | ||
2 | +// OTAManager.m | ||
3 | +// ActsBluetoothOTA | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/5/20. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import "OTAManager.h" | ||
10 | +#import "Utils.h" | ||
11 | + | ||
12 | +@implementation OTAManager { | ||
13 | + NSMutableData *receiveBuffer; | ||
14 | + | ||
15 | + NSInteger waitTimeout; | ||
16 | + NSInteger restartTimeout; | ||
17 | + NSInteger otaUnit; | ||
18 | + NSInteger interval; | ||
19 | + NSInteger ackEnable; | ||
20 | + | ||
21 | + NSString *fwVersion; | ||
22 | + NSData *vRAM; | ||
23 | + NSInteger otaMode; | ||
24 | + NSInteger batteryThreshold; | ||
25 | + | ||
26 | + OTAStatus curState; | ||
27 | + | ||
28 | + NSData *otaFileData; | ||
29 | + | ||
30 | + NSTimer *timeoutTimer; | ||
31 | + | ||
32 | + BOOL remoteCrcSupport; | ||
33 | + | ||
34 | + NSInteger mWriteBytes; | ||
35 | + | ||
36 | + NSInteger maxMtu; | ||
37 | +} | ||
38 | + | ||
39 | +- (id)init { | ||
40 | + self = [super init]; | ||
41 | + | ||
42 | + fwVersion = @"1.01"; | ||
43 | + otaMode = 0; | ||
44 | + batteryThreshold = 30; | ||
45 | + remoteCrcSupport = NO; | ||
46 | + | ||
47 | + return self; | ||
48 | +} | ||
49 | + | ||
50 | +- (BOOL)setOTAFile:(NSString *) path { | ||
51 | + if (path.length <= 0) | ||
52 | + return NO; | ||
53 | + | ||
54 | + NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path]; | ||
55 | + otaFileData = [handle readDataToEndOfFile]; | ||
56 | + | ||
57 | + return YES; | ||
58 | +} | ||
59 | + | ||
60 | +- (NSString *)getOTAVersion { | ||
61 | + NSData *data = [otaFileData subdataWithRange:NSMakeRange(0, 4)]; | ||
62 | + NSString *magic = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
63 | + if ([magic isEqualToString:@"AOTA"]) { | ||
64 | + data = [otaFileData subdataWithRange:NSMakeRange(64, 32)]; | ||
65 | + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
66 | + } else { | ||
67 | + data = [otaFileData subdataWithRange:NSMakeRange(12, 4)]; | ||
68 | + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
69 | + } | ||
70 | +} | ||
71 | + | ||
72 | +- (void)scheduleTimeoutTimer { | ||
73 | + [self removeTimeoutTimer]; | ||
74 | + | ||
75 | + timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timeoutReport) userInfo:nil repeats:NO]; | ||
76 | +} | ||
77 | + | ||
78 | +- (void)removeTimeoutTimer { | ||
79 | + if (timeoutTimer) { | ||
80 | + [timeoutTimer invalidate]; | ||
81 | + } | ||
82 | +} | ||
83 | + | ||
84 | +- (void)timeoutReport { | ||
85 | + [self notifyStatus:STATE_UNKNOWN]; | ||
86 | +} | ||
87 | + | ||
88 | +- (void)readOTAHeader { | ||
89 | + NSData *data = [otaFileData subdataWithRange:NSMakeRange(0, 4)]; | ||
90 | + NSString *magic = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
91 | + if ([magic isEqualToString:@"AOTA"]) { | ||
92 | + data = [otaFileData subdataWithRange:NSMakeRange(64, 32)]; | ||
93 | + fwVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
94 | + Byte placeholder[16] = {0x00}; | ||
95 | + vRAM = [NSData dataWithBytes:placeholder length:16]; | ||
96 | + } else { | ||
97 | + data = [otaFileData subdataWithRange:NSMakeRange(12, 4)]; | ||
98 | + fwVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
99 | + vRAM = [otaFileData subdataWithRange:NSMakeRange(56, 16)]; | ||
100 | + } | ||
101 | +} | ||
102 | + | ||
103 | +- (void)checkRemoteStatus { | ||
104 | + NSMutableArray *tlvs = [[NSMutableArray alloc] init]; | ||
105 | + | ||
106 | + // package version | ||
107 | + NSData *tlv = [self assembleType:0x01 length:fwVersion.length value:[fwVersion dataUsingEncoding:NSUTF8StringEncoding]]; | ||
108 | + [tlvs addObject:tlv]; | ||
109 | + | ||
110 | + // component size | ||
111 | + Byte valueSize[2] = {0x00, 0x00}; | ||
112 | + tlv = [self assembleType:0x02 length:2 value:[NSData dataWithBytes:valueSize length:2]]; | ||
113 | + [tlvs addObject:tlv]; | ||
114 | + | ||
115 | + // VRAM | ||
116 | + tlv = [self assembleType:0x03 length:vRAM.length value:vRAM]; | ||
117 | + [tlvs addObject:tlv]; | ||
118 | + | ||
119 | + // ota work mode | ||
120 | + Byte valueMode[1] = {(Byte)otaMode}; | ||
121 | + tlv = [self assembleType:0x04 length:1 value:[NSData dataWithBytes:valueMode length:1]]; | ||
122 | + [tlvs addObject:tlv]; | ||
123 | + | ||
124 | + // feature support: Support CRC32 Checksum | ||
125 | + Byte valueFeature[1] = {(Byte)0x1}; | ||
126 | + tlv = [self assembleType:0x09 length:1 value:[NSData dataWithBytes:valueFeature length:1]]; | ||
127 | + [tlvs addObject:tlv]; | ||
128 | + | ||
129 | + NSData *command = [self assembleCommand:0x01 withTLVs:tlvs]; | ||
130 | + [self sendData:command]; | ||
131 | +} | ||
132 | + | ||
133 | +- (void)prepare{ | ||
134 | + [self notifyStatus:STATE_PREPARING]; | ||
135 | + [self readOTAHeader]; | ||
136 | + [self checkRemoteStatus]; | ||
137 | + [self scheduleTimeoutTimer]; | ||
138 | +} | ||
139 | + | ||
140 | +- (void)setMaxMtu:(NSInteger)maxMtu; { | ||
141 | + self -> maxMtu = maxMtu; | ||
142 | +} | ||
143 | + | ||
144 | +- (void)upgrade { | ||
145 | +/* [self notifyStatus:STATE_PREPARING]; | ||
146 | + | ||
147 | + [self readOTAHeader]; | ||
148 | + [self checkRemoteStatus]; | ||
149 | + [self scheduleTimeoutTimer]; | ||
150 | + */ | ||
151 | + | ||
152 | + [self requestRemoteParameters]; | ||
153 | + [self scheduleTimeoutTimer]; | ||
154 | + mWriteBytes = 0; | ||
155 | +} | ||
156 | + | ||
157 | +- (void)confirmUpdateAndReboot { | ||
158 | + NSLog(@"confirmUpdateAndReboot"); | ||
159 | + NSData *buffer = [self assembleCommand:0x06 withTLVs:nil]; | ||
160 | + [self sendData:buffer]; | ||
161 | +} | ||
162 | + | ||
163 | +/* TLV结构组装 */ | ||
164 | +- (NSData *)assembleType:(NSInteger) type length:(NSInteger) length value:(NSData *) value { | ||
165 | + NSInteger len = 3; | ||
166 | + Byte *buffer = (Byte *)malloc(len); | ||
167 | + | ||
168 | + NSInteger index = 0; | ||
169 | + | ||
170 | + // Type | ||
171 | + buffer[index++] = (Byte) (type & 0xFF); | ||
172 | + | ||
173 | + // Length | ||
174 | + buffer[index++] = (Byte) (length & 0xFF); | ||
175 | + buffer[index++] = (Byte) ((length >> 8) & 0xFF); | ||
176 | + | ||
177 | + // Value | ||
178 | + NSMutableData *data = [NSMutableData dataWithBytes:buffer length:len]; | ||
179 | + [data appendData:value]; | ||
180 | + | ||
181 | + free(buffer); | ||
182 | + | ||
183 | + return data; | ||
184 | +} | ||
185 | + | ||
186 | +/* 命令组装 */ | ||
187 | +- (NSData *)assembleCommand:(NSInteger) cmdId withTLVs:(NSArray *) subTLVs { | ||
188 | + NSInteger subTLVsLen = 0; | ||
189 | + if (subTLVs && subTLVs.count > 0) { | ||
190 | + for (NSData *tlv in subTLVs) | ||
191 | + subTLVsLen += tlv.length; | ||
192 | + } | ||
193 | + | ||
194 | + // Header | ||
195 | + Byte *buffer = (Byte *)malloc(2); | ||
196 | + // Server ID | ||
197 | + buffer[0] = (Byte) 0x09; | ||
198 | + // Command ID | ||
199 | + buffer[1] = (Byte) (cmdId & 0xFF); | ||
200 | + NSData *header = [NSData dataWithBytes:buffer length:2]; | ||
201 | + free(buffer); | ||
202 | + | ||
203 | + // Super TLV | ||
204 | + NSData *superTLV = [self assembleType:0x80 length:subTLVsLen value:nil]; | ||
205 | + | ||
206 | + NSMutableData *cmdData = [NSMutableData dataWithData:header]; | ||
207 | + [cmdData appendData:superTLV]; | ||
208 | + for (NSData *tlv in subTLVs) { | ||
209 | + [cmdData appendData:tlv]; | ||
210 | + } | ||
211 | + | ||
212 | + return cmdData; | ||
213 | +} | ||
214 | + | ||
215 | +- (void)notifyStatus:(OTAStatus) state { | ||
216 | + if (curState == state) | ||
217 | + return; | ||
218 | + | ||
219 | + curState = state; | ||
220 | + if(_delegate && [_delegate respondsToSelector:@selector(onStatus:)]) { | ||
221 | + [_delegate onStatus:curState]; | ||
222 | + } | ||
223 | +} | ||
224 | + | ||
225 | +- (NSArray *)readOTADataFrom:(NSInteger) offset withLength:(NSInteger) length byBitmap:(NSData *) bitmap groupNum:(int) groupNum { | ||
226 | + NSLog(@"groupNum:%d", groupNum); | ||
227 | + NSArray *pkgIdx = [Utils getZeroBitIndexMap:bitmap groupNum:groupNum]; | ||
228 | + NSInteger pkgNum = pkgIdx.count; | ||
229 | + NSLog(@"pkgNum:%d", pkgNum); | ||
230 | + | ||
231 | + NSMutableArray *packages = [[NSMutableArray alloc] init]; | ||
232 | + for (NSInteger i = 0; i < pkgNum; i++) { | ||
233 | + NSInteger index = [pkgIdx[i] integerValue]; | ||
234 | + NSInteger odd = length - index * otaUnit; | ||
235 | + if (odd <= 0) | ||
236 | + break; | ||
237 | + | ||
238 | + NSInteger o = offset + index * otaUnit; | ||
239 | + NSInteger len = odd > otaUnit ? otaUnit : odd; | ||
240 | + | ||
241 | + NSData *pkg = [otaFileData subdataWithRange:NSMakeRange(o, len)]; | ||
242 | + NSLog(@"i: %ld o: %ld pkgIdx: %ld pkgNum: %ld count: %ld", i, o, index, pkgNum, pkg.length); | ||
243 | + if (pkg.length <= 0) | ||
244 | + break; | ||
245 | + | ||
246 | + [packages addObject:pkg]; | ||
247 | + } | ||
248 | + | ||
249 | + return packages; | ||
250 | +} | ||
251 | + | ||
252 | +- (void)sendData:(NSData *) data { | ||
253 | + if(_delegate && [_delegate respondsToSelector:@selector(sendData:)]) { | ||
254 | + [_delegate sendData:data]; | ||
255 | + } | ||
256 | +} | ||
257 | + | ||
258 | +- (void)sendData:(NSData *) data index:(int) i{ | ||
259 | + if(_delegate && [_delegate respondsToSelector:@selector(sendData:index:)]) { | ||
260 | + [_delegate sendData:data index:i]; | ||
261 | + } | ||
262 | +} | ||
263 | + | ||
264 | +- (void)requestRemoteParameters { | ||
265 | + NSData *data = [self assembleCommand:0x02 withTLVs:nil]; | ||
266 | + [self sendData:data]; | ||
267 | +} | ||
268 | + | ||
269 | +- (void)notifyRemoteAppReady:(NSInteger) state { | ||
270 | + // ota status | ||
271 | + Byte bytes[1] = {(Byte)(state & 0xFF)}; | ||
272 | + NSData *value = [NSData dataWithBytes:bytes length:1]; | ||
273 | + NSData *tlv = [self assembleType:0x01 length:0x01 value:value]; | ||
274 | + | ||
275 | + NSArray *tlvs = [NSArray arrayWithObject:tlv]; | ||
276 | + | ||
277 | + NSData *data = [self assembleCommand:0x09 withTLVs:tlvs]; | ||
278 | + [self sendData:data]; | ||
279 | +} | ||
280 | + | ||
281 | +/* 去除开始位置无效数据 */ | ||
282 | +- (NSData *)abandonInvalidHeaderData:(NSData *) data { | ||
283 | + Byte *bytes = (Byte *)[data bytes]; | ||
284 | + NSInteger length = data.length; | ||
285 | + | ||
286 | + NSInteger index = 0; | ||
287 | + for (index = 0; index < length; index++) { | ||
288 | + if (bytes[index] == 0x09) | ||
289 | + break; | ||
290 | + } | ||
291 | + | ||
292 | + return [data subdataWithRange:NSMakeRange(index, length - index)]; | ||
293 | +} | ||
294 | + | ||
295 | +- (BOOL)receiveData:(NSData *) data { | ||
296 | + if (!receiveBuffer) { | ||
297 | + receiveBuffer = [[NSMutableData alloc] init]; | ||
298 | + } | ||
299 | + | ||
300 | + [receiveBuffer appendData:data]; | ||
301 | + | ||
302 | + data = [self abandonInvalidHeaderData:receiveBuffer]; | ||
303 | + receiveBuffer = [NSMutableData dataWithData:data]; | ||
304 | + | ||
305 | + /* 数据长度不足,等待下一次数据包 */ | ||
306 | + if (data.length <= 5) { | ||
307 | + return NO; | ||
308 | + } | ||
309 | + | ||
310 | + Byte *bytes = (Byte *)[data bytes]; | ||
311 | + NSInteger length = data.length; | ||
312 | + NSInteger index = 0; | ||
313 | + | ||
314 | + Byte serviceId = bytes[index++]; | ||
315 | + Byte commandId = bytes[index++]; | ||
316 | + | ||
317 | + Byte superTLV[3] = {0}; | ||
318 | + superTLV[0] = bytes[index++]; | ||
319 | + superTLV[1] = bytes[index++]; | ||
320 | + superTLV[2] = bytes[index++]; | ||
321 | + NSInteger subTLVsLen = superTLV[1] + (superTLV[2] << 8); | ||
322 | + | ||
323 | + /* 数据长度不足,等待下一次数据包 */ | ||
324 | + if (length - index < subTLVsLen) { | ||
325 | + return NO; | ||
326 | + } | ||
327 | + | ||
328 | + NSData *tmp = [data subdataWithRange:NSMakeRange(index + subTLVsLen, length - index - subTLVsLen)]; | ||
329 | + receiveBuffer = [NSMutableData dataWithData:tmp]; | ||
330 | + | ||
331 | + NSData *tlvs = [data subdataWithRange:NSMakeRange(index, subTLVsLen)]; | ||
332 | + bytes = (Byte *)[tlvs bytes]; | ||
333 | + length = tlvs.length; | ||
334 | + index = 0; | ||
335 | + | ||
336 | + switch (commandId) { | ||
337 | + case 0x01: { | ||
338 | + if (bytes[index] != 0x7F) { | ||
339 | + return NO; | ||
340 | + } | ||
341 | + | ||
342 | + index += 3; | ||
343 | + // index++; | ||
344 | + // int length = bytes[index]; | ||
345 | + // index++; | ||
346 | + // length += (((int)bytes[index]) << 8); | ||
347 | + // index++; | ||
348 | + NSLog(@"length: %ld", length); | ||
349 | + NSInteger errCode = [Utils bytes2UInt32:bytes index:index]; | ||
350 | + index += 4; | ||
351 | + | ||
352 | + NSLog(@"error code: %ld", errCode); | ||
353 | + [self removeTimeoutTimer]; | ||
354 | + | ||
355 | + NSData *data; | ||
356 | + RemoteStatus *status = [[RemoteStatus alloc] init]; | ||
357 | + | ||
358 | + for ( ; index < length; ) { | ||
359 | + int type = bytes[index++]; | ||
360 | + int len = bytes[index++]; | ||
361 | + len += (((int)bytes[index]) << 8); | ||
362 | + index++; | ||
363 | + NSLog(@"index: %ld, type: %d, len: %d", index, type, len); | ||
364 | + switch (type) { | ||
365 | + case 0x04: // battery threshold | ||
366 | + status.batteryThreshold = bytes[index++]; | ||
367 | + break; | ||
368 | + case 0x05: // version name | ||
369 | + data = [NSData dataWithBytes:&bytes[index] length:len]; | ||
370 | + index += len; | ||
371 | + status.versionName = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
372 | + break; | ||
373 | + case 0x06: // board name | ||
374 | + data = [NSData dataWithBytes:&bytes[index] length:len]; | ||
375 | + index += len; | ||
376 | + status.boardName = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
377 | + break; | ||
378 | + case 0x07: // hardware rev | ||
379 | + data = [NSData dataWithBytes:&bytes[index] length:len]; | ||
380 | + index += len; | ||
381 | + status.hardwareRev = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||
382 | + break; | ||
383 | + case 0x08: // version code | ||
384 | + status.versionCode = [Utils bytes2UInt32:bytes index:index]; | ||
385 | + index += 4; | ||
386 | + break; | ||
387 | + case 0x09: // feature support | ||
388 | + status.featureSupport = bytes[index++]; | ||
389 | + remoteCrcSupport = status.featureSupport & 0x01; | ||
390 | + break; | ||
391 | + default: | ||
392 | + index += len; | ||
393 | + break; | ||
394 | + } | ||
395 | + } | ||
396 | + | ||
397 | + if (errCode == 100000) { | ||
398 | + // [self requestRemoteParameters]; | ||
399 | + // [self scheduleTimeoutTimer]; | ||
400 | + if(_delegate && [_delegate respondsToSelector:@selector(receiveRemoteStatus:)]) { | ||
401 | + [_delegate receiveRemoteStatus:status]; | ||
402 | + } | ||
403 | + | ||
404 | + [self notifyStatus:STATE_PREPARED]; | ||
405 | + } else { | ||
406 | + curState = STATE_UNKNOWN; | ||
407 | + if(_delegate && [_delegate respondsToSelector:@selector(onError:)]) { | ||
408 | + [_delegate onError:errCode]; | ||
409 | + } | ||
410 | + } | ||
411 | + break; | ||
412 | + } | ||
413 | + case 0x02:{ | ||
414 | + // app wait timeout | ||
415 | + index += 3; | ||
416 | + waitTimeout = [Utils bytes2Short:bytes index:index]; | ||
417 | + index += 2; | ||
418 | + | ||
419 | + // device restart timeout | ||
420 | + index += 3; | ||
421 | + restartTimeout = [Utils bytes2Short:bytes index:index]; | ||
422 | + index += 2; | ||
423 | + | ||
424 | + // ota unit size | ||
425 | + index += 3; | ||
426 | + otaUnit = [Utils bytes2Short:bytes index:index]; | ||
427 | + NSLog(@"otaUnit:%ld", otaUnit); | ||
428 | + if (maxMtu != 0) { | ||
429 | + if ((maxMtu - 10) >= otaUnit) { | ||
430 | + | ||
431 | + } else { | ||
432 | + otaUnit = maxMtu - 10; //根据最大mtu定制化设置单包最大数据量 | ||
433 | + } | ||
434 | + } else { | ||
435 | + otaUnit = 172; | ||
436 | + } | ||
437 | + | ||
438 | + index += 2; | ||
439 | + | ||
440 | + // interval | ||
441 | + index += 3; | ||
442 | + interval = [Utils bytes2Short:bytes index:index]; | ||
443 | + index += 2; | ||
444 | + | ||
445 | + // ack enable | ||
446 | + index += 3; | ||
447 | + ackEnable = bytes[index] & 0xFF; | ||
448 | + index++; | ||
449 | + | ||
450 | + [self removeTimeoutTimer]; | ||
451 | + [self notifyRemoteAppReady:1]; | ||
452 | + | ||
453 | + // [self notifyStatus:STATE_PREPARED]; | ||
454 | + break; | ||
455 | + } | ||
456 | + case 0x03:{ | ||
457 | + [self notifyStatus:STATE_TRANSFERRING]; | ||
458 | + | ||
459 | + if (ackEnable == 1) { | ||
460 | + NSArray *array = [NSArray arrayWithObject:tlvs]; | ||
461 | + NSData *buf = [self assembleCommand:0x03 withTLVs:array]; | ||
462 | + [self sendData:buf]; | ||
463 | + } | ||
464 | + | ||
465 | + // file offset | ||
466 | + int index = 3; | ||
467 | + int offset = [Utils bytes2UInt32:bytes index:index]; | ||
468 | + index += 4; | ||
469 | + | ||
470 | + // file length | ||
471 | + index += 3; | ||
472 | + int length = [Utils bytes2UInt32:bytes index:index]; | ||
473 | + index += 4; | ||
474 | + | ||
475 | + // file apply bitmap | ||
476 | + if (tlvs.length > 17) { | ||
477 | + index ++; | ||
478 | + int len = [Utils bytes2Short:bytes index:index]; | ||
479 | + index += 2; | ||
480 | + if (tlvs.length >= 17 + len) { | ||
481 | + Byte *bitmap = (Byte *)malloc(len); | ||
482 | + for (int i = 0; i < len; i++) { | ||
483 | + bitmap[i] = bytes[index++]; | ||
484 | + NSLog(@"bitmap[%d]: %x", i, bitmap[i]); | ||
485 | + } | ||
486 | + | ||
487 | + NSData *bitmapData = [NSData dataWithBytes:bitmap length:len]; | ||
488 | + free(bitmap); | ||
489 | + | ||
490 | + // 计算组数 | ||
491 | + int groupNum = length / otaUnit + 1; | ||
492 | + | ||
493 | + NSArray *frames = [self readOTADataFrom:offset withLength:length byBitmap:bitmapData groupNum:groupNum]; | ||
494 | +// for (NSInteger i = 0; i < frames.count; i++) { | ||
495 | + for (int i = 0; i < frames.count; i++) { | ||
496 | + Byte b[1] = {(Byte)(i % 256)}; | ||
497 | + NSMutableData *pkg = [NSMutableData dataWithBytes:b length:1]; | ||
498 | + | ||
499 | + NSInteger command = 0x04; | ||
500 | + if (remoteCrcSupport) { | ||
501 | + command = 0x0B; | ||
502 | + int32_t checksum = [Utils crc32:frames[i]]; | ||
503 | + [pkg appendData: [NSData dataWithBytes: &checksum length: 4]]; | ||
504 | + } | ||
505 | + | ||
506 | + [pkg appendData:frames[i]]; | ||
507 | + NSArray *array = [NSArray arrayWithObject:pkg]; | ||
508 | + NSData *data = [self assembleCommand:command withTLVs:array]; | ||
509 | + | ||
510 | + [self sendData:data index:i]; | ||
511 | +// NSLog(@"nick_data:%@", data); | ||
512 | + mWriteBytes += data.length; | ||
513 | + [_delegate receiveSpeed:mWriteBytes]; | ||
514 | + } | ||
515 | + } | ||
516 | + } | ||
517 | + break; | ||
518 | + } | ||
519 | + case 0x05:{ | ||
520 | + // package valid size | ||
521 | + index += 3; | ||
522 | + int pkgValidSize = [Utils bytes2UInt32:bytes index:index]; | ||
523 | + index += 4; | ||
524 | + | ||
525 | + // received file size | ||
526 | + index += 3; | ||
527 | + int receivedSize = [Utils bytes2UInt32:bytes index:index]; | ||
528 | + index += 4; | ||
529 | + | ||
530 | + NSLog(@"receive 0x0905 pkgValidSize: %d, receivedSize: %d", pkgValidSize, receivedSize); | ||
531 | + break; | ||
532 | + } | ||
533 | + case 0x06:{ | ||
534 | + index += 3; | ||
535 | + int valid = bytes[index] & 0xFF; | ||
536 | + index++; | ||
537 | + | ||
538 | + // NSData *buffer = [self assembleCommand:0x06 withTLVs:nil]; | ||
539 | + // [self sendData:buffer]; | ||
540 | + NSLog(@"receive 0x0906 valid: %d", valid); | ||
541 | + | ||
542 | + if (_delegate && [_delegate respondsToSelector:@selector(onStatus:)]) { | ||
543 | + if (valid == 1) { | ||
544 | + [_delegate onStatus:STATE_TRANSFERRED]; | ||
545 | + } else { | ||
546 | + [_delegate onStatus:STATE_UNKNOWN]; | ||
547 | + } | ||
548 | + } | ||
549 | + | ||
550 | + break; | ||
551 | + } | ||
552 | + case 0x07:{ | ||
553 | + if (bytes[index++] == 0x7F) { | ||
554 | + index += 2; | ||
555 | + int errCode = [Utils bytes2UInt32:bytes index:index]; | ||
556 | + index += 4; | ||
557 | + if (errCode != 100000 | ||
558 | + && _delegate | ||
559 | + && [_delegate respondsToSelector:@selector(onError:)]) { | ||
560 | + NSLog(@"0x0907 Error: %d", errCode); | ||
561 | + | ||
562 | + [_delegate onError:errCode]; | ||
563 | + } | ||
564 | + } | ||
565 | + | ||
566 | + break; | ||
567 | + } | ||
568 | + case 0x7D:{ | ||
569 | + int type = bytes[index] & 0xFF; | ||
570 | + index++; | ||
571 | + int len = [Utils bytes2Short:bytes index:index]; | ||
572 | + index += 2; | ||
573 | + int psn = bytes[index] & 0xFF; | ||
574 | + index++; | ||
575 | + NSLog(@"0x097D, : %d, len: %d", psn, len); | ||
576 | + | ||
577 | + type = bytes[index] & 0xFF; | ||
578 | + index++; | ||
579 | + len = [Utils bytes2Short:bytes index:index]; | ||
580 | + index += 2; | ||
581 | + | ||
582 | + NSData *buffer = [tlvs subdataWithRange:NSMakeRange(index, len)]; | ||
583 | + NSLog(@"Receive 0x097D, data size: %d, %@", len, buffer); | ||
584 | + | ||
585 | + if (_delegate && [_delegate respondsToSelector:@selector(receiveAudioPSN:data:)]) { | ||
586 | + [_delegate receiveAudioPSN:psn data:buffer]; | ||
587 | + } | ||
588 | + | ||
589 | + break; | ||
590 | + } | ||
591 | + default: | ||
592 | + break; | ||
593 | + } | ||
594 | + | ||
595 | + // 如果还有剩余数据,则继续处理 | ||
596 | + if (receiveBuffer.length > 0) { | ||
597 | + [self receiveData:nil]; | ||
598 | + } | ||
599 | + | ||
600 | + return YES; | ||
601 | +} | ||
602 | + | ||
603 | +@end |
HDFwear/Tools/OTA/RemoteStatus.h
0 → 100644
1 | +// | ||
2 | +// RemoteStatus.h | ||
3 | +// ActsBluetoothOTA | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/9/12. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import <Foundation/Foundation.h> | ||
10 | + | ||
11 | +NS_ASSUME_NONNULL_BEGIN | ||
12 | + | ||
13 | +@interface RemoteStatus : NSObject | ||
14 | + | ||
15 | +@property (nonatomic, strong, nullable) NSString *versionName; | ||
16 | +@property (nonatomic, strong, nullable) NSString *boardName; | ||
17 | +@property (nonatomic, strong, nullable) NSString *hardwareRev; | ||
18 | +@property (nonatomic, readwrite) NSInteger batteryThreshold; | ||
19 | +@property (nonatomic, readwrite) NSInteger versionCode; | ||
20 | +@property (nonatomic, readwrite) NSInteger featureSupport; | ||
21 | + | ||
22 | +@end | ||
23 | + | ||
24 | +NS_ASSUME_NONNULL_END |
HDFwear/Tools/OTA/RemoteStatus.m
0 → 100644
1 | +// | ||
2 | +// RemoteStatus.m | ||
3 | +// ActsBluetoothOTA | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/9/12. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import "RemoteStatus.h" | ||
10 | + | ||
11 | +@implementation RemoteStatus | ||
12 | + | ||
13 | +- (id)init | ||
14 | +{ | ||
15 | + if (self = [super init]) { | ||
16 | + | ||
17 | + self.batteryThreshold = 30; | ||
18 | + self.versionName = nil; | ||
19 | + self.boardName = nil; | ||
20 | + self.hardwareRev = nil; | ||
21 | + self.versionCode = 0; | ||
22 | + self.featureSupport = 0x00; | ||
23 | + } | ||
24 | + | ||
25 | + return self; | ||
26 | +} | ||
27 | + | ||
28 | +@end |
HDFwear/Tools/OTA/Utils.h
0 → 100644
1 | +// | ||
2 | +// Utils.h | ||
3 | +// ActsBluetoothOTA | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/5/22. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import <Foundation/Foundation.h> | ||
10 | + | ||
11 | +NS_ASSUME_NONNULL_BEGIN | ||
12 | + | ||
13 | +@interface Utils : NSObject | ||
14 | + | ||
15 | ++ (UInt32)bytes2UInt32:(Byte*) bytes index:(NSInteger) index; | ||
16 | ++ (UInt16)bytes2Short:(Byte*) bytes index:(NSInteger) index; | ||
17 | + | ||
18 | ++ (NSArray *)getZeroBitIndexMap:(NSData *) bitmap groupNum:(int) group; | ||
19 | + | ||
20 | ++ (int32_t)crc32:(NSData *)data; | ||
21 | + | ||
22 | +@end | ||
23 | + | ||
24 | +NS_ASSUME_NONNULL_END |
HDFwear/Tools/OTA/Utils.m
0 → 100644
1 | +// | ||
2 | +// Utils.m | ||
3 | +// ActsBluetoothOTA | ||
4 | +// | ||
5 | +// Created by inidhu on 2019/5/22. | ||
6 | +// Copyright © 2019 Actions. All rights reserved. | ||
7 | +// | ||
8 | + | ||
9 | +#import "Utils.h" | ||
10 | + | ||
11 | +@implementation Utils | ||
12 | + | ||
13 | + | ||
14 | ++ (UInt32)bytes2UInt32:(Byte*)bytes index:(NSInteger)index { | ||
15 | + return (((bytes[index + 3] & 0xFF) << 24) + ((bytes[index + 2] & 0xFF) << 16)) + ((bytes[index + 1] & 0xFF) << 8) + (bytes[index] & 0xFF); | ||
16 | +} | ||
17 | + | ||
18 | ++ (UInt16)bytes2Short:(Byte*)bytes index:(NSInteger)index { | ||
19 | + return ((bytes[index + 1] & 0xFF) << 8) + (bytes[index] & 0xFF); | ||
20 | +} | ||
21 | + | ||
22 | +/* 计算0比特位的个数 */ | ||
23 | ++ (NSInteger)countZeroBit:(NSData *) bitmap { | ||
24 | + NSInteger count = 0; | ||
25 | + | ||
26 | + Byte *bytes = (Byte *)[bitmap bytes]; | ||
27 | + NSInteger length = bitmap.length; | ||
28 | + | ||
29 | + for (int i = 0; i < length; i++ ) { | ||
30 | + Byte x = bytes[i]; | ||
31 | + while ((x + 1) != 0) { | ||
32 | + x |= (x + 1); | ||
33 | + count++; | ||
34 | + } | ||
35 | + } | ||
36 | + | ||
37 | + return count; | ||
38 | +} | ||
39 | + | ||
40 | +/* 从bitmap获取相应0bit位的索引 */ | ||
41 | ++ (NSArray *)getZeroBitIndexMap:(NSData *) bitmap groupNum:(int) groupNum{ | ||
42 | + NSMutableArray *indexMap = [[NSMutableArray alloc] init]; | ||
43 | + | ||
44 | + NSInteger count = 0; | ||
45 | + NSInteger index = 0; | ||
46 | + Byte *bytes = (Byte *)[bitmap bytes]; | ||
47 | + for (NSInteger i = 0; i < bitmap.length; i++) { | ||
48 | + Byte b = bytes[i]; | ||
49 | + for (NSInteger j = 0; j < groupNum; j++) { | ||
50 | + int offset = (index % groupNum); | ||
51 | + | ||
52 | + if ((b & (0x1 << offset)) == 0x0) { | ||
53 | + [indexMap addObject:[NSNumber numberWithInteger:index]]; | ||
54 | + count++; | ||
55 | + } | ||
56 | + | ||
57 | + index++; | ||
58 | + } | ||
59 | + } | ||
60 | + | ||
61 | + return indexMap; | ||
62 | +} | ||
63 | + | ||
64 | ++ (int32_t)crc32:(NSData *)data { | ||
65 | + uint32_t *table = malloc(sizeof(uint32_t) * 256); | ||
66 | + uint32_t crc = 0xffffffff; | ||
67 | + uint8_t *bytes = (uint8_t *)[data bytes]; | ||
68 | + | ||
69 | + for (uint32_t i=0; i<256; i++) { | ||
70 | + table[i] = i; | ||
71 | + for (int j=0; j<8; j++) { | ||
72 | + if (table[i] & 1) { | ||
73 | + table[i] = (table[i] >>= 1) ^ 0xedb88320; | ||
74 | + } else { | ||
75 | + table[i] >>= 1; | ||
76 | + } | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + for (int i=0; i<data.length; i++) { | ||
81 | + crc = (crc >> 8) ^ table[crc & 0xff ^ bytes[i]]; | ||
82 | + } | ||
83 | + crc ^= 0xffffffff; | ||
84 | + | ||
85 | + free(table); | ||
86 | + return crc; | ||
87 | +} | ||
88 | + | ||
89 | +@end |