Skip to content

Instantly share code, notes, and snippets.

@jsorge
Last active August 29, 2015 14:00
Show Gist options
  • Save jsorge/11390537 to your computer and use it in GitHub Desktop.
Save jsorge/11390537 to your computer and use it in GitHub Desktop.
CoreBluetooth Helpers
//
// JMSBTCentral.h
// CP130_HW2_Bluetooth
//
// Created by Jared Sorge on 4/20/14.
// Copyright (c) 2014 jsorge. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol JMSBTCentralDelegate;
@interface JMSBTCentral : NSObject
@property (weak, nonatomic)id<JMSBTCentralDelegate>delegate;
@property (readonly, nonatomic)BOOL scanning;
- (instancetype)initWithDelegate:(id<JMSBTCentralDelegate>)delegate serviceID:(NSString *)serviceID characteristicID:(NSString *)characteristicID;
- (void)startUpdating;
- (void)stopUpdating;
@end
@protocol JMSBTCentralDelegate <NSObject>
- (void)jmsCentralDidUpdateDataOutput:(JMSBTCentral *)central withString:(NSString *)outputString;
- (void)jmsCentralDidConnectToPeripheral:(JMSBTCentral *)central;
- (void)jmsCentralDidDisconnectPeripheral:(JMSBTCentral *)central;
@end
//
// JMSBTCentral.m
// CP130_HW2_Bluetooth
//
// Created by Jared Sorge on 4/20/14.
// Copyright (c) 2014 jsorge. All rights reserved.
//
#import "JMSBTCentral.h"
#import <CoreBluetooth/CoreBluetooth.h>
@interface JMSBTCentral ()<CBCentralManagerDelegate, CBPeripheralDelegate>
@property (readwrite, nonatomic)NSMutableData *dataOutput;
@property (readwrite, nonatomic)BOOL scanning;
@property (strong, nonatomic)CBUUID *serviceUUID;
@property (strong, nonatomic)CBUUID *characteristicUUID;
@property (strong, nonatomic)CBCentralManager *manager;
@property (strong, nonatomic)CBPeripheral *peripheral;
@end
@implementation JMSBTCentral
#pragma mark - API
- (instancetype)initWithDelegate:(id<JMSBTCentralDelegate>)delegate serviceID:(NSString *)serviceID characteristicID:(NSString *)characteristicID
{
self = [super init];
if (self) {
self.serviceUUID = [CBUUID UUIDWithString:serviceID];
self.characteristicUUID = [CBUUID UUIDWithString:characteristicID];
self.delegate = delegate;
[self startUpdating];
}
return self;
}
- (void)startUpdating
{
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
- (void)stopUpdating
{
[self.manager stopScan];
if (self.peripheral) {
[self.manager cancelPeripheralConnection:self.peripheral];
}
self.scanning = NO;
}
#pragma mark - Properties
- (NSMutableData *)dataOutput
{
if (!_dataOutput) {
_dataOutput = [NSMutableData data];
}
return _dataOutput;
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (central.state == CBCentralManagerStatePoweredOn) {
[self startCentralScan];
self.scanning = YES;
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (peripheral != self.peripheral) {
self.peripheral = peripheral;
[self.manager connectPeripheral:peripheral options:nil];
}
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[self cleanup];
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[self.manager stopScan];
self.dataOutput = nil;
peripheral.delegate = self;
[peripheral discoverServices:@[self.serviceUUID]];
[self.delegate jmsCentralDidConnectToPeripheral:self];
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"%s", __PRETTY_FUNCTION__);
self.peripheral = nil;
[self.delegate jmsCentralDidDisconnectPeripheral:self];
[self startCentralScan];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (error) {
NSLog(@"Error: %@", error);
[self cleanup];
return;
}
for (CBService *service in peripheral.services) {
NSLog(@"Found service %@", service.UUID);
[peripheral discoverCharacteristics:@[self.characteristicUUID]
forService:service];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (error) {
NSLog(@"Erro: %@", error);
[self cleanup];
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:self.characteristicUUID]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
NSLog(@"discovered characteristic %@", characteristic.UUID);
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (error) {
NSLog(@"Error: %@", error);
return;
}
NSString *receivedString = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
if ([receivedString isEqualToString:@"EOM"]) {
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
[self.manager cancelPeripheralConnection:peripheral];
} else {
[self.delegate jmsCentralDidUpdateDataOutput:self withString:receivedString];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (![characteristic.UUID isEqual:self.characteristicUUID]) {
return;
}
if (!characteristic.isNotifying) {
[self.manager cancelPeripheralConnection:peripheral];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray *)invalidatedServices
{
NSLog(@"%s", __PRETTY_FUNCTION__);
}
#pragma mark - Private
- (void)cleanup
{
if (self.peripheral.services != nil) {
for (CBService *service in self.peripheral.services) {
if (service.peripheral != nil) {
for (CBCharacteristic *characteristic in service.characteristics) {
if (characteristic.isNotifying) {
[self.peripheral setNotifyValue:NO forCharacteristic:characteristic];
}
}
}
}
}
}
- (void)startCentralScan
{
[self.manager scanForPeripheralsWithServices:@[self.serviceUUID]
options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @YES}];
}
@end
//
// JMSBTPeripheral.h
// ImHere
//
// Created by Jared Sorge on 4/13/14.
// Copyright (c) 2014 jsorge. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol JMSBTPeripheralDelegate;
@interface JMSBTPeripheral : NSObject
@property (readonly, nonatomic)BOOL broadcasting;
@property (weak, nonatomic)id<JMSBTPeripheralDelegate>delegate;
@property (strong, nonatomic)NSData *dataToSend;
- (instancetype)initWithDelegate:(id<JMSBTPeripheralDelegate>)delegate serviceID:(NSString *)serviceID characteristicID:(NSString *)characteristicID;
- (void)broadcastData:(NSString *)data;
- (void)startBroadcasting;
- (void)stopBroadcasting;
@end
@protocol JMSBTPeripheralDelegate <NSObject>
@end
//
// JMSBTPeripheral.m
// ImHere
//
// Created by Jared Sorge on 4/13/14.
// Copyright (c) 2014 jsorge. All rights reserved.
//
#import "JMSBTPeripheral.h"
#import <CoreBluetooth/CoreBluetooth.h>
@interface JMSBTPeripheral () <CBPeripheralManagerDelegate>
@property (readwrite, nonatomic)BOOL broadcasting;
@property (strong, nonatomic)CBUUID *serviceUUID;
@property (strong, nonatomic)CBUUID *characteristicUUID;
@property (strong, nonatomic)CBPeripheralManager *peripheralManager;
@property (strong, nonatomic)CBMutableCharacteristic *transferCharacteristic;
@property (strong, nonatomic)CBMutableService *service;
@property (nonatomic)NSInteger sendDataIndex;
@end
@implementation JMSBTPeripheral
#pragma mark - API
- (instancetype)initWithDelegate:(id<JMSBTPeripheralDelegate>)delegate serviceID:(NSString *)serviceID characteristicID:(NSString *)characteristicID
{
self = [super init];
if (self) {
self.delegate = delegate;
self.serviceUUID = [CBUUID UUIDWithString:serviceID];
self.characteristicUUID = [CBUUID UUIDWithString:characteristicID];
}
return self;
}
- (void)broadcastData:(NSString *)data
{
if (self.broadcasting) {
self.dataToSend = [data dataUsingEncoding:NSStringEncodingConversionExternalRepresentation];
self.sendDataIndex = 0;
[self sendData];
}
}
- (void)startBroadcasting
{
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey: @[self.serviceUUID]}];
self.broadcasting = YES;
NSLog(@"Started broadcasting");
}
- (void)stopBroadcasting
{
[self.peripheralManager stopAdvertising];
[self.peripheralManager removeAllServices];
self.peripheralManager = nil;
self.broadcasting = NO;
}
#pragma mark - Properties
- (CBPeripheralManager *)peripheralManager
{
if (!_peripheralManager) {
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
}
return _peripheralManager;
}
#pragma mark - CBPeripheralManagerDelegate
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
return;
}
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:self.characteristicUUID
properties:CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable];
CBMutableService *transferService = [[CBMutableService alloc] initWithType:self.serviceUUID
primary:YES];
transferService.characteristics = @[self.transferCharacteristic];
[self.peripheralManager addService:transferService];
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
[self sendData];
}
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral
{
[self sendData];
}
#pragma mark - Private
- (void)sendData
{
static BOOL sendingEndOfMessage = NO;
if (sendingEndOfMessage) {
BOOL didSend = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding]
forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil];
if (didSend) {
sendingEndOfMessage = NO;
}
return;
}
if (self.sendDataIndex >= self.dataToSend.length) {
return;
}
BOOL didSend = YES;
while (didSend) {
NSInteger amountToSend = self.dataToSend.length - self.sendDataIndex;
amountToSend = amountToSend > 20 ? 20 : amountToSend;
NSData *chunk = [NSData dataWithBytes:self.dataToSend.bytes+self.sendDataIndex length:amountToSend];
didSend = [self.peripheralManager updateValue:chunk
forCharacteristic:self.transferCharacteristic
onSubscribedCentrals:nil];
if (!didSend) {
return;
}
NSString *stringFromData = [[NSString alloc] initWithData:chunk encoding:NSUTF8StringEncoding];
NSLog(@"Sent: %@", stringFromData);
self.sendDataIndex += amountToSend;
if (self.sendDataIndex >= self.dataToSend.length || self.sendDataIndex == self.dataToSend.length) {
sendingEndOfMessage = YES;
BOOL endOfMessageSent = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding]
forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil];
if (endOfMessageSent) {
sendingEndOfMessage = NO;
NSLog(@"Sent: EOM");
}
return;
}
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment