17

I'm writing an application for iOS that requires that the application advertise both an iOS iBeacon as well as advertise peripheral service concurrently. It's necessary that the service is advertised rather that simply discoverable on the peripheral because the use case requires the central (in BLE parlance) connect to the peripheral after being woken up by iOS (but still in the background) due to proximity to the iBeacon. Apps running in the background on centrals can only discover peripheral by available service rather than discovering all peripherals [] ; My code works to advertise either the service or the iBeacon but I haven't figured out how to do both at the same time. It's possible that the iBeacon uses 21bytes of the 38bytes of available space and there simply isn't enough space to advertise a beacon as well as a service?

This works (beacon):

self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid 
    major:1 
    minor:1 
    identifier:@"bentboolean"];
NSMutableDictionary *dict = [[self.beaconRegion peripheralDataWithMeasuredPower:nil] mutableCopy];    
[self.peripheralManager startAdvertising:dict ];

This works (service):

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:@[serviceUUID] forKey:CBAdvertisementDataServiceUUIDsKey];
[self.peripheralManager startAdvertising:dict ];

Adding the two together, trying to advertise both services at the same time doesn't work. It only advertises the Beacon, not the service:

self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid 
    major:1 
    minor:1 
    identifier:@"bentboolean"];
NSMutableDictionary *dict = [[self.beaconRegion peripheralDataWithMeasuredPower:nil] mutableCopy];  
[dict setValue:@[serviceUUID] forKey:CBAdvertisementDataServiceUUIDsKey];  
[self.peripheralManager startAdvertising:dict ];

Thanks for taking a look!

user2876951
  • 171
  • 1
  • 4

2 Answers2

0

I was able to get this going with a separate CLLocationManager and CLBeaconRegion for both the reciever and the beacon separately:

#import "GRBothViewController.h"

@interface GRBothViewController ()

@end

@implementation GRBothViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];


    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;

    self.locationScanner = [[CLLocationManager alloc] init];
    self.locationScanner.delegate = self;

    [self initBeacon];
    [self initRegion];
    [self locationManager:self.locationManager didStartMonitoringForRegion:self.scannerRegion];
}

- (void)initBeacon {
    NSLog(@"Starting beacon");

    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString: @"23542266-18D1-4FE4-B4A1-23F8195B9D39"];
    self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
                                                                major:1
                                                                minor:1
                                                           identifier:@"com.thisisgrow.Grow"];
    [self transmitBeacon:self];
}

- (IBAction)transmitBeacon:(id)sender {
    self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
    self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                                     queue:nil
                                                                   options:nil];
}

-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
        NSLog(@"Powered On");
        [self.peripheralManager startAdvertising:self.beaconPeripheralData];
    } else if (peripheral.state == CBPeripheralManagerStatePoweredOff) {
        NSLog(@"Powered Off");
        [self.peripheralManager stopAdvertising];
    }
}

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
    [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}

- (void)initRegion {
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"23542266-18D1-4FE4-B4A1-23F8195B9D39"];
    _scannerRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:@"com.thisisgrow.Grow"];
    _scannerRegion.notifyEntryStateOnDisplay = YES;
    [_locationScanner startMonitoringForRegion:self.scannerRegion];
}

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
    //SCANNER
    [self.locationScanner startRangingBeaconsInRegion:self.scannerRegion];
}

-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    //SCANNER HAS LEFT THE AREA
    [self.locationScanner stopRangingBeaconsInRegion:self.scannerRegion];
}

-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
    CLBeacon *beacon = [[CLBeacon alloc] init];
    NSLog(@"Beacons: %d", [beacons count]);
    if(beacons && [beacons count]>0){
        beacon = [beacons firstObject];
    }
    else{

    }
}

The only limitation here is that the device cannot detect itself.

d2burke
  • 4,081
  • 3
  • 39
  • 51
  • One other interesting observation I have seen: even if *another* app advertises as an iBeacon, your app cannot detect it. What is more, if an external iBeacon exists advertising the same identifiers, your app still cannot detect it. In other words, advertising an iBeacon ID set blocks any detections of the same ID set. – davidgyoung Nov 27 '13 at 02:56
  • True enough, however, when using a device as an iBeacon, it's simple enough to auto-generate a unique UUID for the device (even on launch if desired) if you want to use them for this purpose. To me, the most limiting factors are the fact that 1) a device used as an iBeacon won't broadcast in the background AT ALL, and 2) apps can not range for individual iBeacon's in the background except when woken up on onEnter and onLeave events which only fire when entering or leaving UUID regions. – d2burke Dec 02 '13 at 16:48
  • @d2burke You mention that a device used as an iBeacon won't broadcast in the background at all. Do you know if this is the case even if the app uses the bluetooth-peripheral background mode? – darrinm Dec 20 '13 at 23:14
  • @darrinm - unfortunately, yes. the iBeacon framework is actually separate from Core Bluetooth and behaves differently. – d2burke Dec 24 '13 at 22:24
  • "One other interesting observation I have seen: even if another app advertises as an iBeacon, your app cannot detect it. " Correct me, if I'm wrong, but this is no longer true with 7.1 – decades Mar 28 '14 at 19:15
  • (This comment has been removed) – decades Mar 28 '14 at 19:49
  • - (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region { [self.locationManager startRangingBeaconsInRegion:self.beaconRegion]; } You need to replace self.beaconRegion by self.scannerRegion to make it work :) – decades Mar 28 '14 at 20:25
  • 2
    I'm not sure how this response even comes close to answering the question. The question was: "Can I advertise myself as a beacon and a BLE peripheral with services?". This response discusses advertising as a beacon and trying to detect beacons. – Michael McGuire Apr 24 '14 at 20:13
0

In my practice, iBeacon & BLE service can not advertise at same time.

BLE service can not advertise in foreground if advertise mixed with iBeacon. iBeacon can not advertise in background.

iBeacon & BLE service can advertise one by one, e.g. iBeacon advertise 0.5 seconds, then BLE service advertise 30.0 seconds.

Hope this will helpful

ET Worker
  • 275
  • 1
  • 3
  • 11