#import "ViewController.h"
#define TRANSFER_SERVICE_UUID @"9275FAEC-DCC5-4806-84B6-94C056D28F8E"
#define TRANSFER_CHARACTERISTIC_UUID @"F47E85EE-2C2D-4D79-B77A-2BE3F617635E"
@implementation ViewController
CBCentralManager *_centralManager;
CBPeripheral *_discoveredPeripheral;
NSMutableData *_data;
NSApplicationPresentationOptions *presentationOptions;
- (void)viewDidLoad {
[superviewDidLoad];
_centralManager = [[CBCentralManageralloc] initWithDelegate:selfqueue:nil];
_discoveredPeripheral = nil;
// And somewhere to store the incoming data
_data = [[NSMutableDataalloc] init];
/* NSApplicationPresentationOptions presentationOptions = (NSApplicationPresentationHideDock |
NSApplicationPresentationHideMenuBar |
NSApplicationPresentationDisableAppleMenu |
NSApplicationPresentationDisableProcessSwitching |
NSApplicationPresentationDisableSessionTermination |
NSApplicationPresentationDisableForceQuit |
NSApplicationPresentationDisableHideApplication);
NSDictionary *fullScreenOptions = @{NSFullScreenModeApplicationPresentationOptions: @(presentationOptions)};
[self.view enterFullScreenMode: [NSScreen mainScreen] withOptions:fullScreenOptions]; */
// Do any additional setup after loading the view.
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
// In a real app, you'd deal with all the states correctly
if (central.state != CBCentralManagerStatePoweredOn)
return;
// The state must be CBCentralManagerStatePoweredOn...
// ... so start scanning
[self scan];
}
/** Scan for peripherals - specifically for our service's 128bit CBUUID */
- (void)scan { [_centralManagerscanForPeripheralsWithServices:@[[CBUUIDUUIDWithString:TRANSFER_SERVICE_UUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
NSLog(@"Scanning started");
}
/** This callback comes whenever a peripheral that is advertising the TRANSFER_SERVICE_UUID is discovered.
* We check the RSSI, to make sure it's close enough that we're interested in it, and if it is,
* we start the connection process
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
// Reject any where the value is above reasonable range
if (RSSI.integerValue > -15)
return;
// Reject if the signal strength is too low to be close enough (Close is around -22dB)
if (RSSI.integerValue < -50)
return;
NSLog(@"Discovered %@ at %@", peripheral.name, RSSI);
// Ok, it's in range - have we already seen it?
if (_discoveredPeripheral != peripheral) {
// Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
_discoveredPeripheral = peripheral;
// And connect
NSLog(@"Connecting to peripheral %@", peripheral);
[_centralManager connectPeripheral:peripheral options:nil];
}
}
/** If the connection fails for whatever reason, we need to deal with it. */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"Failed to connect to %@. (%@)", peripheral, [error localizedDescription]);
[self cleanup];
}
/** We've connected to the peripheral, now we need to discover the services and characteristics to find the 'transfer' characteristic. */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral Connected");
// Stop scanning
[_centralManagerstopScan];
NSLog(@"Scanning stopped");
// Clear the data that we may already have
[_data setLength:0];
// Make sure we get the discovery callbacks
peripheral.delegate = self;
// Search only for services that match our UUID
[peripheral discoverServices:@[[CBUUIDUUIDWithString:TRANSFER_SERVICE_UUID]]];
}
/** The Transfer Service was discovered*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
NSLog(@"didDiscoverServices...");
if (error) {
NSLog(@"Error discovering services: %@", [error localizedDescription]);
[self cleanup];
return;
}
// Discover the characteristic we want...
// Loop through the newly filled peripheral.services array, just in case there's more than one.
for (CBService *service in peripheral.services) {
NSLog(@"Check services...");
[peripheral discoverCharacteristics:@[[CBUUIDUUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]forService:service];
}
}
/** The Transfer characteristic was discovered.
* Once this has been found, we want to subscribe to it, which lets the peripheral know we want the data it contains */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
NSLog(@"Did discover characteristics");
// Deal with errors (if any)
if (error) {
NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
[self cleanup];
return;
}
NSLog(@"No error...");
// Again, we loop through the array, just in case.
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"%@", characteristic.UUID);
// And check if it's the right one
// if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
// If it is, subscribe to it
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
// }
}
// Once this is complete, we just need to wait for the data to come in.
}
/** This callback lets us know more data has arrived via notification on the characteristic */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
return;
}
NSString *stringFromData = [[NSStringalloc] initWithData:characteristic.valueencoding:NSUTF8StringEncoding];
// Have we got everything we need?
if ([stringFromData isEqualToString:@"EOM"]) {
// We have, so show the data,
NSLog(@"%@", [[NSStringalloc] initWithData:_dataencoding:NSUTF8StringEncoding]);
// Cancel our subscription to the characteristic
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
// and disconnect from the peripehral
[_centralManagercancelPeripheralConnection:peripheral];
}
// Otherwise, just add the data on to what we already have
[_data appendData:characteristic.value];
// Log it
NSLog(@"Received: %@", stringFromData);
}
/** The peripheral letting us know whether our subscribe/unsubscribe happened or not */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state: %@", error.localizedDescription);
}
// Exit if it's not the transfer characteristic
if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
NSLog(@"Exit...");
return;
}
// Notification has started
if (characteristic.isNotifying) {
NSLog(@"Notification began on %@", characteristic);
} else { // Notification has stopped
// so disconnect from the peripheral
NSLog(@"Notification stopped on %@. Disconnecting", characteristic);
[_centralManagercancelPeripheralConnection:peripheral];
}
}
/** Once the disconnection happens, we need to clean up our local copy of the peripheral */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"Peripheral Disconnected");
_discoveredPeripheral = nil;
// We're disconnected, so start scanning again
[self scan];
}
/** Call this when things either go wrong, or you're done with the connection.
* This cancels any subscriptions if there are any, or straight disconnects if not.
* (didUpdateNotificationStateForCharacteristic will cancel the connection if a subscription is involved)
*/
- (void)cleanup {
// Don't do anything if we're not connected
// See if we are subscribed to a characteristic on the peripheral
if (_discoveredPeripheral.services != nil) {
for (CBService *service in _discoveredPeripheral.services) {
if (service.characteristics != nil) {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
if (characteristic.isNotifying) {
// It is notifying, so unsubscribe
[_discoveredPeripheral setNotifyValue:NO forCharacteristic:characteristic];
// And we're done.
return;
}
}
}
}
}
}
// If we've got this far, we're connected, but we're not subscribed, so we just disconnect
[_centralManagercancelPeripheralConnection:_discoveredPeripheral];
}
@end