IOS BLE communication write value; - ios

I am writing my first IOS App, to communicate with the Bluegiga BLE chip. I need to read and write values. The issue I am having is that, I am not able to write the value the first time I run the program. I do not get any error. It shows the last value which was written as the current value. When I re-run the program the correct value is updated correctly. Its delaying my project. Any suggestion will be of great help. I am using the following code snippet.
CBPeripheral *servicePeripheral;
NSData *readIndex;
CBCharacteristic *readIdxCharacteristic;
[servicePeripheral writeValue:readIndex forCharacteristic:readIdxCharacteristic type:CBCharacteristicWriteWithResponse];
- (void) peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if ([characteristic isEqual:readIdxCharacteristic] ){
[peripheral readValueForCharacteristic:readIdxCharacteristic];
NSLog(#"Read Index Characteristic Written Check%#",characteristic.value);
}
}

Your problem is that you are accessing the characteristic.value immediately after the call to readValueForCharacteristic, but it takes some time for Core-Bluetooth to contact the peripheral, request a value for the characteristic and for the peripheral to reply.
If you refer to the documentation for this method you will see -
Discussion
When you call this method to read the value of a
characteristic, the peripheral calls the
peripheral:didUpdateValueForCharacteristic:error: method of its
delegate object. If the value of the characteristic is successfully
retrieved, you can access it through the characteristic’s value
property.
So, you need to access characteristic.value in your didUpdateValueForCharacteristic: CBPeripheral delegate method

Related

Method peripheral: didWriteValueForCharacteristic: error: is not called

I send data over BLE using the following call
[peripheral writeValue:dataPiece forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
when the dataPiece size is 180 bytes or less everything works fine: accepting device receives all the data, on calling device corresponding callback (peripheral: didWriteValueForCharacteristic: error:) is called. When the size goes large (>180) receiving device still gets all the data (in two chunks: of 180 bytes and the rest). However in the latter case the callback on transmitting device is not called.
Also [peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithResponse] returns 512 which is more than 180, so I would expect 200 to work fine.
Am I missing something (obviously yes, but what)?
For peripheral, it should respond descriptor when central call readValueForDescriptor: method.
If it did not respond, central can not write data to it.
See:
(void)readValueForDescriptor:(CBDescriptor *)descriptor;
(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error;

iOS Core bluetooth get all features description from device

How to can I get all available features from my device using Core Bluetooth.
I figured that before get any information we need to observe all devices via low energy bluetooth.
So the first step is scan for all peripheral devices scanForPeripheralsWithServices via CBCentralManager. In the delegate callback:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
we can save peripheral identifier and save peripheral to handle it in future like change some characteristics, switch off/on and etc.
But my question how to get description of this function, for example I got some characteristic, but I don't know how to use. Where to find info about this future.
Let me describe situation. For example I have sound player with some options where I can handle volume of sound via bluetooth.
So I need to get peripheral first, then detect service and then discover characteristic to find volume "property", but how can I find it, how should I understand which is min/max amount of volume where to find this information. For example we can pass 0 as min volume amount and 1 as a max. But it also could be in range from 0 to 1000 or any other. How to detect this information?
You can't be sure to find the "documentation" for each characteristics, like what are the possible values, etc.
If the Services & the Characteristics follow the documentation of Bluetooth, and are per se "documented", it just follow the doc, it's here. In theory, theses "well known" services and characters should have an UUID like "0x0000".
Example: Battery service is 0x180F, Battery Level is 0x2A19 and the possible values have a defined protocol.
For other custom services/characteristics, it's more difficult. Each one has their own documentation, and the UUID is longer (if it's developed respecting the rules).
If set, you can read the CBDescriptor to get more info.
In all case, you have to either refer to the Bluetooth Low Energy Documentation, or if it's custom to the manufacturer. Either wise, it's all about reverse-engeenering.
You can refer this Demo Project here.
With the following snippet you can get all characteristics, services associated with any iOS supported peripheral.
BabyBluetooth *objBluetooth = [BabyBluetooth shareBabyBluetooth];
objBluetooth.scanForPeripherals().begin();
You can set Delegate call-backs in this manner.
-(void)babyDelegate{
//If any peripheral discovered
[objBluetooth setBlockOnDiscoverToPeripherals:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
NSLog(#"搜索到了设备:%#",peripheral.name);
}];
//Set the filtration criteria for the bluetooth peripherals
[objBluetooth setFilterOnDiscoverPeripherals:^BOOL(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI) {
//if ([peripheralName hasPrefix:#"Pxxxx"] ) {
// return YES;
//}
//return NO;
if (peripheralName.length >1) {
return YES;
}
return NO;
}];
//and so on.
}
Once peripheral filtered , you can get it from the array and then use this:
objBluetooth.having(peripheralFromArray).then.connectToPeripherals().discoverServices().discoverCharacteristics().begin();
[_channelForaD40 setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {
//get your characteristics description here
}];
[_channelForaD40 setBlockOnReadValueForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) {
//Read the values of your characteristics here
}];
then, you can finally compare your characteristics with Bluetooth standards.

Reading Bluetooth LE CBCharacteristic returns a smaller value on iOS 7 [duplicate]

I have a characteristic value which contains the data for an image. In the peripheral I setup the value like this:
_photoUUID = [CBUUID UUIDWithString:bPhotoCharacteristicUUID];
_photoCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_photoUUID
properties:CBCharacteristicPropertyRead
value:Nil
permissions:CBAttributePermissionsReadable];
My understanding is that when this value is requested, the didReceiveReadRequest callback will be called:
-(void) peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:_photoUUID]) {
if (request.offset > request.characteristic.value.length) {
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}
else {
// Get the photos
if (request.offset == 0) {
_photoData = [NSKeyedArchiver archivedDataWithRootObject:_myProfile.photosImmutable];
}
request.value = [_photoData subdataWithRange:NSMakeRange(request.offset, request.characteristic.value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}
}
}
This comes pretty much from Apple's documentation. On the Central side in the didDiscoverCharacteristic callback I have the following code:
if ([characteristic.UUID isEqual:_photoUUID]) {
_photoCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
Which in turn calls the didUpdateValueForCharacteristic callback:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(#"updated value for characteristic");
if ([characteristic.UUID isEqual:_photoUUID]) {
NSArray * photos = [NSKeyedUnarchiver unarchiveObjectWithData:characteristic.value];
}
}
All of the callbacks are called but when I try to re-construct the array, it's corrupted because not all of the data is transferred correctly. I would expect the didRecieveReadRequest callback to be called multiple times with a different offset each time. However it's only called once.
I was wondering if anyone knew what I'm doing wrong?
I'm guessing you're bumping up against the 512 byte limit on characteristic length. You'll need to move to subscriptions to characteristics and processing of updates to get around this:
On the central:
Subscribe to the characteristic by calling -[CBPeripheral setNotifyValue:forCharacteristic] (with YES as the notify value).
In -peripheral:didUpdateValueForCharacteristic:error, every update will either be data to append, or something you choose to use on the peripheral side to indicate end-of-data (I use an empty NSData for this). Update your -peripheral:didUpdateValueForCharacteristic:error code so that:
If you're starting to read a value, initialize a sink for the incoming bytes (e.g. an NSMutableData).
If you're in the middle of reading a value, you append to the sink.
If you see the EOD marker, you consider the transfer complete. You may wish to unsubscribe from the characteristic at this state, by calling -[CBPeripheral setNotifyValue:forCharacteristic] with a notify value of NO.
-peripheral:didUpdateNotificationStateForCharacteristic:error: is a good spot to manage the initialization and later use of the sink into which you read chunks. If characteristic.isNotifying is updated to YES, you have a new subscription; if it's updated to NO then you're done reading. At this point, you can use NSKeyedUnarchiver to unarchive the data.
On the peripheral:
In -[CBMutableCharacteristic initWithType:properties:value:permissions], make sure the properties value includes CBCharacteristicPropertyNotify.
Use -peripheralManager:central:didSubscribeToCharacteristic: to kick off the chunking send of your data, rather than -peripheral:didReceiveReadRequest:result:.
When chunking your data, make sure your chunk size is no larger than central.maximumUpdateValueLength. On iOS7, between an iPad 3 and iPhone 5, I've typically seen 132 bytes. If you're sending to multiple centrals, use the least common value.
You'll want to check the return code of -updateValue:forCharacteristic:onSubscribedCentrals; if underlying queue backs up, this will return NO, and you'll have to wait for a callback on -peripheralManagerIsReadyToUpdateSubscribers: before continuing (this is one of the burrs in an otherwise smooth API, I think). Depending upon how you handle this, you could paint yourself into a corner because:
If you're constructing and sending your chunks on the same queue that the peripheral is using for its operations, AND doing the right thing and checking the return value from -updateValue:forCharacteristic:onSubscribedCentrals:, it's easy to back yourself into a non-obvious deadlock. You'll either want to make sure that you yield the queue after each call to -updateValue:forCharacteristic:onSubscribedCentrals:, perform your chunking loop on a different queue than the peripheral's queue (-updateValue:forCharacteristic:onSubscribedCentrals: will make sure its work is done in the right place). Or you could get fancier; just be mindful of this.
To see this in action, the WWDC 2012 Advanced Core Bluetooth video contains an example (sharing VCards) that covers most of this. It doesn't however, check the return value on the update, so they avoid the pitfalls in #4 altogether.
Hope that helps.
I tried the approach described by Cora Middleton, but couldn't get it to work. If I understand her approach correctly, she would send all partial data through the update notifications. The problem for me seemed to be that there was no guarantee each update would be read by the central if the values in these notifications would change often in short succession.
So because that approach didn't work, I did the following:
There's some characteristic that I use to keep track of the state of the peripheral. This characteristic would only contain some flags and would send out notifications if one or more flags change. Interactions by the user on the peripheral would change the state and there's one action on the peripheral that the user can perform to trigger a download from a connected central.
The data to be downloaded from the central is added to a stack on the peripheral. The last item on the stack is a terminator indicator (an empty NSData object)
The central registers to receive notifications of the aforementioned state characteristic. If some flag is set, a download is triggered.
On the peripheral side, every time I receive a read request for a certain characteristic, I remove 1 item from the stack and return this item.
On the central side I add all data that is returned from the read requests. If the empty data value is retrieved, then I create an object from the returned data (in my case it's a JSON string).
On the peripheral side I also know the download is finished after returning the empty NSData object, so afterwards I can change the state once again for the peripheral.

Reading long characteristic values using CoreBluetooth

I have a characteristic value which contains the data for an image. In the peripheral I setup the value like this:
_photoUUID = [CBUUID UUIDWithString:bPhotoCharacteristicUUID];
_photoCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_photoUUID
properties:CBCharacteristicPropertyRead
value:Nil
permissions:CBAttributePermissionsReadable];
My understanding is that when this value is requested, the didReceiveReadRequest callback will be called:
-(void) peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:_photoUUID]) {
if (request.offset > request.characteristic.value.length) {
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}
else {
// Get the photos
if (request.offset == 0) {
_photoData = [NSKeyedArchiver archivedDataWithRootObject:_myProfile.photosImmutable];
}
request.value = [_photoData subdataWithRange:NSMakeRange(request.offset, request.characteristic.value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}
}
}
This comes pretty much from Apple's documentation. On the Central side in the didDiscoverCharacteristic callback I have the following code:
if ([characteristic.UUID isEqual:_photoUUID]) {
_photoCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
Which in turn calls the didUpdateValueForCharacteristic callback:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(#"updated value for characteristic");
if ([characteristic.UUID isEqual:_photoUUID]) {
NSArray * photos = [NSKeyedUnarchiver unarchiveObjectWithData:characteristic.value];
}
}
All of the callbacks are called but when I try to re-construct the array, it's corrupted because not all of the data is transferred correctly. I would expect the didRecieveReadRequest callback to be called multiple times with a different offset each time. However it's only called once.
I was wondering if anyone knew what I'm doing wrong?
I'm guessing you're bumping up against the 512 byte limit on characteristic length. You'll need to move to subscriptions to characteristics and processing of updates to get around this:
On the central:
Subscribe to the characteristic by calling -[CBPeripheral setNotifyValue:forCharacteristic] (with YES as the notify value).
In -peripheral:didUpdateValueForCharacteristic:error, every update will either be data to append, or something you choose to use on the peripheral side to indicate end-of-data (I use an empty NSData for this). Update your -peripheral:didUpdateValueForCharacteristic:error code so that:
If you're starting to read a value, initialize a sink for the incoming bytes (e.g. an NSMutableData).
If you're in the middle of reading a value, you append to the sink.
If you see the EOD marker, you consider the transfer complete. You may wish to unsubscribe from the characteristic at this state, by calling -[CBPeripheral setNotifyValue:forCharacteristic] with a notify value of NO.
-peripheral:didUpdateNotificationStateForCharacteristic:error: is a good spot to manage the initialization and later use of the sink into which you read chunks. If characteristic.isNotifying is updated to YES, you have a new subscription; if it's updated to NO then you're done reading. At this point, you can use NSKeyedUnarchiver to unarchive the data.
On the peripheral:
In -[CBMutableCharacteristic initWithType:properties:value:permissions], make sure the properties value includes CBCharacteristicPropertyNotify.
Use -peripheralManager:central:didSubscribeToCharacteristic: to kick off the chunking send of your data, rather than -peripheral:didReceiveReadRequest:result:.
When chunking your data, make sure your chunk size is no larger than central.maximumUpdateValueLength. On iOS7, between an iPad 3 and iPhone 5, I've typically seen 132 bytes. If you're sending to multiple centrals, use the least common value.
You'll want to check the return code of -updateValue:forCharacteristic:onSubscribedCentrals; if underlying queue backs up, this will return NO, and you'll have to wait for a callback on -peripheralManagerIsReadyToUpdateSubscribers: before continuing (this is one of the burrs in an otherwise smooth API, I think). Depending upon how you handle this, you could paint yourself into a corner because:
If you're constructing and sending your chunks on the same queue that the peripheral is using for its operations, AND doing the right thing and checking the return value from -updateValue:forCharacteristic:onSubscribedCentrals:, it's easy to back yourself into a non-obvious deadlock. You'll either want to make sure that you yield the queue after each call to -updateValue:forCharacteristic:onSubscribedCentrals:, perform your chunking loop on a different queue than the peripheral's queue (-updateValue:forCharacteristic:onSubscribedCentrals: will make sure its work is done in the right place). Or you could get fancier; just be mindful of this.
To see this in action, the WWDC 2012 Advanced Core Bluetooth video contains an example (sharing VCards) that covers most of this. It doesn't however, check the return value on the update, so they avoid the pitfalls in #4 altogether.
Hope that helps.
I tried the approach described by Cora Middleton, but couldn't get it to work. If I understand her approach correctly, she would send all partial data through the update notifications. The problem for me seemed to be that there was no guarantee each update would be read by the central if the values in these notifications would change often in short succession.
So because that approach didn't work, I did the following:
There's some characteristic that I use to keep track of the state of the peripheral. This characteristic would only contain some flags and would send out notifications if one or more flags change. Interactions by the user on the peripheral would change the state and there's one action on the peripheral that the user can perform to trigger a download from a connected central.
The data to be downloaded from the central is added to a stack on the peripheral. The last item on the stack is a terminator indicator (an empty NSData object)
The central registers to receive notifications of the aforementioned state characteristic. If some flag is set, a download is triggered.
On the peripheral side, every time I receive a read request for a certain characteristic, I remove 1 item from the stack and return this item.
On the central side I add all data that is returned from the read requests. If the empty data value is retrieved, then I create an object from the returned data (in my case it's a JSON string).
On the peripheral side I also know the download is finished after returning the empty NSData object, so afterwards I can change the state once again for the peripheral.

Refreshing RSSI value of many bluetooth peripherals

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

Resources