I am getting Data from BLE Device by Delegate
(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
But i am not getting full peripheral.name form this method.
My BLE Device name is - d7bce65 3fa1b4bf
But I am getting - d7bce65 (name is truncated by sapce)
Please help.
Advertising data is limited to 31 bytes. Besides the local name, your device is probably sending other information like service UUIDs. When all the info doesn’t fit in the 31 byte payload, the local name gets truncated.
You can learn the basics of advertising data here.
https://www.silabs.com/community/wireless/bluetooth/knowledge-base.entry.html/2017/02/10/bluetooth_advertisin-hGsf
Related
I have a fitness app that can connect to Bluetooth LE heart rate monitors. I've had no problem with it for the last few years. Now I'm updating it for iOS 10, and something strange is happening. If I scan for peripherals like this...
- (id)init {
self.heartRateService = #"180D";
self.heartRateKey = #"2A37";
self.allServices = [NSArray arrayWithObjects:[CBUUID UUIDWithString:self.heartRateService], nil];
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
- (void)startScanning {
if (self.centralManager.state == CBCentralManagerStatePoweredOn) {
[self.centralManager scanForPeripheralsWithServices:self.allServices options:nil];
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
DLog(#"found peripheral: %#", peripheral);
DLog(#"advertisementData: %#", advertisementData);
DLog(#"RSSI: %#", RSSI);
}
...it can't find the heart rate monitor -- the scan starts but didDiscoverPeripheral is never called. If I try competitor app #1, it finds and displays data from the HRM, but if I quit it and return to my app, my app still can't find the HRM. If I try competitor app #2, it finds and displays data from the HRM, and if I quit it and return to my app, then my app connects immediately and works as expected. It seems like competitor app #1 has a different way to find the HRM, while competitor app #2 does something to not only find the HRM, but let my app find it afterwards. Although this scenario seems strange, it is 100% reproducible.
I can make two guesses about what's happening:
1) Competitor app #2 enables some setting on the HRM that allows it to be found. But I don't think this is possible because I think communication with a BTLE accessory is one way.
2) iOS 10 has some kind of privacy setting that is disabled by default, but competitor app #2 enables it, allowing my app to see the HRM afterwards.
Does this make any sense, or does anyone have a theory about what's wrong?
I want to develop an app like BeaconSet and LightBlue which modifies the values of the any kind of beacons. Till now,I have used CBCentralManager and CBPeripheral in app to retrieve all the value of the beacon which i can get through CBCharacteristics of CoreBluetooth framework from beacon. But when I tried to change the values of beacon like UDID, majorID and minorID of beacon through this below method
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;
then the values don't get change and also this peripheral delegate method is not called which is shown below.
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
So, how should I change this values?
Thanks
I'm trying to discover a heart rate monitor with this statement:
[self.centralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:SERVICE_HR]] options:#{#"CBCentralManagerScanOptionAllowDuplicatesKey":#NO} ];
SERVICE_HR is defined to be the heart rate service number.
As I read the documentation the option I gave means to coalesce all discoveries of the same device into a single discovery event. I'm scanning for 2.2 seconds with only one heart rate monitor available and getting 2 or 3 discoveries. Since the HR monitor advertises once per second that makes sense if every advertisement is "discovered" separately. I get the following callback 2 or 3 times:
(void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
I save the data from each callback into a mutable array. Here is the array with two discoveries:
(lldb) po [RCLBTLE sharedBTLE].discoveredPeripherals
<__NSArrayM 0x17404bc70>(
<CBPeripheral: 0x1700f8180, identifier = 1DC9167F-6DB8-4216-5217-B1E8B2F3FB90, name = Polar H7 3F1DE71C, state = disconnected>,
<CBPeripheral: 0x1700f8180, identifier = 1DC9167F-6DB8-4216-5217-B1E8B2F3FB90, name = Polar H7 3F1DE71C, state = disconnected>
)
As you can see the same device was discovered twice but I think it should have been discovered only once. What am I doing wrong? Or what am I misunderstanding?
When specifying the scan use the following code
[self.centralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] options: #{CBCentralManagerScanOptionAllowDuplicatesKey : #NO }];
If you specify the above key as #YES then it will detect the device when ever it is seen.
I'm trying to mesure RSSI indicator on iOS (6 with BLE) from several bluetooth peripheral.
I can get RSSI with scanForPeripheral :
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[_manager scanForPeripheralsWithServices:nil
options:options];
coupled with:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
this works but I have no control on the rate of packets receive and the result seems uncertain.
I've read : https://stackoverflow.com/a/12486927/270209 but my rate is not close to 100ms at all (more 1~2 seconds)
If I'm connected to the device the result with readRSSI seems more reliable.
I'm looking for a way to "stimulate" peripherals for more frequents updates in scan mode or a way to connect to more than one peripheral at a time.
Thanks
Edit : I've also tried to start / stop scan quickly, it seems that at start scan detects more devices and updates are more frequent
I'm sure you've already figured this out but just in case someone comes across this (like I just did) looking for other info, if you're not using other iOS devices with coreLocation you need to call the peripheralDidUpdateRSSI: delegate using [self.myPeripheral readRSSI];.
You can call for RSSI data updates in didUpdateValueForCharacteristic: as often as you like once in the updateValue delegate.
You don't need this in viewDidLoad:
NSDictionary *options = etc... This NSDictionary is created in the didDiscoverPeripheral: delegate.
So the overall flow would be:
Check that you are receiving RSSI in the NSLog where you obtained the RSSI data...
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
if ([localName length] > 0){
NSLog(#"Discovered: %# RSSI: %#", peripheral.name, RSSI);
// your other needs ...
}
Call peripheralDidUpdateRSSI: here since this is where updates will occur continuously if you've set notify value to YES [peripheral setNotifyValue:YES forCharacteristic:characteristic];
- (void) peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
//Call the peripheralDidUpdateRSSI delegate ..
[self.myPeripheral readRSSI];
// do all of your other characteristic value updates here
}
readRSSI will call the delegate where you perform your RSSI updates on your UILabel (or whatever you're using) every time you update the previous characteristics values:
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error;
{
_rssiLabel.text = [self.myPeripheral.RSSI stringValue];
NSLog(#"RSSI Method”);
}
If you don't need characteristic values for your app just run a loop in there with whatever timing you need the RSSI value to refresh.
(Assuming you're on an iOS device:) CoreBluetooth is likely deliberately limiting the rate at which it's active on the antenna. Bluetooth LE, Bluetooth Classic, and Wi-Fi are all on the same antenna on iOS devices, so the radios try to keep unnecessary chatter to a minimum. You could try filing a bug on CoreBluetooth to add an option or way to control the update frequency, but I don't imagine they'll implement it, as their primary design goals are to: 1. Not drain the device's battery unnecessarily and 2. Co-exist with the other antenna's radios. (They've also said in posts on the Apple devforums that, for example, if you want a higher data rate than the iOS BTLE implementation, you should use Bluetooth Classic or something that's been designed for it. BTLE is meant to be low-power first and foremost, and they've taken that to its logical end.)
Indeed, the delay between peripheral.readRSSI() and peripheralDidUpdateRSSI is like a second!
I think the best way is to read the RSSI value on the BLE device itself and send it to the iOS device.
The peripheral advertises based on the firmware of the peripheral. This can be set from as low at 100ms to almost as high as 2 seconds.
When using an iOS device as a Bluetooth Peripheral, I have found no way of changing it.
Here is Apples documentation: Look in section 3.5...
https://developer.apple.com/hardwaredrivers/BluetoothDesignGuidelines.pdf
The Low Energy Bluetooth spec does not say much about whether peripherals can connect to more than one central at a time, but my experience testing tells me that they cannot.
Because my application requires a non-possessive relationship with peripherals (i.e. no connections, which would block others), and needs to constantly update their RSSI values, I am seeking a way to continuously scan for peripherals and capture their RSSI values.
The scanForPeripheralsWithServices method appears to scan for a certain interval and then stops. I believe my best bet is to scan for 3 seconds at a time, stopScan, wait (several seconds) and then reinitiate a scan. Repeat.
Can anyone point to a better way of doing it? For example, configuring a peripheral to connect to more than one Central?
A peripheral cannot connect to more than one central. But if you need to just capture the RSSI then you don't even need connecting. Scanning for devices can retrieve the RSSI using this function:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
As for the previous answer, if you are interested only in RSSI you can simply get it into the delegate method:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
BTW, by default the CBCentralManager will only call this method once. If you need this callback to be called every time the CBCentralManager receives an advertisement packet you need to initiate the scan with the option CBCentralManagerScanOptionAllowDuplicatesKey set to YES:
NSDictionary *scanningOptions = #{CBCentralManagerScanOptionAllowDuplicatesKey: #YES};
[centralManager scanForPeripheralsWithServices:nil options:scanningOptions];
Beware that Apple discourage the usage of this option if not strictly necessary.
See: iOS Developer Library -Best Practices for Interacting with a Remote Peripheral Device
I solved this type of issue with this code, basically just restarting the scanning every time an advertisement is processed. I was facing the same issue where CBCentralManager instance would stop listening to a peripheral.
(Setting CBCentralManagerScanOptionAllowDuplicatesKey to #YES did not fully solve the issue for me.)
Assuming the class implements CBCentralManagerDelegate:
- (id) init {
self.central = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
[self initScan];
}
- (void) initScan {
[self.central stopScan];
[self.central scanForPeripheralsWithServices:nil
options:[NSDictionary dictionaryWithObjectsAndKeys:#NO, CBCentralManagerScanOptionAllowDuplicatesKey, nil]];
}
- (void) centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary*)advertisementData RSSI:(NSNumber*)RSSI {
//
// Do stuff here
//
[self initScan];
}