Skip to content
This repository has been archived by the owner on Sep 21, 2022. It is now read-only.

Commit

Permalink
Added some extra error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
benwithjamin committed May 14, 2020
1 parent 1e0129f commit 5e09583
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 37 deletions.
10 changes: 6 additions & 4 deletions Derailleur.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -331,15 +331,16 @@
CODE_SIGN_ENTITLEMENTS = Derailleur/Derailleur.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = BY2JKFEKFA;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Derailleur/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = "0.1-alpha";
PRODUCT_BUNDLE_IDENTIFIER = com.benwithjamin.Derailleur;
MARKETING_VERSION = 0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.benwithjamin.derailleur;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
Expand All @@ -351,15 +352,16 @@
CODE_SIGN_ENTITLEMENTS = Derailleur/Derailleur.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = BY2JKFEKFA;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Derailleur/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = "0.1-alpha";
PRODUCT_BUNDLE_IDENTIFIER = com.benwithjamin.Derailleur;
MARKETING_VERSION = 0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.benwithjamin.derailleur;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
Expand Down
9 changes: 9 additions & 0 deletions Derailleur/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,18 @@
//

#import <Cocoa/Cocoa.h>
#import "BluetoothManager.h"

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (nonatomic, strong) NSWindow *window;
@property (nonatomic, strong) IBOutlet NSMenuItem *_connectBikeButton;
@property (nonatomic, strong) IBOutlet NSMenuItem *_disconnectBikeButton;

@property (nonatomic, strong) BluetoothManager *bluetoothManager;

- (IBAction)startConnect:(id)sender;
- (IBAction)disconnect:(id)sender;

@end

19 changes: 13 additions & 6 deletions Derailleur/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@
#import "DerailleurMainView.h"
#import "NSColor+DerailleurColours.h"

@interface AppDelegate ()

@property (nonatomic, strong) NSWindow *window;

@end

@implementation AppDelegate
{
IOPMAssertionID preventSleepAssertion;
Expand All @@ -55,6 +49,10 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

[_window makeKeyAndOrderFront:nil];

/* Setup BluetoothManager here rather than in DerailleurMainView */
_bluetoothManager = [[BluetoothManager alloc] init];
[_bluetoothManager setDelegate: [_window contentView]];

/* Prevent display from sleeping until the application closes */
IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("Derailleur displaying data"), &preventSleepAssertion);
}
Expand All @@ -68,5 +66,14 @@ - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
return YES;
}

- (void)startConnect:(id)sender
{
[_bluetoothManager startConnectAttempt];
}

- (void)disconnect:(id)sender
{
[_bluetoothManager disconnectBike];
}

@end
21 changes: 18 additions & 3 deletions Derailleur/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
Expand All @@ -26,8 +26,6 @@
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
Expand Down Expand Up @@ -71,6 +69,23 @@
</items>
</menu>
</menuItem>
<menuItem title="Bike" id="TNt-nD-TW0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Bike" id="4TI-0g-zFp">
<items>
<menuItem title="Connect" keyEquivalent="C" id="Rvd-tC-bGt">
<connections>
<action selector="startConnect:" target="Voe-Tx-rLC" id="lK1-yW-Xpc"/>
</connections>
</menuItem>
<menuItem title="Disconnect" enabled="NO" keyEquivalent="D" id="S0N-Dw-JpK">
<connections>
<action selector="disconnect:" target="Voe-Tx-rLC" id="eLf-bp-3zr"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
Expand Down
9 changes: 7 additions & 2 deletions Derailleur/Bluetooth/BluetoothManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
#define BluetoothManager_h

#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>

#import "BikeData.h"
#import "BluetoothStatuses.h"

#define SCAN_TIMEOUT 60
#define CONNECT_TIMEOUT 60

NS_ASSUME_NONNULL_BEGIN

@protocol BluetoothManagerDelegate <NSObject>
Expand All @@ -39,11 +42,13 @@ NS_ASSUME_NONNULL_BEGIN

@end


@interface BluetoothManager : NSObject <CBCentralManagerDelegate, CBPeripheralDelegate>

@property (nonatomic, weak) id <BluetoothManagerDelegate> delegate;

- (void) startConnectAttempt;
- (void) disconnectBike;

@end

NS_ASSUME_NONNULL_END
Expand Down
66 changes: 55 additions & 11 deletions Derailleur/Bluetooth/BluetoothManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@
#import "BikeData.h"

@implementation BluetoothManager
{
/* CoreBluetooth properties */
CBCentralManager *_centralManager;
CBPeripheral *_connectedPeripheral;
CBCharacteristic *_currentCharacteristic;

/* CoreBluetooth properties */
CBCentralManager *_centralManager;
CBPeripheral *_connectedPeripheral;
CBCharacteristic *_currentCharacteristic;
/* Connection timer */
NSTimer *_pollTimer;
}

/* General properties */
int dataPacketLength;
Expand Down Expand Up @@ -63,15 +67,43 @@ - (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central
break;

case CBManagerStatePoweredOn:
[_delegate didUpdateStatus:BLUETOOTH_POWERED_ON_SCANNING];
[_centralManager scanForPeripheralsWithServices:nil options:nil];
[self startConnectAttempt];
break;

default:
break;
}
}

/* Start to try to connect to Flywheel bikes. Times out after 60 seconds. */
- (void)startConnectAttempt
{
[_delegate didUpdateStatus:BLUETOOTH_POWERED_ON_SCANNING];
[_centralManager scanForPeripheralsWithServices:nil options:nil];

_pollTimer = [NSTimer scheduledTimerWithTimeInterval:SCAN_TIMEOUT target:self selector:@selector(didTimeoutWhileScanning) userInfo:nil repeats:NO];
}

/* Disconnect from the Flywheel bike, if it is connected */
- (void)disconnectBike
{
if (_connectedPeripheral != nil)
{
[_delegate didUpdateStatus:BIKE_DISCONNECT_REQUEST];
[_centralManager cancelPeripheralConnection:_connectedPeripheral];
_connectedPeripheral = nil;
}
}

/* Called if and when the Bluetooth scan does not find a Flywheel bike */
- (void)didTimeoutWhileScanning
{
[_delegate didUpdateStatus:BIKE_UNABLE_TO_DISCOVER];
[_pollTimer invalidate];

[_centralManager stopScan];
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
NSString *deviceName;
Expand All @@ -82,22 +114,36 @@ - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeri
}

if ([[deviceName lowercaseString] containsString:@"flywheel"]) {
[_pollTimer invalidate];
[_centralManager stopScan];

_connectedPeripheral = peripheral;
_connectedPeripheral.delegate = self;

[_centralManager connectPeripheral:_connectedPeripheral options:nil];
_pollTimer = [NSTimer scheduledTimerWithTimeInterval:CONNECT_TIMEOUT target:self selector:@selector(didTimeoutWhileConnecting) userInfo:nil repeats:NO];

[_delegate didUpdateStatus:BIKE_DISCOVERED];
} else {
/* Let's log this so we can see what devices were found other than Flywheel bikes... */

}
}

- (void)didTimeoutWhileConnecting
{
[_delegate didUpdateStatus:BIKE_UNABLE_TO_CONNECT];
[_pollTimer invalidate];

[_centralManager cancelPeripheralConnection:_connectedPeripheral];
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
if (peripheral == _connectedPeripheral) {
[_connectedPeripheral discoverServices: @[[CBUUID UUIDWithString:ICG_SERVICE_UUID]]];
[_delegate didUpdateStatus:BIKE_CONNECTED];
[_pollTimer invalidate];
}
}

Expand Down Expand Up @@ -204,19 +250,17 @@ - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(C
return;
}

// TODO: Aggregated data and calibration
if (bikeData.message_id == BRAKE_CALIBRATION_RESET) {

[_delegate didUpdateStatus:BIKE_NEEDS_CALIBRATION];
return;
}

if (bikeData.message_id == SEND_ICG_AGGREGATED_STREAM_DATA) {

}

if (bikeData.message_id == REQUEST_DISCONNECT) {
[_delegate didUpdateStatus:BIKE_DISCONNECT_REQUEST];
[_centralManager cancelPeripheralConnection:_connectedPeripheral];
_connectedPeripheral = nil;
[self disconnectBike];
return;
}

Expand Down
10 changes: 6 additions & 4 deletions Derailleur/Bluetooth/BluetoothStatuses.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,22 @@
#define BIKE_MESSAGE_UNKNOWN 0x09
#define BIKE_CONNECTED 0x0A
#define BIKE_CONNECTED_RECEIVING 0x0B
#define BIKE_NEEDS_CALIBRATION 0x0C

static const char *STATUS_MESSAGES[] = {
"Bluetooth is powered off. Power it on in System Preferences to continue.",
"Bluetooth powered on. Scanning for Flywheel bikes...",
"Bluetooth use is unauthorised. You may lack the required privileges.",
"Bluetooth is not supported on this device.",
"Flywheel bike discovered. Attempting to connect...",
"Unable to connect to your Flywheel bike in 60 seconds",
"No Flywheel bikes found. Start pedaling to activate the Bluetooth in the bike.",
"Could not connect to your Flywheel bike within 60 seconds",
"No Flywheel bikes found. Start pedaling to activate the bike's Bluetooth.",
"An unknown error has occurred",
"Received a disconnect request from your Flywheel bike",
"Disconnected from your Flywheel bike successfully",
"Your bike sent a message that could not be understood",
"Connected to your Flywheel bike. Waiting for data",
"Connected to your Flywheel bike. Receiving data"
"Connected to your Flywheel bike. Receiving data",
"Your bike needs to be calibrated"
};

#endif /* BluetoothStatuses_h */
2 changes: 1 addition & 1 deletion Derailleur/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.healthcare-fitness</string>
<key>LSMinimumSystemVersion</key>
Expand Down
2 changes: 0 additions & 2 deletions Derailleur/UI/DerailleurMainView.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) NSTextField *statusLabel;
@property (nonatomic, strong) StatusDot *statusDot;

@property (nonatomic, strong) BluetoothManager* bluetoothManager;

@end

NS_ASSUME_NONNULL_END
4 changes: 1 addition & 3 deletions Derailleur/UI/DerailleurMainView.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ - (instancetype) init
{
self = [super init];
if (self) {
_bluetoothManager = [[BluetoothManager alloc] init];
[_bluetoothManager setDelegate:self];

[self setupViews];
[self setupLayout];
}
Expand Down Expand Up @@ -211,6 +208,7 @@ - (void)didUpdateStatus:(int)status {
break;

case BIKE_CONNECTED:
case BIKE_NEEDS_CALIBRATION:
[_statusDot stopFlashing];
[_statusDot setColour: GREEN];
break;
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ I created this app to give new life to the Flywheel bike taking up a large amoun

If you want to tinker with the code, go forth and do so. Otherwise, head to Releases and download the latest stable release.

Just start the app and it will automatically look for your Flywheel bike - make sure your Bluetooth is turned on and you've turned the pedals to activate the Bluetooth in the bike.
Just start the app and it will automatically look for your Flywheel bike - make sure your Bluetooth is turned on and you've turned the pedals to activate the Bluetooth in the bike. If you've never used your Flywheel bike before, make sure you have inserted the batteries in the compartment by the pedals - the Bluetooth is actually powered by these batteries and not the AC adapter.

![Screenshot of Derailleur in action](screenshot.png)
# If you have any issues...
Expand Down

0 comments on commit 5e09583

Please sign in to comment.