Browse Source

First basic working implementation

Thomas Buck 7 years ago
parent
commit
581b5e1e19
No account linked to committer's email address

+ 45
- 0
BlueTerm.xcodeproj/xcuserdata/thomas.xcuserdatad/xcschemes/BlueTerm.xcscheme View File

@@ -5,6 +5,22 @@
5 5
    <BuildAction
6 6
       parallelizeBuildables = "YES"
7 7
       buildImplicitDependencies = "YES">
8
+      <BuildActionEntries>
9
+         <BuildActionEntry
10
+            buildForTesting = "YES"
11
+            buildForRunning = "YES"
12
+            buildForProfiling = "YES"
13
+            buildForArchiving = "YES"
14
+            buildForAnalyzing = "YES">
15
+            <BuildableReference
16
+               BuildableIdentifier = "primary"
17
+               BlueprintIdentifier = "E9CDD7411EDCC9F4002FF7B8"
18
+               BuildableName = "BlueTerm"
19
+               BlueprintName = "BlueTerm"
20
+               ReferencedContainer = "container:BlueTerm.xcodeproj">
21
+            </BuildableReference>
22
+         </BuildActionEntry>
23
+      </BuildActionEntries>
8 24
    </BuildAction>
9 25
    <TestAction
10 26
       buildConfiguration = "Debug"
@@ -13,6 +29,15 @@
13 29
       shouldUseLaunchSchemeArgsEnv = "YES">
14 30
       <Testables>
15 31
       </Testables>
32
+      <MacroExpansion>
33
+         <BuildableReference
34
+            BuildableIdentifier = "primary"
35
+            BlueprintIdentifier = "E9CDD7411EDCC9F4002FF7B8"
36
+            BuildableName = "BlueTerm"
37
+            BlueprintName = "BlueTerm"
38
+            ReferencedContainer = "container:BlueTerm.xcodeproj">
39
+         </BuildableReference>
40
+      </MacroExpansion>
16 41
       <AdditionalOptions>
17 42
       </AdditionalOptions>
18 43
    </TestAction>
@@ -26,6 +51,16 @@
26 51
       debugDocumentVersioning = "YES"
27 52
       debugServiceExtension = "internal"
28 53
       allowLocationSimulation = "YES">
54
+      <BuildableProductRunnable
55
+         runnableDebuggingMode = "0">
56
+         <BuildableReference
57
+            BuildableIdentifier = "primary"
58
+            BlueprintIdentifier = "E9CDD7411EDCC9F4002FF7B8"
59
+            BuildableName = "BlueTerm"
60
+            BlueprintName = "BlueTerm"
61
+            ReferencedContainer = "container:BlueTerm.xcodeproj">
62
+         </BuildableReference>
63
+      </BuildableProductRunnable>
29 64
       <AdditionalOptions>
30 65
       </AdditionalOptions>
31 66
    </LaunchAction>
@@ -35,6 +70,16 @@
35 70
       savedToolIdentifier = ""
36 71
       useCustomWorkingDirectory = "NO"
37 72
       debugDocumentVersioning = "YES">
73
+      <BuildableProductRunnable
74
+         runnableDebuggingMode = "0">
75
+         <BuildableReference
76
+            BuildableIdentifier = "primary"
77
+            BlueprintIdentifier = "E9CDD7411EDCC9F4002FF7B8"
78
+            BuildableName = "BlueTerm"
79
+            BlueprintName = "BlueTerm"
80
+            ReferencedContainer = "container:BlueTerm.xcodeproj">
81
+         </BuildableReference>
82
+      </BuildableProductRunnable>
38 83
    </ProfileAction>
39 84
    <AnalyzeAction
40 85
       buildConfiguration = "Debug">

+ 197
- 2
BlueTerm/main.m View File

@@ -4,14 +4,209 @@
4 4
 //
5 5
 //  Created by Thomas Buck on 29.05.17.
6 6
 //  Copyright © 2017 Thomas Buck. All rights reserved.
7
+//  xythobuz@xythobuz.de
7 8
 //
9
+//  Heavily based on:
10
+//  https://gist.github.com/brayden-morris-303/09a738ed9c83a7d14c82
11
+//
12
+//  This utility connects to a common Bluetooth 4.0 BLE UART bridge
13
+//  and logs all received data to the local filesystem.
14
+//  This has been developed for use as a Betaflight blackbox replacement.
8 15
 
9 16
 #import <Foundation/Foundation.h>
17
+#import <CoreBluetooth/CoreBluetooth.h>
18
+
19
+// ---------------------------------------------------------------------------------------
20
+
21
+// BLE UART device name that is searched for
22
+static NSString * const kDeviceName = @"xyCopter";
23
+
24
+// BLE Service & Characteristic UUIDs for the UART data stream
25
+static NSString * const kUARTServiceUUID = @"ffe0";
26
+static NSString * const kUARTCharacteristicUUID = @"ffe1";
27
+
28
+// Default log file path, if no command line argument is given
29
+static NSString * const kDefaultFilePath = @"~/BlueTerm.log";
30
+
31
+// ---------------------------------------------------------------------------------------
32
+
33
+@interface BlueTerm : NSObject <CBCentralManagerDelegate, CBPeripheralDelegate>
34
+
35
+@property (strong, nonatomic) CBCentralManager *centralManager;
36
+@property (strong, nonatomic) CBPeripheral *discoveredPeripheral;
37
+@property (assign) BOOL shouldRun;
38
+@property (strong, nonatomic) NSFileHandle *fileHandle;
39
+
40
+@end
41
+
42
+@implementation BlueTerm
43
+
44
+- (id)init {
45
+    if (self = [super init]) {
46
+        self.shouldRun = false;
47
+        self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
48
+        return self;
49
+    }
50
+    return nil;
51
+}
52
+
53
+- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
54
+    if (!self.shouldRun) return;
55
+    
56
+    // You should test all scenarios
57
+    if (central.state != CBCentralManagerStatePoweredOn) {
58
+        NSLog(@"Unknown BLE state: %ld", (long)central.state);
59
+        return;
60
+    }
61
+    
62
+    if (central.state == CBCentralManagerStatePoweredOn) {
63
+        // Scan for devices
64
+        [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kUARTServiceUUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
65
+        NSLog(@"Scanning started...");
66
+    }
67
+}
68
+
69
+- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
70
+    
71
+    NSLog(@"Discovered %@ at %@", peripheral.name, RSSI);
72
+    
73
+    if (([peripheral.name isEqualToString:kDeviceName]) && (self.discoveredPeripheral != peripheral)) {
74
+        // Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
75
+        self.discoveredPeripheral = peripheral;
76
+        
77
+        // And connect
78
+        NSLog(@"Connecting to peripheral %@", peripheral);
79
+        [self.centralManager connectPeripheral:peripheral options:nil];
80
+    }
81
+}
82
+
83
+- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
84
+    NSLog(@"Connected");
85
+    
86
+    [_centralManager stopScan];
87
+    NSLog(@"Scanning stopped");
88
+    
89
+    peripheral.delegate = self;
90
+    
91
+    [peripheral discoverServices:@[[CBUUID UUIDWithString:kUARTServiceUUID]]];
92
+}
93
+
94
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
95
+    if (error) {
96
+        NSLog(@"Error: %@", error);
97
+        return;
98
+    }
99
+    
100
+    for (CBService *service in peripheral.services) {
101
+        NSLog(@"Discovered service: %@", service.UUID);
102
+        
103
+        if ([service.UUID isEqual:[CBUUID UUIDWithString:kUARTServiceUUID]]) {
104
+            NSLog(@"This is our service. Discovering characteristics...");
105
+            
106
+            [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kUARTCharacteristicUUID]] forService:service];
107
+        }
108
+    }
109
+}
110
+
111
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
112
+    if (error) {
113
+        return;
114
+    }
115
+    
116
+    for (CBCharacteristic *characteristic in service.characteristics) {
117
+        NSLog(@"Discovered characteristic: %@", characteristic.UUID);
118
+        
119
+        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kUARTCharacteristicUUID]]) {
120
+            NSLog(@"This is our characteristic! Subscribing...");
121
+            
122
+            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
123
+        }
124
+    }
125
+}
126
+
127
+- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
128
+    if (error) {
129
+        return;
130
+    }
131
+    
132
+    static time_t lastTime = 0;
133
+    time_t now = time(NULL);
134
+    if ((now - lastTime) > 0) {
135
+        NSString *valueString = [[[[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding] stringByReplacingOccurrencesOfString:@"\n" withString:@""] stringByReplacingOccurrencesOfString:@"\r" withString:@""];
136
+        NSLog(@"Received more data (excerpt: \"%@\")", valueString);
137
+        lastTime = now;
138
+    }
139
+    
140
+    [self.fileHandle writeData:characteristic.value];
141
+}
142
+
143
+- (BOOL)initializeTargetFile:(int)argc withParams:(const char **)argv {
144
+    NSString *filePath;
145
+    if (argc == 1) {
146
+        // Use hard-coded default path
147
+        filePath = [kDefaultFilePath stringByExpandingTildeInPath];
148
+    } else if (argc == 2) {
149
+        // Use given path
150
+        filePath = [[NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding] stringByExpandingTildeInPath];
151
+    } else {
152
+        NSLog(@"Usage:\n\t%@ [/path/to/log]", [NSString stringWithCString:argv[0] encoding:NSUTF8StringEncoding]);
153
+        return false;
154
+    }
155
+    
156
+    [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
157
+    self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
158
+    if (self.fileHandle) {
159
+        NSLog(@"Writing received data to: \"%@\"!", filePath);
160
+        NSLog(@"File has been overwritten (if it existed before)!");
161
+        [self.fileHandle seekToEndOfFile];
162
+    } else {
163
+        NSLog(@"Error opening logfile: \"%@\"!", filePath);
164
+        return false;
165
+    }
166
+    
167
+    self.shouldRun = true;
168
+    return true;
169
+}
170
+
171
+- (void)cleanup {
172
+    if (self.discoveredPeripheral) {
173
+        if ([self.discoveredPeripheral state] != CBPeripheralStateDisconnected) {
174
+            NSLog(@"Disconnecting from device...");
175
+            [self.centralManager cancelPeripheralConnection:self.discoveredPeripheral];
176
+        }
177
+    }
178
+    
179
+    if (self.fileHandle) {
180
+        NSLog(@"Closing logfile...");
181
+        [self.fileHandle closeFile];
182
+        self.fileHandle = nil;
183
+    }
184
+    
185
+    exit(0);
186
+}
187
+
188
+@end
189
+
190
+BlueTerm *bt;
191
+
192
+void signalHandler(int sig) {
193
+    [bt cleanup];
194
+}
10 195
 
11 196
 int main(int argc, const char * argv[]) {
12 197
     @autoreleasepool {
13
-        // insert code here...
14
-        NSLog(@"Hello, World!");
198
+        NSLog(@"Initializing BlueTerm...");
199
+        if (signal(SIGINT, signalHandler) == SIG_ERR) {
200
+            NSLog(@"Error, can't catch SIGINT...");
201
+            return 1;
202
+        }
203
+        
204
+        bt = [[BlueTerm alloc] init];
205
+        if (![bt initializeTargetFile:argc withParams:argv]) {
206
+            return 2;
207
+        }
208
+        
209
+        [[NSRunLoop currentRunLoop] run];
15 210
     }
16 211
     return 0;
17 212
 }

Loading…
Cancel
Save