22

I am trying to read data from a bluetooth device (BR-LE4.0-S2). I was able to connect BLE device, but not able to read data from it.I don't have any specification about BLE services and it's characteristics. Here what my issue is - (void)peripheral:didUpdateValueForCharacteristic:error: not getting called. I followed tutorial "https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonCentralRoleTasks/PerformingCommonCentralRoleTasks.html#//apple_ref/doc/uid/TP40013257-CH3-SW2" .Following is my code.

What my requirement is to read data continuously from BLE device. Any help is greatly appreciated.

- (void)viewDidLoad
{
    self.myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    self.peripheral = [[CBPeripheral alloc] init];
    self.peripheral.delegate = self;
    [super viewDidLoad];
}

- (void) centralManagerDidUpdateState:(CBCentralManager *)central {

    switch (central.state) {
        case CBCentralManagerStatePoweredOn:
        [self.myCentralManager scanForPeripheralsWithServices:nil options:nil];
            break;
        default:
            NSLog(@"Central Manager did change state");
            break;
    }

}

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

    NSLog(@"Discovered %@", peripheral.name);
    [self.myCentralManager stopScan];
    NSLog(@"Scanning stopped");

    if (self.peripheral != peripheral) {
        self.peripheral = peripheral;
        NSLog(@"Connecting to peripheral %@", peripheral);
        // Connects to the discovered peripheral
    [self.myCentralManager connectPeripheral:peripheral options:nil];
    }
}

- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {

    NSLog(@"Peripheral connected");

    NSLog(@"Peripheral services : %@",peripheral.services );

    [self.peripheral setDelegate:self];

    [peripheral discoverServices:nil];

}
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {

    if (error) {
        NSLog(@"Error discovering service: %@", [error localizedDescription]);
        return;
    }

    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:nil forService:nil];
    }
}

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
             error:(NSError *)error {

    int i = 0;
    for (CBCharacteristic *characteristic in service.characteristics) {

[peripheral setNotifyValue:YES forCharacteristic: characteristic];

    }
}

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {

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

    NSLog(@"Value %@",value);
    NSString *stringFromData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSLog(@"Data ====== %@", stringFromData);
}


- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {

    if (error) {
        NSLog(@"Error changing notification state: %@",
              [error localizedDescription]);
    }
    NSString *value = [[NSString alloc] initWithData:self.interestingCharacteristic.value encoding:NSUTF8StringEncoding];

    NSLog(@"Value %@",value);

    NSLog(@"description: %@, descriptors: %@, properties: %d, service :%@, value:%@", characteristic.description, characteristic.descriptors, characteristic.properties, characteristic.service, characteristic.value);
    NSData *data = characteristic.value;

    if (characteristic.isNotifying) {
        NSString *stringFromData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        [peripheral readValueForCharacteristic:characteristic];

        NSLog(@"Data ====== %@", @"ccdc");

    } else {
        [self.myCentralManager cancelPeripheralConnection:peripheral];

    }

}

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

    // We're disconnected, so start scanning again
    NSDictionary *scanOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];

    [self.myCentralManager scanForPeripheralsWithServices:nil options:scanOptions];
}
Meet Doshi
  • 3,983
  • 10
  • 35
  • 76
Ab'initio
  • 5,128
  • 4
  • 25
  • 40
  • Can you please give the details/errors getting on the call back method - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error – itZme Nov 12 '13 at 12:49
  • I am not getting any error in didDiscoverCharacteristicsForService. but for some characteristics I am getting error like "Error changing notification state: The specified UUID is not allowed for this operation" in didUpdateNotificationStateForCharacteristic. I think it is due to subscribed characteristic does not support the specified operation. – Ab'initio Nov 12 '13 at 13:02
  • Try this: https://github.com/yuanda/YmsCoreBluetooth#read-a-characteristic "A block-based framework for building Bluetooth 4.0 Low Energy (aka Smart or LE) iOS and OS X applications using the CoreBluetooth API. " – Tony May 01 '14 at 04:07

4 Answers4

76

To read a value from a BLE peripheral device, follow these steps

  1. Scan for avilable devices

    NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
    [self.myCentralManager scanForPeripheralsWithServices:nil options:options];`
    
  2. On detecting a device, will get a call back to "didDiscoverPeripheral" delegate method. Then establish a connection with detected BLE device

    -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    
        //Connect detected device....
        if (!peripheral.isConnected) {
            peripheral.delegate = self;
            [bluetoothManager_ connectPeripheral:peripheral options:nil];
    
        }
    }
    
  3. On successful connection, request for all the services available in the BLE device

    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"Peripheral Connected");
    
        // Make sure we get the discovery callbacks
        peripheral.delegate = self;
    
        // Search only for services that match our UUID
        [peripheral discoverServices:nil];
    }
    
  4. Request all the characteristics available in each services

    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    
        if (error) {
            NSLog(@"Error discovering services: %@", [error localizedDescription]);
            return;
        }
        // Loop through the newly filled peripheral.services array, just in case there's more than one.
        for (CBService *service in peripheral.services) {
            [peripheral discoverCharacteristics:nil forService:service];
        }
    }
    
  5. Once we get the required characteristics detail, we need to subscribe to it, which lets the peripheral know we want the data it contains

    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    
        // Deal with errors (if any)
        if (error) {
            NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
            return;
        }
    
        // Again, we loop through the array, just in case.
        for (CBCharacteristic *characteristic in service.characteristics) {
            if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:REQUIRED_CHARA_ID]]) {
                // If it is, subscribe to it
                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            }
        }
    }
    
  6. Completing all these steps, BLE device will let you know the notification status change through delegate method

    - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    
        if (error) {
            NSLog(@"Error changing notification state: %@", error.localizedDescription);
        }
    
        // Notification has started
        if (characteristic.isNotifying) {
            NSLog(@"Notification began on %@", characteristic);
        }
    }
    

You will recieve any notification from BLE device in the following method

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"Error reading characteristics: %@", [error localizedDescription]);
        return;
    }

    if (characteristic.value != nil) {
          //value here.        
    }
}
Bouke
  • 10,025
  • 6
  • 49
  • 88
itZme
  • 1,421
  • 9
  • 14
  • 5
    Thanks a lot for your and: I subscribed for a characteristic using "setNotifyValue:YES forCharacteristic:characteristic".And delegate method "didUpdateNotificationStateForCharacteristic"got called.But delegate method "didUpdateValueForCharacteristic"is not called.What my understanding about subscribing a characteristic using "setNotifyValue: forCharacteristic"is that when the value changes for the subscribed characteristic the delegate method "didUpdateValueForCharacteristic" will get called.That is whenever subscribed characteristic value changes the delegate method will be called.Am I right? – Ab'initio Nov 13 '13 at 03:37
  • 3
    I wish I could vote this answer up more than once; it is an excellent example of a complete and thorough answer to a complex problem. Thanks! – Olie Jul 15 '14 at 00:28
  • Can we read ble characteristics event when app is closed in iOS ? – DAMM108 Apr 02 '15 at 07:01
  • I'm dealing with a real device but I don't know its CHARACTERISTIC UUID and I can't seem to find a way to log it. How can I deal with it? – Jongers Mar 21 '16 at 08:12
  • Hi... i am working on bluethooth and i am done exactly what u did and my didUpdateValueForCharacteristic method also called but i am getting : value... here i am writing "81" value on characteristic but in response i am getting .. i dont know what its mean... my question is how to i get value which i have written.. please help me in that... thaks in advance.. :@anny – Anny – Anny Apr 04 '16 at 11:57
  • @Anny have you got the solution ? because i am also getting data as kCBAdvDataManufacturerData = ; .. so how to find values from it ? do you have any solution ? – Moxarth Aug 22 '17 at 09:32
  • @DAMM108 Did you got any solution for your asked question. – Zalak Patel Oct 24 '17 at 11:51
7

Swift version of itZme's answer with a little modification due to didConnectToPeripheral not being called (you also need to keep a strong reference to the peripherals in order to connect, as follows):

Scan for available devices:

centralManager.scanForPeripheralsWithServices(nil, options: nil)

On detecting a device, will get a call back to "didDiscoverPeripheral" delegate method. Then establish a connection with detected BLE device. But also keep a strong reference of the peripherals first:

private var peripherals: [CBPeripheral] = []

func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {

  if peripheral.state != .connected {
    self.peripherals.append(peripheral)
    peripheral.delegate = self
    centralManager.connectPeripheral(peripheral , options: nil)
  }
}

And the rest should be like this:

extension ViewController: CBPeripheralDelegate {

func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {

  if error != nil {
    print("Error connecting to peripheral: \(error?.localizedDescription)")
    return
  }
}

func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("Peripheral connected.")

  peripheral.delegate = self
  peripheral.discoverServices(nil)
}

func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {

  if error != nil {
    print("Error discovering services \(error?.localizedDescription)")
    return
  }

  for service: CBService in peripheral.services! {
    peripheral.discoverCharacteristics(nil, forService: service)
  }
}

func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {

  if error != nil {
    print("Error discovering characteristics \(error?.localizedDescription)")
    return
  }

  for characteristic: CBCharacteristic in service.characteristics! {
    if characteristic.UUID == CBUUID(string: YOUR_CHARACTERISTIC_UUID) {
      peripheral.readValueForCharacteristic(characteristic)
      // for some devices, you can skip readValue() and print the value here
    }
  }
}

func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {

   if characteristic.UUID == CBUUID(string: YOUR_CHARACTERISTIC_UUID) {
      print(characteristic.value)
    }       
}

}
Teodor Ciuraru
  • 2,949
  • 1
  • 27
  • 33
  • Hi... i am working on bluethooth and i am done exactly what u did and my didUpdateValueForCharacteristic method also called but i am getting : value... here i am writing "81" value on characteristic but in response i am getting .. i dont know what its mean... my question is how to i get value which i have written.. please help me in that... thaks in advance.. :@anny – Anny Apr 04 '16 at 11:55
0
  func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

    for newChar: CBCharacteristic in service.characteristics!{

        peripheral.readValue(for: newChar)

        if newChar.properties.rawValue == 0x10 || newChar.properties.rawValue == 0x8C{

            peripheral.setNotifyValue(true, for: newChar)
        }
        else if newChar.properties.rawValue == 0x12{

            peripheral.setNotifyValue(true, for: newChar)
        }
     }


 func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

    print(characteristic)
 }
Deepak Tagadiya
  • 1,821
  • 11
  • 27
-1

First of all you must execute read() function.If you execute read () function then "didUpdateValueForCharesteristics" will run.You can read string value in this function.This is simple.