Created
December 19, 2013 00:35
-
-
Save yonran/8032363 to your computer and use it in GitHub Desktop.
Bluetooth LE Peripheral Role hello world based on [Performing Common Peripheral Role Tasks](https://developer.apple.com/library/mac/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonPeripheralRoleTasks/PerformingCommonPeripheralRoleTasks.html). When I run this program on a Mac Mini, the peripheral is advertisi…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import <IOBluetooth/IOBluetooth.h> | |
enum HandlerState { | |
HandlerStateStarting, | |
HandlerStateWaitingForServiceToAdd, | |
HandlerStateWaitingForAdvertisingToStart, | |
HandlerStateAdvertising, | |
HandlerStateError, | |
}; | |
@interface Handler: NSObject <CBPeripheralManagerDelegate> | |
@property (nonatomic) CBPeripheralManager *peripheralManager; | |
@property (nonatomic) enum HandlerState state; | |
@property (nonatomic) NSTimer *incrementTimer; | |
@property (nonatomic) uint16_t count; | |
@property (nonatomic) CBMutableCharacteristic *countCharacteristic; | |
@end | |
static NSString *MyServiceUuid = @"5C128D26-741E-4BE4-95E5-9DEE6F97414B"; | |
static NSString *CounterCharacteristicUuid = @"5C128D26-741E-4BE4-95E5-9DEE6F97414C"; | |
@implementation Handler | |
+ (NSString*)stringFromCBPeripheralManagerState:(CBPeripheralManagerState)state { | |
switch (state) { | |
case CBPeripheralManagerStatePoweredOff: return @"PoweredOff"; | |
case CBPeripheralManagerStatePoweredOn: return @"PoweredOn"; | |
case CBPeripheralManagerStateResetting: return @"Resetting"; | |
case CBPeripheralManagerStateUnauthorized: return @"Unauthorized"; | |
case CBPeripheralManagerStateUnknown: return @"Unknown"; | |
case CBPeripheralManagerStateUnsupported: return @"Unsupported"; | |
} | |
} | |
+ (NSString*)stringFromHandlerState:(enum HandlerState)state { | |
switch (state) { | |
case HandlerStateStarting: return @"Starting"; | |
case HandlerStateWaitingForServiceToAdd: return @"WaitingForServiceToAdd"; | |
case HandlerStateAdvertising: return @"Advertising"; | |
case HandlerStateWaitingForAdvertisingToStart: return @"WaitingForAdvertisingToStart"; | |
case HandlerStateError: return @"Error"; | |
} | |
} | |
- (void)raiseBadStateTransition:(enum HandlerState) newState { | |
[NSException raise:@"Invalid state transition" format:@"Cannot transition from %@ to %@", [Handler stringFromHandlerState:self.state], [Handler stringFromHandlerState:newState]]; | |
} | |
- (void)transitionState:(enum HandlerState)newState { | |
switch (self.state) { | |
case HandlerStateStarting: | |
switch (newState) { | |
case HandlerStateStarting: break; | |
case HandlerStateWaitingForServiceToAdd: { | |
CBMutableService *service = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:MyServiceUuid] primary:YES]; | |
NSMutableArray *characteristics = [[NSMutableArray alloc] init]; | |
CBMutableCharacteristic *characteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:CounterCharacteristicUuid] properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyIndicate value:nil permissions:CBAttributePermissionsReadable]; | |
[characteristics addObject:characteristic]; | |
self.countCharacteristic = characteristic; | |
[self.peripheralManager addService:service]; | |
break; | |
} | |
case HandlerStateWaitingForAdvertisingToStart: | |
case HandlerStateAdvertising: | |
[self raiseBadStateTransition:newState]; | |
case HandlerStateError: break; | |
} | |
break; | |
case HandlerStateWaitingForServiceToAdd: | |
switch (newState) { | |
case HandlerStateStarting: break; | |
case HandlerStateWaitingForServiceToAdd: break; | |
case HandlerStateWaitingForAdvertisingToStart: | |
[self.peripheralManager startAdvertising:@{CBAdvertisementDataLocalNameKey: @"Hello World Peripheral", CBAdvertisementDataServiceUUIDsKey: @[[CBUUID UUIDWithString:MyServiceUuid]]}]; | |
break; | |
case HandlerStateAdvertising: | |
[self raiseBadStateTransition:newState]; | |
break; | |
case HandlerStateError: break; | |
} | |
break; | |
case HandlerStateWaitingForAdvertisingToStart: | |
switch (newState) { | |
case HandlerStateStarting: | |
[self.peripheralManager stopAdvertising]; | |
break; | |
case HandlerStateWaitingForServiceToAdd: | |
[self raiseBadStateTransition:newState]; | |
break; | |
case HandlerStateWaitingForAdvertisingToStart: break; | |
case HandlerStateAdvertising: | |
self.incrementTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(incrementCount:) userInfo:nil repeats:YES]; | |
break; | |
case HandlerStateError: break; | |
} | |
break; | |
case HandlerStateAdvertising: | |
switch (newState) { | |
case HandlerStateStarting: | |
[self.peripheralManager stopAdvertising]; | |
[self.incrementTimer invalidate]; | |
self.incrementTimer = nil; | |
break; | |
case HandlerStateWaitingForServiceToAdd: | |
case HandlerStateWaitingForAdvertisingToStart: | |
[self.incrementTimer invalidate]; | |
self.incrementTimer = nil; | |
[self raiseBadStateTransition:newState]; | |
break; | |
case HandlerStateAdvertising: | |
break; | |
case HandlerStateError: | |
[self.incrementTimer invalidate]; | |
self.incrementTimer = nil; | |
break; | |
} | |
break; | |
case HandlerStateError: break; | |
} | |
NSLog(@"Transitioning state from %@ to %@", [Handler stringFromHandlerState:self.state], [Handler stringFromHandlerState:newState]); | |
self.state = newState; | |
} | |
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { | |
NSLog(@"CBPeripheralManager entered state %@", [Handler stringFromCBPeripheralManagerState:peripheral.state]); | |
if (peripheral.state == CBPeripheralManagerStatePoweredOn) { | |
[self transitionState:HandlerStateWaitingForServiceToAdd]; | |
} else { | |
[self transitionState:HandlerStateStarting]; | |
} | |
} | |
- (void)incrementCount:(NSTimer*)timer { | |
self.count++; | |
uint16_t bigEndianCount = ntohs(self.count); | |
NSData *countData = [NSData dataWithBytes:&bigEndianCount length:sizeof(bigEndianCount)]; | |
if (self.countCharacteristic.subscribedCentrals.count) { | |
if ([self.peripheralManager updateValue:countData forCharacteristic:self.countCharacteristic onSubscribedCentrals:nil]) { | |
NSLog(@"Incremented count to %u. Successfully notified %lu subscribers", self.count, self.countCharacteristic.subscribedCentrals.count); | |
} else { | |
NSLog(@"Incremented count to %u. Queue full; waiting to notify %lu subscribers", self.count, self.countCharacteristic.subscribedCentrals.count); | |
} | |
} else { | |
NSLog(@"Incremented timer to %u, but no one is subscribed.", self.count); | |
} | |
} | |
- (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary *)dict { | |
NSLog(@"peripheralManager:willRestoreState: %@", dict); | |
} | |
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error { | |
NSLog(@"peripheralManagerDidStartAdvertising (error:%@)", error); | |
if (error == nil) { | |
[self transitionState:HandlerStateAdvertising]; | |
} else { | |
[self transitionState:HandlerStateError]; | |
} | |
} | |
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{ | |
if (error == nil) { | |
[self transitionState:HandlerStateWaitingForAdvertisingToStart]; | |
} else { | |
[self transitionState:HandlerStateError]; | |
} | |
} | |
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{ | |
NSLog(@"Central:%@ didSubscribeToCharacteristic:%@", central, characteristic); | |
} | |
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{ | |
NSLog(@"Central:%@ didUnsubscribeFromCharacteristic:%@", central, characteristic); | |
} | |
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{ | |
NSLog(@"DidReceiveReadRequest:%@", request); | |
if ([request.characteristic.UUID isEqual:[CBUUID UUIDWithString:CounterCharacteristicUuid]]) { | |
uint16_t bigEndianCount = ntohs(self.count); | |
request.value = [NSData dataWithBytes:&bigEndianCount length:sizeof(bigEndianCount)]; | |
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; | |
} else { | |
[self.peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound]; | |
} | |
} | |
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{ | |
for (CBATTRequest *request in requests) { | |
[self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted]; | |
} | |
} | |
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral { | |
uint16_t bigEndianCount = ntohs(self.count); | |
NSData *countData = [NSData dataWithBytes:&bigEndianCount length:sizeof(bigEndianCount)]; | |
if ([self.peripheralManager updateValue:countData forCharacteristic:self.countCharacteristic onSubscribedCentrals:nil]) { | |
NSLog(@"peripheralManagerIsReadyToUpdateSubscribers count to %us. Successfully sent to %lu subscribers", self.count, self.countCharacteristic.subscribedCentrals.count); | |
} else { | |
NSLog(@"peripheralManagerIsReadyToUpdateSubscribers count to %us. Queue full; waiting again to notify %lu subscribers", self.count, self.countCharacteristic.subscribedCentrals.count); | |
} | |
} | |
@end | |
int main(int argc, const char *argv[]) | |
{ | |
@autoreleasepool { | |
Handler *handler = [[Handler alloc] init]; | |
CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:handler queue:nil]; | |
handler.peripheralManager = peripheralManager; | |
peripheralManager.delegate = handler; | |
NSLog(@"Hello, World!"); | |
[[NSRunLoop currentRunLoop] run]; | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment