title: IOS Bluetooth Low Energy Notifications - Objective-C Code Works with Some Devices and Not Others
body: <p>I've put together some pretty basic iOS Objective-C code for Bluetooth BLE intended to connect and get notifications from a custom peripheral device. The custom device updates the characteristic value 1x per second.  </p>
  
  <p>The callback method does not (seem to) fire when the characteristic value is updated:</p>
  
  <pre><code>- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
  </code></pre>
  
  <p>This is in spite of the fact that the characteristic has  setNotifyValue:YES per below code fragment:</p>
  
  <pre><code>if([aChar.UUID isEqual:[CBUUID UUIDWithString:MW_DEVICE_BREATH_DATA4_CHAR_UUID]])
      [peripheral setNotifyValue:YES forCharacteristic:aChar];
  </code></pre>
  
  <p>The custom peripheral device works perfectly fine on LightBlue on the Mac as well as LightBlue Explorer on the iPhone; notifications are received and the correct value updates are displayed.</p>
  
  <p>Furthermore, and most confusingly, the same exact code works fine for a Heart Rate Monitor peripheral when just the service and characteristic UUIDs are changed.  The callback gets fired and the characteristic value is updated.</p>
  
  <p>This question seems to very similar to : </p>
  
  <p>1) <a href="https://stackoverflow.com/questions/34322746/ios-bluetooth-le-cant-get-notification-programatically-but-can-in-other-apps">iOS Bluetooth LE cant get notification programatically but can in other apps</a>
  2) <a href="https://stackoverflow.com/questions/29341435/bluetooth-le-notifications-not-received-on-ios">Bluetooth LE notifications not received on iOS</a>
  3) <a href="https://stackoverflow.com/questions/11858096/how-to-get-notification-from-bluetooth-le-devices-in-ios-app">How to get notification from Bluetooth LE devices in iOS app</a></p>
  
  <p>Per question 1) above, I do have the delegate set.  There weren't answers for 2) or 3) that seemed relevant.</p>
  
  <p>I admit that I'm a bit confused about the persistence of "peripheral".  I've created a property mwDvcPeripheral and used explicit references to it in the various function parameters; it doesn't seem to have an effect positive or negative.</p>
  
  <p>Is this a subtle timing issue involving 128bit UUIDs? What is LightBlue doing that I am not?</p>
  
  <p>I have a feeling that the answer to this is so obvious that I'll need to kick myself when it is pointed out.</p>
  
  <p>Here is the Objective-C header file:
      //
      //  ViewController.h</p>
  
  <pre><code>#import &lt;UIKit/UIKit.h&gt;
  @import CoreBluetooth;
  
  #define MW_DEVICE_INFO_SVC_UUID @"180a"
  #define MW_MFG_NAME_CHAR_UUID  @"2A29"
  
  // Heart Rate Monitor ***** Use these UUIDs instead of below and everything works ******
  //#define MW_DEVICE_MET_SVC_UUID @"180d"
  //#define MW_DEVICE_BREATH_DATA4_CHAR_UUID @"2a37"
  
  // MetWear Custom Device
  #define MW_DEVICE_MET_SVC_UUID @"11223344-5566-7788-9900-aabbccddeeff"
  #define MW_DEVICE_BREATH_DATA4_CHAR_UUID @"01020304-0506-0708-0900-0a0b0c0d0e0f"
  
  @interface ViewController : UIViewController &lt;CBCentralManagerDelegate, CBPeripheralDelegate&gt;
  
  @property (nonatomic, strong) CBCentralManager *centralManager;
  @property (nonatomic, strong) CBPeripheral *mwDvcPeripheral;
  
  @property (nonatomic, strong) IBOutlet UITextView *deviceInfo;
  @property (nonatomic, strong) IBOutlet UILabel *mwData4Characteristic;
  
  @property (nonatomic, strong) NSString *connected;
  @property (nonatomic, strong) NSString *mfgr;
  @property (nonatomic, strong) NSString *metWearDvcData;
  
  
  -(void)getMWData4Characteristic:(CBCharacteristic *)characteristic;
  -(void)getMfgNameCharacteristic:(CBCharacteristic *)characteristic;
  
  @end
  </code></pre>
  
  <p>Here is the Objective-C code stripped of most non-essentials:
      //
      // ViewController.m</p>
  
  <pre><code>#import "ViewController.h"
  
  @interface ViewController ()
  
  @end
  
  @implementation ViewController
  
  - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.
  
      self.metWearDvcData = nil;
      [self.deviceInfo setText:@""];
      [self.deviceInfo setUserInteractionEnabled:NO];   // keep keyboard from popping up.
      CBCentralManager *centralManager = [[CBCentralManager     alloc]initWithDelegate:self queue:nil];
      self.centralManager = centralManager;
  }
  
  - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
  }
  
  #pragma mark - CBCentralManagerDelegate
  
  // method called whenever a connection has been established.
  -(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
  {
      [peripheral setDelegate:self];
      [peripheral discoverServices:nil];
      self.connected = [NSString stringWithFormat:@"Connected: %@", peripheral.state == CBPeripheralStateConnected ? @"YES" : @"NO"];
      NSLog(@"%@", self.connected);
  }
  
  // CBCentralManagerDelegate - This is called with the CBPeripheral class as its main input parameter.  This contains most of the information there is to know about a BLE peripheral.
  -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
  {
      NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
      if([localName length] &gt; 0 ){
          NSLog(@"Found MetWear Device: %@", localName);
          [self.centralManager stopScan];
          self.mwDvcPeripheral = peripheral;
          peripheral.delegate = self;
          [self.centralManager connectPeripheral:peripheral options:nil];
      }
  }
  
  // method called whenever the device state changes
  -(void)centralManagerDidUpdateState:(CBCentralManager *)central
  {
      if([central state] == CBCentralManagerStatePoweredOff){
      NSLog(@"Core Bluetooth BLE hardware powered off");
      }
      else if([central state] == CBCentralManagerStatePoweredOn){
          NSLog(@"Core Bluetooth BLE hardware powered on and ready");
          NSArray *services = @[[CBUUID UUIDWithString:MW_DEVICE_INFO_SVC_UUID],[CBUUID UUIDWithString:MW_DEVICE_MET_SVC_UUID]];
          [self.centralManager scanForPeripheralsWithServices:services options:nil];
      }
      else if([central state] == CBCentralManagerStateUnauthorized){
          NSLog(@"Core Bluetooth BLE state is unauthorized");
      }
      else if ([central state] == CBCentralManagerStateUnknown){
          NSLog(@"Core Bluetooth BLE state is unknown");
      }
      else if ([central state] == CBCentralManagerStateUnsupported){
          NSLog(@"Core Bluetooth BLE state is unsupported");
      }
  }
  
  #pragma mark - CBPeripheralDelegate 
  
  // CBPeripheralDelegate - Invoked when you discover the peripheral's available services
  -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
  {
      for(CBService *service in peripheral.services){
          NSLog(@"Discovered Service: %@", service.UUID);
          [peripheral discoverCharacteristics:nil forService:service];
      }
  }
  
  // Invoked when you discover characteristics of the spec'd service
  -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
  {
      if([service.UUID isEqual:[CBUUID UUIDWithString:MW_DEVICE_MET_SVC_UUID]]){
          for(CBCharacteristic *aChar in service.characteristics) {
              if([aChar.UUID isEqual:[CBUUID UUIDWithString:MW_DEVICE_BREATH_DATA4_CHAR_UUID]]){
                  [peripheral setNotifyValue:YES forCharacteristic:aChar];
                  NSLog(@"Discovered MetWear Data4 Characteristic: %@", aChar);
                  [self getMWData4Characteristic:aChar];
  
              }
          }
      }
      if([service.UUID isEqual:[CBUUID UUIDWithString:MW_DEVICE_INFO_SVC_UUID]]){
          for(CBCharacteristic *aChar in service.characteristics) {
              [peripheral readValueForCharacteristic:aChar];
              NSLog(@"Discovered MetWear mfgr name Characteristic: %@", aChar);
          }
      }
  }
  
  // Invoked when a spec'd characteristic's state changes
  -(void)peripheral:(CBPeripheral *)peripheral didUpdateStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
  {
      NSLog(@"Peripheral State Changed");
  }
  
  // Invoked when you retrieve a spec'd characteristic's value, or when the peripheral device notifies the app that the characteristic's value has changed
  -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
  {
      if([characteristic.UUID isEqual:[CBUUID UUIDWithString:MW_DEVICE_BREATH_DATA4_CHAR_UUID]]){
          [self getMWData4Characteristic:characteristic];
          NSLog(@"Data4 Characteristic is: %@", characteristic);
      }
      if([characteristic.UUID isEqual:[CBUUID UUIDWithString:MW_MFG_NAME_CHAR_UUID]]){
          [self getMfgNameCharacteristic:characteristic];
          NSLog(@"Mfgr is: %@", characteristic);
      }
  }
  
  // Characteristic helpers
  
  - (void)getMWData4Characteristic:(CBCharacteristic *)characteristic {
       NSLog(@"Data4: %@", [characteristic value]);
  }
  
  - (void)getMfgNameCharacteristic:(CBCharacteristic *)characteristic {
       NSString *mfgrName = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
       self.deviceInfo.text = [NSString stringWithFormat:@"%@\n%@\n", self.connected, mfgrName];
  }
  
  @end
  </code></pre>
  
  <p>Lastly, here is the Console Output associated with the output for the above code:</p>
  
  <pre><code>2016-07-31 21:01:26.042 MetWear[551:99182] Core Bluetooth BLE hardware powered on and ready
  2016-07-31 21:01:26.256 MetWear[551:99182] Found MetWear Device: METWR1
  2016-07-31 21:01:26.421 MetWear[551:99182] Connected: YES
  2016-07-31 21:01:26.488 MetWear[551:99182] Discovered Service: Device Information
  2016-07-31 21:01:26.488 MetWear[551:99182] Discovered Service: 11223344-5566-7788-9900-AABBCCDDEEFF
  2016-07-31 21:01:26.489 MetWear[551:99182] Discovered Service: Battery
  2016-07-31 21:01:26.493 MetWear[551:99182] Discovered MetWear mfgr name Characteristic: &lt;CBCharacteristic: 0x17e39f20, UUID = Serial Number String, properties = 0x2, value = &lt;31323334&gt;, notifying = NO&gt;
  2016-07-31 21:01:26.493 MetWear[551:99182] Discovered MetWear mfgr name Characteristic: &lt;CBCharacteristic: 0x17e3a040, UUID = Hardware Revision String, properties = 0x2, value = &lt;312e30&gt;, notifying = NO&gt;
  2016-07-31 21:01:26.494 MetWear[551:99182] Discovered MetWear mfgr name Characteristic: &lt;CBCharacteristic: 0x17e3a120, UUID = Firmware Revision String, properties = 0x2, value = &lt;312e30&gt;, notifying = NO&gt;
  2016-07-31 21:01:26.494 MetWear[551:99182] Discovered MetWear mfgr name Characteristic: &lt;CBCharacteristic: 0x17e3a160, UUID = Software Revision String, properties = 0x2, value = &lt;312e30&gt;, notifying = NO&gt;
  2016-07-31 21:01:26.494 MetWear[551:99182] Discovered MetWear mfgr name Characteristic: &lt;CBCharacteristic: 0x17e3a1a0, UUID = Manufacturer Name String, properties = 0x2, value = &lt;4a617276 6973204c 616273&gt;, notifying = NO&gt;
  2016-07-31 21:01:26.495 MetWear[551:99182] Discovered MetWear mfgr name Characteristic: &lt;CBCharacteristic: 0x17e3a0c0, UUID = Model Number String, properties = 0x2, value = &lt;4d657457 6561724f 6e65&gt;, notifying = NO&gt;
  2016-07-31 21:01:26.496 MetWear[551:99182] Discovered MetWear Data4 Characteristic: &lt;CBCharacteristic: 0x17d3cee0, UUID = 01020304-0506-0708-0900-0A0B0C0D0E0F, properties = 0x12, value = (null), notifying = NO&gt;
  2016-07-31 21:01:26.496 MetWear[551:99182] Data4: (null)
  2016-07-31 21:01:26.793 MetWear[551:99182] Mfgr is: &lt;CBCharacteristic: 0x17e3a1a0, UUID = Manufacturer Name String, properties = 0x2, value = &lt;4a617276 6973204c 616273&gt;, notifying = NO&gt;
  </code></pre>
  
  <p>
  I have been successful in getting this code working; only with brute force however.  I found that a (long) delay using a "for loop" added after:
      [self.centralManager connectPeripheral:peripheral options:nil];
  was successful.  </p>
  
  <p>I'm a bit confused as to why this is effective and needed in the first place.  I'm supposing that there is a synchronization issue with the call back or a subtle timing issue with the RN4020 BLE module but that is only a guess.  I certainly have some more exploring to do.</p>