Run iOS 6 device as a BLE peripheral
Asked Answered
F

5

17

As we know, iOS 6 support running devices (iPhone 4s and above, and new iPad) as a BLE peripheral. There is a demo in WWDC 2012 Session 705 called "advanced core bluetooth". I asked for the source code from Apple. They sent me a modified version of source code (BTLE_Transfer_Draft). Then I:

  • Run the app in iPhone 5 (iOS 6) in "Peripheral Mode" and start "Advertising"
  • Run the app in new iPad (iOS 5.1.1) in "Central Mode"

The problem is that the peripheral is never been discovered at all. So I use other testing applications including some downloaded from App Store. All failed to discover peripherals. I think the problem should be in BTLE_Transfer_Draft. Because I'm not sure whether I'm allowed to present the whole source code. So I just show the "peripheral mode" part here:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Start up the CBPeripheralManager
    _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    // Opt out from any other state
    if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
        return;
    }

    // We're in CBPeripheralManagerStatePoweredOn state...
    NSLog(@"self.peripheralManager powered on.");

    // ... so build our service.

    // Start with the CBMutableCharacteristic
    self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]
                                                                  properties:CBCharacteristicPropertyNotify
                                                                       value:nil
                                                                 permissions:CBAttributePermissionsReadable];

    // Then the service
    CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]
                                                                    primary:YES];

    // Add the characteristic to the service
    transferService.characteristics = @[self.transferCharacteristic];

    // And add it to the peripheral manager
    [self.peripheralManager addService:transferService];
}

/** Start advertising
 */
- (IBAction)switchChanged:(id)sender
{
    if (self.advertisingSwitch.on) {
        // All we advertise is our service's UUID
        [self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
    }
    else {
        [self.peripheralManager stopAdvertising];
    }
}

The BLE is in powered on status and the startAdvertising is called. But the BLE central can never discover it.

Post updated:

According to mttrb's suggestion I added "CBAdvertisementDataLocalNameKey" when I startAdvertising. But my service is still can't be discovered by most of the apps including some apps from app store. The only one app can discover my service is an app from app store called "BLE scanner".

My question is: does this mean my application is working as a peripheral? But why my own code can't discover the service? How am I supposed to debug it ?

My code in Central Mode is like this:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Start up the CBCentralManager
    _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    if (central.state != CBCentralManagerStatePoweredOn) {
        return;
    }
    [self.centralManager scanForPeripheralsWithServices:nil options:nil];
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
......
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error) {
        NSLog(@"Error discovering services: %@", [error localizedDescription]);
        return;
    }
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    // Deal with errors (if any)
    if (error) {
        NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
        return;
    }
}

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"Peripheral Disconnected");
    self.discoveredPeripheral = nil;
}

The didDiscoverPeripheral and didDiscoverServices are never called. What could be wrong? Any idea? Thanks

Fiorenza answered 24/10, 2012 at 1:56 Comment(8)
Check this development kit.its having client and server example with code.Download the kit and check. LinkAgan
Can you check the property isAdvertising to see if you are actually advertising when you expect or did you ever the peripheralManagerDidStartAdvertising callback on the delegate?Variolite
I add peripheralManagerDidStartAdvertising method. And I check isAdvertising in this method. It returns YES. That means my app is advertising correctly?Fiorenza
I change my code to scan the Peripherals like this: [self.centralManager scanForPeripheralsWithServices:nil options:nil]; Are there anyway to indicate that my scanning succeed?Fiorenza
The - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI never been called. I don't know it's because of Peripheral side problem or the Central side problem now.Fiorenza
Have you tried the app lightblue? It can act as both a central and a peripheral. You can try to use your central to discover it or use its central to discover your peripheral. Just another data point to try to help isolating whether it's a central/peripheral issue or both.Variolite
I tried lightblue. Unfortunately it doesn't work well. I can never discover it in my app if I run lightblue as a peripheral (my application can discover BLE Thermometers. And it can never discover me if my app run as a peripheral. I don't know how to use it properly.Fiorenza
@bagusflyer Ever come up with a solution for this? I'm running into the same issues now.Hebrew
B
12

There is also a high quality free app called LightBlue that you can use to test your code with. It should be able to pick up all devices advertising in peripheral mode and it can even turn itself into an advertising peripheral if you want to make sure your device is working properly.

Began answered 6/11, 2012 at 23:37 Comment(2)
I used LightBlue as a periphral and my app did find it! Thanks!Tape
Does LightBlue works in Background mode? If not, have you any other suggestion to test BLE in Background mode?Alsatia
U
4

I would try moving the startAdvertising: method call up and into the end of your peripheralManagerDidUpdateState: delegate method and see if that helps.

I would also add a CBAdvertisementDataLocalNameKey key-value pair to your startAdvertising: method call. I found things were unreliable when the advertisement didn't have a name.

Finally, I would invest in the BLExplr app available in the App Store to help with scanning for your peripheral. It removes the assumption that your central is working correctly.

Ultrasonic answered 24/10, 2012 at 4:1 Comment(5)
Are there any free app can do the same thing?Fiorenza
No idea. Three dollars seemed quite reasonable to me.Ultrasonic
I find one called BLE Scanner which is free and the only one can discover me.Fiorenza
Interesting! Sounds like you are getting somewhere :)Ultrasonic
At least I got one app can detect my service. But I still need to figure it out why my own code can't detect the service. Once I'm able to do that. I'll post a complete source code here.Fiorenza
W
3

This Git hub project also throws some light on the CBPeripheralManager API. Called PeripheralModeTest. This line is particularly useful for setting the advertising data

 NSDictionary *advertisingData = @{CBAdvertisementDataLocalNameKey : @"Device Name", CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:CBUUIDGenericAccessProfileString]]};

Though I can't see any official documentation in the Apple iOS Developer Library yet. More specifically anything about setting the repeat period for the advertising.

Whatley answered 8/1, 2013 at 10:43 Comment(0)
G
2

The BTLE Transfer example has this piece of (odd) code, which may cause some troubles:

- (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 < -35) {
        return;
    }

Just remove those two if-statements as it makes no sense!

I have made a simplified version available here that can be used to test high volumes of messages being sent from peripheral to central.

Please note that the CBPeripheralManager class was first introduced in iOS 6.0.

Goodson answered 12/2, 2014 at 9:57 Comment(0)
C
0

I do not know witch version of BTLE Central Peripheral Transfer you did actually test but current version has iOS 6 as requirement.

So I would suggest to test linkage against iOS 5.1 to see what compatibility issues it shows.

Catenate answered 26/6, 2013 at 13:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.