1

Edited: Removed iBeacon code. Since there is not issue with iBeacon code.

-> Once we recognize our beacons, we have to communicate with the first beacon using BLE. We are using Bleno framework on Respberry PI.

//Passing my service UUID

[bluetoothManager scanForPeripheralsWithServices:@[@"87fa7df2-748c-4093-8e73-b93ee73543b4"] options:scanOptions];

Issue is, If we pass my service UUID (validated with Light Blue App), I am not getting any results. Instead, If we pass "nil", I am at least able to recognize my device.

So, Is there any generic way to recognize our Bluetooth device from iOS Code. Since CBPeripheral is returning limited properties.

<CBPeripheral: 0x1700ff700, identifier = 7915DCF8-AF2E-4AF2-B5FC-A5EEA10D3812, name = raspberrypi, state = connected>

I tried to change my bluetooth device name using below command using BLENO framework.

sudo BLENO_DEVICE_NAME="SOME_NAME_86" node iBeacon.js

Sometimes, I am able to read the name. But not all the times. So I am looking for alternative solution.

Here is my code:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.    
    _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    _data = [[NSMutableData alloc] init];
}


- (void)centralManagerDidUpdateState:(CBCentralManager *)central {    
    switch (central.state) {            
        case CBManagerStatePoweredOn:
[_centralManager scanForPeripheralsWithServices:@[[CBUUID 
UUIDWithString:@"87FA7DF2-748C-4093-8E73-B93EE73543B4"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}];                break;            
        default:            break;
    }
}


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

    if (_discoveredPeripheral != peripheral) {
        _discoveredPeripheral = peripheral;
        [_centralManager connectPeripheral:peripheral options:nil];
        [_centralManager stopScan];
    }
}

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"Failed to connect");
    [self cleanup];
}

- (void)cleanup {

    // See if we are subscribed to a characteristic on the peripheral
    if (_discoveredPeripheral.services != nil) {
        for (CBService *service in _discoveredPeripheral.services) {
            if (service.characteristics != nil) {
                for (CBCharacteristic *characteristic in service.characteristics) {
                    if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
                        if (characteristic.isNotifying) {
                            [_discoveredPeripheral setNotifyValue:NO forCharacteristic:characteristic];
                            return;
                        }
                    }
                }
            }
        }
    }

    [_centralManager cancelPeripheralConnection:_discoveredPeripheral];
}


- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@"Connected");

    [_centralManager stopScan];
    NSLog(@"Scanning stopped");

    [_data setLength:0];

    peripheral.delegate = self;    
    [peripheral discoverServices:@[[CBUUID UUIDWithString:@"87FA7DF2-748C-4093-8E73-B93EE73543B4"]]];
}


- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    if (error) {
        [self cleanup];
        return;
    }

    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]] forService:service];
    }
    // Discover other characteristics
}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"Error");
        return;
    }

    NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];

    if ([stringFromData isEqualToString:@"EOM"]) {        
        [peripheral setNotifyValue:NO forCharacteristic:characteristic];        
        [_centralManager cancelPeripheralConnection:peripheral];
    }

    [_data appendData:characteristic.value];
}

@end

Problem is, I have two Bluetooth devices side by side, advertising same service ID. Based on scenario, I should connect to only one device & read their characteristics.

Praveen
  • 126
  • 1
  • 6
  • You cannot associate a beacon UUID with a BLE peripheral. You need to use CoreBluetooth to discover your advertising peripherals and connect to them that way. The identifier that CoreBluetooth presents to your app is *related to* the device MAC, but *is not* the device MAC. The Beacon UUID is not related to the MAC or the CoreBluetooth identifier – Paulw11 Feb 28 '17 at 22:23
  • Thanks Paul for the comment. I gave a try with iBeacnon UUID too. Anyhow this is the right one. In the same code, I have also added my service UUID (Unique to my device) in the scanPeripheral method. But It did not find my blue tooth devices with that service UUID. I was not able to proceed without recognizing my blue tooth device. – Praveen Mar 01 '17 at 18:38
  • Once you detect a device advertising your service, you need to connect to and read the value of some characteristic to identify it. You can't identify a specific device from the service advertisement alone – Paulw11 Mar 01 '17 at 19:48
  • I tried to connect to my device service using Service UUID in scanForPeripherals method. [bluetoothManager scanForPeripheralsWithServices:@[87FA7DF2-748C-4093-8E73-B93EE7354666] options:nil]; Since my device is active, still not able to find the bluetooth. – Praveen Mar 01 '17 at 23:38
  • Is your device advertising that service? Start with the LightBlue app from the app store and make sure it can see your device and that service. – Paulw11 Mar 02 '17 at 00:11
  • Yes... I am able to see the service UUID in the light blue app. But If give the same service UUID in my code, it is not working. Not sure..something went wrong. – Praveen Mar 02 '17 at 00:19
  • Can you edit your question to show your code? – Paulw11 Mar 02 '17 at 00:39
  • Yes...Edited the question with sample code. – Praveen Mar 02 '17 at 01:07

3 Answers3

0

If you are trying to detect and communicate with an iBeacon from iOS, you most likely want to use Core Location, not Core Bluetooth...

Take a look here: https://developer.apple.com/reference/corelocation?language=objc

Edit: I haven't run this code, but after a quick browse of the code, it looks like a decent example of scanning for and detecting iBeacons: https://github.com/ThomasJay/Locate

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Thanks DonMag for the comment. But I am able to recognize my iBeacon using unique identifier. I am using Core Location for this. Issue is, Once we recognize the Raspberry PI (Bluetooth device), I was trying to connect using Core Bluetooth framework. I am using ScanPeripheral method to scan the nearby bluetooth devices again. In this step, I am not able to recognize my blue tooth device using my unique Service UUID. – Praveen Mar 01 '17 at 18:45
  • Ah - makes sense. I take it you have multiple devices in the area? So you are using iBeacon to maybe "detect the closest one" and then you need to connect to it? – DonMag Mar 01 '17 at 19:36
  • Yes... Thats correct... In Android we are able to connect to the bluetooth peripheral by passing service UUID. But in iOS, it is not working. – Praveen Mar 01 '17 at 21:30
  • Hmmm... Do you have control of the environment / BLE device configurations? If so, can you give each one its own Service UUID? Or, can you use the BLE RSSI values to determine proximity? – DonMag Mar 01 '17 at 21:40
  • Yes. I have control over BLE configurations. For this scenarios, I have tried only on One blue tooth device (Respberry PI) by giving unique service UUID. Here is the method calling code with service UUID. [_centralManager scanForPeripheralsWithServices:@[@"87FA7DF2-748C-4093-8E73-B93EE73543B4"] options:nil]; – Praveen Mar 02 '17 at 00:24
0

The array you send to scanForPeripheralsWithServices is an array of CBUUID objects, not an array of strings. You need:

[_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"87FA7DF2-748C-4093-8E73-B93EE73543B4"]] options:nil];
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • As an aside, problems like this is why you should seriously consider moving to Swift; Swift would have caught this at compile time – Paulw11 Mar 02 '17 at 01:39
  • Yes..I have tried in both ways. [_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"87FA7DF2-748C-4093-8E73-B93EE73543B4"]] options:nil]; – Praveen Mar 02 '17 at 02:01
  • Check your service UUID carefully; Compare it with the services that LightBlue is discovering from the advertisement packet. There is a difference between service available on a peripheral and the service that are advertised. Also try will `nil` instead of the array of CBUUID; that will discover all advertising peripherals and then you can see what service(s) your Pi is advertising – Paulw11 Mar 02 '17 at 04:01
  • I double checked the service UUID in light blue app. Seems like no mistake. I was able to print the service UUID once connected. I am passing "nil" in the ScanForPeripherals method. [_centralManager scanForPeripheralsWithServices:nil options:scanOptions]; Here i am pass, service UUID in the discoverServices method. Sometimes it is working. But not all the times. [_discoveredPeripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]]; – Praveen Mar 02 '17 at 20:03
  • It looks like you haven't configured your peripheral to advertise your service. What service uuid is included in the advertising data; you can check the dictionary in `didDiscoverPeripheral`. – Paulw11 Mar 02 '17 at 20:05
  • If multiple devices are there, some time my code is connecting other devices than my device. Is there any way to stop the scan once we get our device. If Yes, On what basis we can recognize our device. -- Thanks – Praveen Mar 02 '17 at 20:07
  • That is why you need to be able to scan for devices advertising a specific service rather than nil; once you get your device to advertise correctly then you can scan for that service. – Paulw11 Mar 02 '17 at 20:08
  • I tried to scan the peripherals using my service UUID. But my code is not able to find the peripherals. Thats the reason, I passed the nil. After I get some peripherals, I am trying to connect & discover the services over connected peripheral. But is not consistent. Am I missing anything here. [_discoveredPeripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]]; – Praveen Mar 03 '17 at 01:08
0

Stumbled this by accident and this seems stale, but since I was in similar situation (and spent a day or so figuring this out) decided I will answer this for anyone reading this in the future. Most likely you have services running, but you are not broadcasting their availability. That way, your services are only visible after pairing to the device.

To test that, scan with nil value for services and print the contents of the advData array available in centralManager:didDiscoverPeripheral:advertisementData:RSSI:. If your service UUID is not available in that dictionary (under key CBAdvertisementDataServiceUUIDsKey), there's no way for iOS device to know that service is available without pairing.

Raimundas Sakalauskas
  • 2,016
  • 21
  • 24