I am trying to write a BTLE monitoring app that receives notifications. I have built both the central app and a mock peripheral app using Core Bluetooth (on iOS 7.1). My central can connect fine to the peripheral, which advertises a service that has 2 CBCharacteristicPropertyNotify characteristics. But here is what happens once the central discovers the characteristics (removing non-essential code for clarity).
PD = CBPeripheralDelegate in central
PMD = CBPeripheralManagerDelegate in peripheral
PD callback invoked
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
for (CBCharacteristic *characteristic in service.characteristics)
{
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
PMD callback invoked twice for the 2 characteristics
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:FILTER_GENUINE_CHARACTERISTIC_UUID]])
{
BOOL genuineSent = [self.peripheralManager updateValue:genuineValue
forCharacteristic:self.filterGenuineCharacteristic
onSubscribedCentrals:nil];
}
else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:FILTER_LOADING_CHARACTERISTIC_UUID]])
{
BOOL loadingSent = [self.peripheralManager updateValue:loadingValue
forCharacteristic:self.filterLoadingCharacteristic
onSubscribedCentrals:nil];
}
}
Note: The first time this method is invoked the return from updateValue:forCharacteristic:onSubscribedCentrals: is YES. The second time the return is always NO.
PD callback invoked twice for the 2 characteristics
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
Note: characteristic.isNotifying is always YES.
But that¹s it, no other callback methods are invoked!
PMD callback peripheralManagerIsReadyToUpdateSubscribers: is never called for the updateValue:forCharacteristic:onSubscribedCentrals: call that returned NO
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral
PD callback peripheral:didUpdateValueForCharacteristic:error: is never called for the updateValue:forCharacteristic:onSubscribedCentrals: call that returned YES
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
I have been testing these apps on an iPad Air and iPhone 5s, both running iOS 7.1.
Any idea why the notification is never invoked?
Related
I have a BLEPeripheral that holds about thirty CBCHaracteristics. I'm building an app that interfaces with a custom BLE device. When the view loads I need to write, read, and subscribe to Characteristics on that device. The problem I'm having is I'm not sure when all my Characteristics have been assigned to the data model and if I try to load my view before thats finished my app will crash. I've confirmed this by adding a count integer and incrementing the count after it discovers each characteristic, then when my count int == 30, I load the UI. This feel extremely clunky and I feel like theres a better way to do this.
I'm trigger the loading of the data on the UI view with an observer notification. To give a little more information, I'm using a splitview, user selects the ble object that want to connect to. Once it's connected it hides the splitview and shows the detailview.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
if(_itemCount == 30) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"STOPINDICATOR" object:self];
}
// Serial Number
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:SERIAL_NUMBER]]) {
_serialNumber = characteristic;
_itemCount++;
}
// MFG Name
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:MFG_NAME]]) {
_mfgName = characteristic;
_itemCount++;
}
// Device Type
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:DEVICE_TYPE]]) {
_deviceType = characteristic;
_itemCount++;
}
// Model Number
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:MODEL_NUMBER]]) {
_modelNumber = characteristic;
_itemCount++;
}
}
}
Currently i am using the following watchkit delegates.. I need to check whether they are being called or not while using simulators.
-(void)session:(WCSession *)session didReceiveApplicationContext:(NSDictionary<NSString *,id> *)applicationContext
{
NSLog(#"REminders Array %#",applicationContext);
}
- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *,id> *)userInfo{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Received userInfo Transferr");
NSLog(#"%#", userInfo);
});
}
but they are not getting called while using simulators it is working fine in iphone applications. Any idea how to work with these??
mmm your question is not very clear, as the steps to test your capabilities? in general these delegates are launched following a submission by
- (BOOL)updateApplicationContext:(NSDictionary<NSString *,id> *)applicationContext error:(NSError * _Nullable *)error
and
- (WCSessionUserInfoTransfer *)transferCurrentComplicationUserInfo:(NSDictionary<NSString *,id> *)userInfo
Then if you're working with the simulator you have to set debugging to make sure that it can debug both applications (again if it's your case).
I'm trying to use the CoreBluetooth module to list all the detected Bluetooth devices in a command-line OSX application.
What I have looks like this, so far:
#import CoreBluetooth;
#interface MyCentralManager : NSObject<CBCentralManagerDelegate>
- (void) centralManagerDidUpdateState: (CBCentralManager *) central;
- (void) centralManager:(CBCentralManager *) central
didDiscoverPeripheral:(CBPeripheral *) peripheral
advertisementData:(NSDictionary *) advertisementData
RSSI:(NSNumber *)RSSI;
#end
#implementation MyCentralManager
- (void) centralManagerDidUpdateState: (CBCentralManager *) central
{
NSLog(#"State changed...");
}
- (void) centralManager:(CBCentralManager *) central
didDiscoverPeripheral:(CBPeripheral *) peripheral
advertisementData:(NSDictionary *) advertisementData
RSSI:(NSNumber *)RSSI
{
NSLog(#"Discovered %#", peripheral.name);
}
#end
int main() {
MyCentralManager* myCentralManager = [[MyCentralManager alloc] init];
CBCentralManager* cbCentralManager = [[CBCentralManager alloc] initWithDelegate:myCentralManager queue:nil options:nil];
NSLog(#"Scanning devices now !");
[cbCentralManager scanForPeripheralsWithServices:nil options:nil];
sleep(5); // Wait 5 seconds before stopping the scan.
[cbCentralManager stopScan];
NSLog(#"Scanning devices ended.");
return 0;
}
Now this doesn't work at all as I never get any "State changed..." nor "Discovered ..." log output.
I never actually written any Objective C application before so I'm probably missing the obvious. If I had to guess what I'm doing wrong I would assume that:
I actually have to wait for the CentralManager to be in the appropriate state before starting the scan.
I never actually get inside the state changed delegate method so I assume that my first mistake is: instead of just sleep()'ing, I have to run an event loop of some sort so that the underlying system has a chance to notify me of the state change.
I'm basically stuck at this point: I don't have a GUI, nor do I want one but couldn't figure out a way to run an event loop (assuming that's actually what is missing). How can I do that ?
As I said, this is actually my first attempt with Objective C, so don't be afraid to state the obvious.
Simply run the thread's run loop
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
More information here.
BTW: You do not have to declare the methods already declared in the protocol.
I have multiple BLE devices connected each 'notifying' when their battery state changes.
How can Identify which BLE is notifying when data is read after being notified of a change in state?
// Instance method to get the battery state
- (void) getBattery:(CBCharacteristic *)characteristic
{
//----------------
NSLog(#"getBattery");
//----------------
{
// Get the Battery Data
NSLog(#"Battery Level is : %# (HEX)" , characteristic.value);
}
}
This is the code reading the data when notified to...
Thanks for your answer, but it appears I'm only receiving data from the last service setup to notify from. Here is the calling code for Notify...
Hi, That's great. I can identify the device notifying when data has changed.
But, it appears that my code is only notifying and collecting data from the last characteristic identified.
Here is the calling code:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
// Retrieve the characteristic value for battery
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:SR1_DEVICE_BATTERY_LEVEL_SERVICE_UUID]]) {
[self getBattery:characteristic];
}
}
When a peripheral sends a notification that a value has changed, the CBPeripheralDelegate method didUpdateValueForCharacteristic is invoked. This method includes a reference to the CBPeripheral. You don't show how you get from this method to your getBattery method, but you need pass the peripheral when you do.
You can also reference the peripheral property of the service property of the CBCharacteristic -
CBPeripheral *p=characteristic.service.peripheral;
I've two problems.
What can i do if i don't find a service at the first try?
Is this function called anyway:
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error{
if(error != nil){
[connectedDevice discoverServices:nil];
Is it more reliable to search for a service with UUID?
If i lost the connection is there any method called? Do i need to disconnect and reconnect again or only search for service and characteristics again?
Regards