123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- //
- // main.m
- // BlueTerm
- //
- // Created by Thomas Buck on 29.05.17.
- // Copyright © 2017 Thomas Buck. All rights reserved.
- // xythobuz@xythobuz.de
- //
- // Heavily based on:
- // https://gist.github.com/brayden-morris-303/09a738ed9c83a7d14c82
- //
- // This utility connects to a common Bluetooth 4.0 BLE UART bridge
- // and logs all received data to the local filesystem.
- // This has been developed for use as a Betaflight blackbox replacement.
-
- #import <Foundation/Foundation.h>
- #import <CoreBluetooth/CoreBluetooth.h>
-
- // ---------------------------------------------------------------------------------------
-
- // BLE UART device name that is searched for
- static NSString * const kDeviceName = @"xyCopter";
-
- // BLE Service & Characteristic UUIDs for the UART data stream
- static NSString * const kUARTServiceUUID = @"ffe0";
- static NSString * const kUARTCharacteristicUUID = @"ffe1";
-
- // Default log file path, if no command line argument is given
- static NSString * const kDefaultFilePath = @"~/BlueTerm.log";
-
- // ---------------------------------------------------------------------------------------
-
- @interface BlueTerm : NSObject <CBCentralManagerDelegate, CBPeripheralDelegate>
-
- @property (strong, nonatomic) CBCentralManager *centralManager;
- @property (strong, nonatomic) CBPeripheral *discoveredPeripheral;
- @property (assign) BOOL shouldRun;
- @property (strong, nonatomic) NSFileHandle *fileHandle;
-
- @end
-
- @implementation BlueTerm
-
- - (id)init {
- if (self = [super init]) {
- self.shouldRun = false;
- self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
- return self;
- }
- return nil;
- }
-
- - (void)centralManagerDidUpdateState:(CBCentralManager *)central {
- if (!self.shouldRun) return;
-
- // You should test all scenarios
- if (central.state != CBCentralManagerStatePoweredOn) {
- NSLog(@"Unknown BLE state: %ld", (long)central.state);
- return;
- }
-
- if (central.state == CBCentralManagerStatePoweredOn) {
- // Scan for devices
- [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kUARTServiceUUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
- NSLog(@"Scanning started...");
- }
- }
-
- - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
-
- NSLog(@"Discovered %@ at %@", peripheral.name, RSSI);
-
- if (([peripheral.name isEqualToString:kDeviceName]) && (self.discoveredPeripheral != peripheral)) {
- // Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
- self.discoveredPeripheral = peripheral;
-
- // And connect
- NSLog(@"Connecting to peripheral %@", peripheral);
- [self.centralManager connectPeripheral:peripheral options:nil];
- }
- }
-
- - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
- NSLog(@"Connected");
-
- [_centralManager stopScan];
- NSLog(@"Scanning stopped");
-
- peripheral.delegate = self;
-
- [peripheral discoverServices:@[[CBUUID UUIDWithString:kUARTServiceUUID]]];
- }
-
- - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
- if (error) {
- NSLog(@"Error: %@", error);
- return;
- }
-
- for (CBService *service in peripheral.services) {
- NSLog(@"Discovered service: %@", service.UUID);
-
- if ([service.UUID isEqual:[CBUUID UUIDWithString:kUARTServiceUUID]]) {
- NSLog(@"This is our service. Discovering characteristics...");
-
- [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kUARTCharacteristicUUID]] forService:service];
- }
- }
- }
-
- - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
- if (error) {
- return;
- }
-
- for (CBCharacteristic *characteristic in service.characteristics) {
- NSLog(@"Discovered characteristic: %@", characteristic.UUID);
-
- if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kUARTCharacteristicUUID]]) {
- NSLog(@"This is our characteristic! Subscribing...");
-
- [peripheral setNotifyValue:YES forCharacteristic:characteristic];
- }
- }
- }
-
- - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
- if (error) {
- return;
- }
-
- static time_t lastTime = 0;
- time_t now = time(NULL);
- if ((now - lastTime) > 0) {
- NSString *valueString = [[[[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding] stringByReplacingOccurrencesOfString:@"\n" withString:@""] stringByReplacingOccurrencesOfString:@"\r" withString:@""];
- NSLog(@"Received more data (excerpt: \"%@\")", valueString);
- lastTime = now;
- }
-
- [self.fileHandle writeData:characteristic.value];
- }
-
- - (BOOL)initializeTargetFile:(int)argc withParams:(const char **)argv {
- NSString *filePath;
- if (argc == 1) {
- // Use hard-coded default path
- filePath = [kDefaultFilePath stringByExpandingTildeInPath];
- } else if (argc == 2) {
- // Use given path
- filePath = [[NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding] stringByExpandingTildeInPath];
- } else {
- NSLog(@"Usage:\n\t%@ [/path/to/log]", [NSString stringWithCString:argv[0] encoding:NSUTF8StringEncoding]);
- return false;
- }
-
- [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
- self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
- if (self.fileHandle) {
- NSLog(@"Writing received data to: \"%@\"!", filePath);
- NSLog(@"File has been overwritten (if it existed before)!");
- [self.fileHandle seekToEndOfFile];
- } else {
- NSLog(@"Error opening logfile: \"%@\"!", filePath);
- return false;
- }
-
- self.shouldRun = true;
- return true;
- }
-
- - (void)cleanup {
- if (self.discoveredPeripheral) {
- if ([self.discoveredPeripheral state] != CBPeripheralStateDisconnected) {
- NSLog(@"Disconnecting from device...");
- [self.centralManager cancelPeripheralConnection:self.discoveredPeripheral];
- }
- }
-
- if (self.fileHandle) {
- NSLog(@"Closing logfile...");
- [self.fileHandle closeFile];
- self.fileHandle = nil;
- }
-
- exit(0);
- }
-
- @end
-
- BlueTerm *bt;
-
- void signalHandler(int sig) {
- [bt cleanup];
- }
-
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- NSLog(@"Initializing BlueTerm...");
- if (signal(SIGINT, signalHandler) == SIG_ERR) {
- NSLog(@"Error, can't catch SIGINT...");
- return 1;
- }
-
- bt = [[BlueTerm alloc] init];
- if (![bt initializeTargetFile:argc withParams:argv]) {
- return 2;
- }
-
- [[NSRunLoop currentRunLoop] run];
- }
- return 0;
- }
|