0

I am working on a test BTLE app. The user can select to start central or peripheral mode. if 2 devices are both active and in range they instantly detect, connect and pass data, but...

If device1 is started in central mode and then placed into background, then device2 is taken out of range and put into peripheral mode, when the peripheral is brought next to the backgrounded central, the central detects the peripheral and gets data.

If device1 is started in peripheral mode and placed in background, then device2 is taken out of range and placed into central mode, and then brought into range it does NOT detect the peripheral. (i waited up to 5 minutes) If i press the "start central" button again, it detects immediately!

the "start central" only does the following:

-(id)init{
    if (self=[super init]) {
        dispatch_queue_t peripheralQueue=dispatch_queue_create("com.Dev.peripheralQueue", 0);
        dispatch_queue_t centralQueue=dispatch_queue_create("com.Dev.centralQueue", 0);

        // Initiate Managers with restore keys for background mode
        self.centralManager=[[CBCentralManager alloc]initWithDelegate:self
                                                                queue:centralQueue
                                                              options:@{CBCentralManagerOptionRestoreIdentifierKey: CM_RESTORE_KEY}];
        self.peripheralManager=[[CBPeripheralManager alloc]initWithDelegate:self
                                                                      queue:peripheralQueue
                                                                    options:@{CBPeripheralManagerOptionRestoreIdentifierKey: PM_RESTORE_KEY}];
        // 1. create The UUIDs
        // 2. create the service and characteristics
//service is alloc/inited and published in did update state
    }
    return self;
}

-(void)switchToCentral
{
    NSLog(@"BTM switchToCentral");
    [self.peripheralManager stopAdvertising];
    [self scan];
    self.isCentral = YES;
}

- (void)scan
{
    [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]
                                                options:@{ CBCentralManagerScanOptionAllowDuplicatesKey:@NO}];
    NSLog(@"CM Scanning started");
}

I added the following shameful hack:

-(void)switchToCentral
{
    NSLog(@"BTM switchToCentral");
    [self stopBroadcasting];
    [self scan];
    self.isCentral = YES;

    dispatch_async(dispatch_get_main_queue(), ^{
        [NSTimer scheduledTimerWithTimeInterval:5
                                         target:self
                                       selector:@selector(centralShouldRescan:)
                                       userInfo:nil
                                        repeats:YES];
    });
}

-(void)centralShouldRescan:(NSTimer*)timer{

    NSLog(@"BTM centralShouldRescan");
    if (self.isCentral) {
        NSLog(@"BTM centralShouldRescan isCentral");
        [self stopBroadcasting];
        [self scan];


    }else{
        NSLog(@"BTM centralShouldRescan isCentral");
        [timer invalidate];
    }
}

and now it works! I am at a loss to understand this.It was my understanding and experience that when CBCentral manager starts to scan, it keeps scanning until stopped, especially if it is in the foreground.

mflac
  • 353
  • 3
  • 16

1 Answers1

1

Have you implemented the centralManagerDidUpdateState delegate method? I suspect that when you disable the peripheral mode, there is some time delay before the centralManager is ready to scan - this is just a guess. You could try something like -

-(void) centralManagerDidUpdateState:(CBCentralManager *)central
{
    if (central.state == CBCentralManagerStatePoweredOn)
    {
       [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey:@NO}];
    }
}

This will ensure the scan is re-initiated if the central manager transitions out of the powered on state.

Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Interesting idea, but i do implement centralManagerDidUpdateState and I have a log statement in it. It gets called on startup but does not get called again. I also added a call to retrieveConnectedPeripheralsWithServices but it doesn't find any peripherals. This situation would be less mysterious if the central had been backgrounded, but it is only the peripheral that has been backgrounded, and it works fine! – mflac Mar 31 '14 at 14:22
  • I just ran a quick test with some code I have been playing with. I put my peripheral into background mode and placed it out of range (it was easier to move the peripheral so the central could remain connected to Xcode, but it should be the same). I then started my central and switched on the scan. When I brought the peripheral into range it discovered and connected straight away. What is the status of the device2 before it is "placed into central mode"? Is it acting as a peripheral? – Paulw11 Apr 01 '14 at 04:45
  • when this happened, the app had just started on an iPhone (7.1)from Xcode and initialized both managers and started detecting but NOT advertising. I've added my init to original code above. – mflac Apr 02 '14 at 00:54
  • The only difference I can see between your code and mine is that you are using separate dispatch queues and you are specifying `CBCentralManagerScanOptionAllowDuplicatesKey:@NO` and I am not – Paulw11 Apr 02 '14 at 10:12