How to get the characteristic from UUID in objective-C? - ios

I am developing for BLE in objective-C.
I define the UUID like the following code:
static NSString *const LEDStateCharacteristicUUID = #"ffffffff-7777-7uj7-a111-d631d00173f4";
I want to write characteristic to BLE device by following code , it need to pass 3 parameter:1.Data 2.Characteristic 3.type
CBCharacteristic *chara = ??? // how to set the characteristic via above UUID let it can pass to following function?
[peripheral writeValue:data forCharacteristic:chara type:CBCharacteristicWriteWithoutResponse];
How to set the characteristic via above UUID let it can pass to writeCharacteristic function?
Thanks in advance.

First you need to know what service UUID and what characteristic UUID from this service you want. When you have these UUIDs you can use this logic below to get the right characteristic instance:
- (CBCharacteristic *)characteristicWithUUID:(CBUUID *)characteristicUUID forServiceUUID:(CBUUID *)serviceUUID inPeripheral:(CBPeripheral *)peripheral {
CBCharacteristic *returnCharacteristic = nil;
for (CBService *service in peripheral.services) {
if ([service.UUID isEqual:serviceUUID]) {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:characteristicUUID]) {
returnCharacteristic = characteristic;
}
}
}
}
return returnCharacteristic;
}

You need to set a delegate for the peripheral:
peripheral.delegate = self;
In didConnectToPeripheral you discover the peripheral's services. In the peripheral's didDiscoverServices callback you then discover characteristics. In didDiscoverCharacteristics you then loop through each characteristic and save them in a variable.
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error
{
if (error) {
NSLog(#"Error discovering characteristics: %#", error.localizedDescription);
} else {
NSLog(#"Discovered characteristics for %#", peripheral);
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID.UUIDString isEqualToString: LEDStateCharacteristicUUID]) {
// Save a reference to it in a property for use later if you want
_LEDstateCharacteristic = characteristic;
[peripheral writeValue:data forCharacteristic: _LEDstateCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
}
}
}

Related

unable to read the data after write operation using CoreBluetooth

Am new to Objective C Am trying to read data from an arduino peripheral with a specific service and characteristic
there are two characteristics with for two different services
//arduino code
BLEService dataService("938548e6-c655-11ea-87d0-0242ac130003"); //Data reading
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); //LED control service
// Initialize dataService characteristics
BLECharacteristic dataArray("77539407-6493-4b89-985f-baaf4c0f8d86", BLERead | BLENotify, 114); // standard 16-bit characteristic UUID
// Initialize ledService characteristics
BLECharacteristic eventChar("19B10001-E8F2-537E-4F6C-D104768A1213", BLERead | BLEWrite, 1);
I was able to notify and got the read value for the first characteristics for when am trying to write for second characteristic am not receiving data from peripheral
for (CBCharacteristic *characteristic in service. characteristics) {
NSLog(#"Charecteristics %#", characteristic.UUID);
if ([characteristic.UUID.UUIDString containsString: #"77539407"]) {
NSLog(#"Specific Characteristics %#", characteristic.UUID);
//Working........ Characteristic
[peripheral setNotifyValue: YES forCharacteristic: characteristic];
}
if ([characteristic.UUID.UUIDString containsString: #"19B10001"]) {
NSLog(#"Specific Charecteristics %#", characteristic.UUID);
NSData *data = [#"01" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"WRITING DATA");
NSLog(#"%# : dataaaaaa",data);
[peripheral writeValue: data forCharacteristic: characteristic
type: CBCharacteristicWriteWithResponse];
}
}
after writing the data it goes to the didWriteValueForCharacteristic where I get null for the writeCallBack can someone explain me what mistake am doing
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(#"didWrite");
NSString *key = [self keyForPeripheral: peripheral andCharacteristic:characteristic];
NSLog(#" writeCallback : %# ",key);
RCTResponseSenderBlock writeCallback = [writeCallbacks objectForKey:key];
NSLog(#" writeCallback : %# ",writeCallback);
if (writeCallback) {
if (error) {
NSLog(#"%#", error);
[writeCallbacks removeObjectForKey:key];
writeCallback(#[error.localizedDescription]);
} else {
if ([writeQueue count] == 0) {
NSLog(#"NO 1");
[writeCallbacks removeObjectForKey:key];
writeCallback(#[]);
}else{
// Remove and write the queud message
NSLog(#"NO 2");
NSData *message = [writeQueue objectAtIndex:0];
[writeQueue removeObjectAtIndex:0];
[peripheral writeValue:message forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
}
}
Below image is the log
OUTPUT LOG

Write to multiple bluetooth Peripherals from Central

I've searched SO for help on this and haven't found anything that will answer, address or get me pointed in the right direction so I've decided to post my issue.
I have a BT Central app running on an Apple TV. I have a peripheral app running on an iPhone and iPad.
The central app is able to connect to both peripheral devices just fine. I'm able to transfer all kinds of data to the central app and have control over all of the phases of the session (didDiscoverPeripheral, didDiscoverServices, didDiscoverChracteristics, etc.) All the delegate methods on both central and peripheral sides are behaving exactly as they should.
When the central app connects to a peripheral and it discovers the "writable" characteristic it sends (writes) an NSString to the peripheral with something like "Hi iPad, you've connected to central" or "Hi iPhone you've connected to central". In doing this I know that everyone is connected, discovered, processed and a reference to the peripherals is saved. None of this is an issue and behaves exactly as is documented by Apple.
On the central app I have a UIButton that performs a write to all of the connected peripherals. I attempt to loop through the connected peripherals and write something to each one inside the loop. Unfortunately only the last connected peripheral receives the written data.
I have a nice NSDictionary of all of the peripheral information and object that I enumerate through. I've even based the loop on the
retrieveConnectedPeripheralsWithServices method. My peripherals all use a custom class for their delegate so I know I'm not crossing the same delegate with multiple peripherals.
Inside the loop I can see that the peripheral is connected, the characteristic I'm targeting has writeWithResponse properties and write permissions. No reference to the peripheral has been lost or released. Everything maps out and looks great.
I just can't write to all the connected peripherals from inside a loop. I've tried queuing up the writes in an NSOperation and or dispatch_async in case it's a timing thing but nothing is working.
If the iPad is the last connect peripheral it gets the write. If the iPhone connect last then it gets the write. The last connected peripheral is the only clue I've got to go on but I'm just not seeing the problem.
At this point I'm out of sticks and carrots and several days of googling and SO searching. Sorry for the long post but I wanted to explain and also show that I'm not just asking out of laziness but have sincerely tried everything I know.
Thanks for any help.
Added relevant code:
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *) advertisementData RSSI:(NSNumber *)RSSI {
//it's in range - have we already seen it?
if([self findPeripheralMatching:peripheral.identifier] == nil) {
//hack if the advertisingData local name or the peripheral GATT name is NULL
NSDictionary *dict = [self cleanupAdvertisementData:peripheral advertisementData:advertisementData];
if(dict == nil) {
[self stop];
[self start];
return;
}
//save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
XCBluetoothPeripheralDictionary *obj = [[XCBluetoothPeripheralDictionary alloc] init];
obj.peripheral = peripheral;
obj.advertisementData = dict;
[self.peripheralDictionary setObject:obj forKey:[peripheral.identifier UUIDString]];
//and connect is not connected...
if(peripheral.state == CBPeripheralStateDisconnected) {
[self.centralManager connectPeripheral:peripheral options:nil];
}
} //findPeripheralMatching
} //didDiscoverPeripheral
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
//make sure we get the discovery callbacks
XCBluetoothPeripheral *pd = [[XCBluetoothPeripheral alloc] initWithDelegate:self];
pd.subscriptionDictionary = self.subscriptionDictionary;
peripheral.delegate = pd;
//save a copy of the XCPeripheral object
[self.peripheralDictionary objectForKey:[peripheral.identifier UUIDString]].delegate = pd;
//discover and search only for services that match our service UUID
[peripheral discoverServices:#[[CBUUID UUIDWithString:self.serviceUUID]]];
//notify the delegate we connected
XC_SelectorAssert(#selector(bluetoothCentralDidConnect:), self.delegate)
if (self.delegate && [self.delegate respondsToSelector:#selector(bluetoothCentralDidConnect:)]) {
XCBluetoothPeripheralDictionary *dict = [self.peripheralDictionary objectForKey:[peripheral.identifier UUIDString]];
[self.delegate bluetoothCentralDidConnect:dict];
} else {
NSAssert(NO, XCMissingSelectorForProtocol);
}
} //didConnectPeripheral
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if(error) {
[self callbackError:error];
return;
}
//again, we loop through the array, and if the characteristic matches
//whats in the subscriptionDictionary then we subscribe to it
for (CBCharacteristic *characteristic in service.characteristics) {
if([self isDesiredCharachteristic:characteristic.UUID]) {
[peripheral discoverDescriptorsForCharacteristic:characteristic];
if(characteristic.properties & CBCharacteristicPropertyNotify) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
if(characteristic.properties & CBCharacteristicPropertyRead) {
}
if(characteristic.properties & CBCharacteristicPropertyWrite) {
NSLog(#"Writing value to %# - %#", peripheral.identifier, peripheral.name);
NSString *string = [NSString stringWithFormat:#"%# connected to %#",
peripheral.name,
[XCUtilities deviceName]];
[peripheral writeValue:[string dataUsingEncoding:NSUTF8StringEncoding]
forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
}
if(characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
}
[self.subscriptionDictionary objectForKey:(NSString *)characteristic.UUID].characteristic = characteristic;
} //if isMatching
} //for CBCharacteristic
} //didDiscoverCharacteristicsForService
The following method is called from an IBAction
-(void)writeValueToCharacteristic:(CBUUID *)cbuuid value:(NSString *)string {
//get a reference to the characteristic we specified
CBCharacteristic *chr = [self findCharacteristicMatching:cbuuid];
XC_CBCharacteristicAssert(chr)
//enumerate through the discovered peripherals
[self.peripheralDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, XCBluetoothPeripheralDictionary *obj, BOOL *stop){
XC_CBPeripheralAssert(obj.peripheral)
if(obj.peripheral.state == CBPeripheralStateConnected) {
//check the properties
if(chr.properties & CBCharacteristicPropertyWriteWithoutResponse ||
chr.properties & CBCharacteristicPropertyWrite) {
NSLog(#"Writing value to:\n%#\n%#\n%#\n%#\n%#",
key,
obj.advertisementData,
obj.peripheral.name,
obj.peripheral.delegate,
obj.peripheral);
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[obj.peripheral writeValue:data forCharacteristic:chr
type:CBCharacteristicWriteWithResponse];
} else {
[self localError:XC_BTErrorPermissionWritable description:XCErrorWritingCharacteristic];
}
} //is connected
}];
} //writeValueToCharacteristic
I would think that if something were wrong with the way I'm saving the peripherals or my custom dictionary or the way I'm using this stuff then my writes would fail for all peripherals and not just one of the two I'm testing with from inside a loop. And I know I'm connected and discovered and all is well because when central initially processes these peripherals it writes to each one as sort of a confirmation that they are indeed ready to go.
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests {
[peripheral respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorSuccess];
CBATTRequest *aRequest = requests[0];
NSData *aData = aRequest.value;
NSString *string = [[NSString alloc] initWithData:aData encoding:NSUTF8StringEncoding];
[self logToDelegate:string];
}
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if(error) {
[self callbackError:error];
return;
}
[self logToDelegate:#"didWriteValueForCharacteristic"];
} //didWriteValueForCharacteristic

How to enable Indications on Client Configuration descriptor from iOS8

I am trying to get the notification from a bluetooth device upon the characteristic value change. For this I need to enable notification for Client Characteristic Configuration(CCC) descriptor. I have used setNotifyValue(enabled: Bool, forCharacteristic characteristic: CBCharacteristic) for the characteristic but not getting the update for value changes.
I tried to enable the indication for CCC using writeValue(data: NSData, forDescriptor descriptor: CBDescriptor) but my app crashes for this API and shows the error as
Cannot write Client Characteristic Configuration descriptors using this method!
Any help!!
Provide more codes might help to imporove accuracy of an answer; however, let's be assuming you have already been able to discover all characteristic values. Usually you just need to iterate all characteristics and set/write value according to each Client Characteristic Configuration(CCC) descriptor in CBPeripheralDelegate implementation.
An example is attached below:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error) {
NSLog(#"Error discovering characteristics: %#", error);
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBManager accelDataUUID]]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
} else if ([characteristic.UUID isEqual:[CBManager accelConfigUUID]]) {
[peripheral writeValue:[CBManager switchData:YES]
forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
}
//... if you have more to iterate
}
}
You need to check for the availability of the characteristic's notification.
From Apple's doc
When you subscribe to (or unsubscribe from) a characteristic’s value,
the peripheral calls the
peripheral:didUpdateNotificationStateForCharacteristic:error: method
of its delegate object. If the subscription request fails for any
reason, you can implement this delegate method to access the cause of
the error, as the following example shows:
-(void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(#"Error changing notification state: %#",
[error localizedDescription]);
}
...
Note: Not all characteristics offer subscription. You can determine
if a characteristic offers subscription by checking if its properties
attribute includes either of the CBCharacteristicPropertyNotify or
CBCharacteristicPropertyIndicate constants.

Bluetooth LE can't send data from central to peripheral

I am dealing with BluetoothLow Energy problem. I find my peripheral BLE device with iPhone, connect to it, also find service and the characteristic which is in write mode. But when I try to send some data, nothing happens - device doesn't receive anything and also - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error is not called.
My writeValue method is here:
-(void) writeValue:(int)serviceUUID characteristicUUID:(int)characteristicUUID p:(CBPeripheral *)p data:(NSData *)data {
CBUUID *UUIDService = [CBUUID UUIDWithString:[NSString stringWithFormat:#"%#",TRANSFER_SERVICE_UUID]];
CBUUID *UUIDCharacteristic = [CBUUID UUIDWithString:[NSString stringWithFormat:#"%#",TRANSFER_CHARACTERISTIC_UUID]];
NSLog(#"ALERT");
CBService *service = [self findServiceFromUUID:UUIDService p:p];
if (!service)
{
NSLog(#"Could not find service with UUID %# on peripheral with UUID %#",
[self CBUUIDToString:UUIDService],
p.identifier.UUIDString);
return;
}
CBCharacteristic *characteristic = [self findCharacteristicFromUUID:UUIDCharacteristic service:service];
if (!characteristic)
{
NSLog(#"Could not find characteristic with UUID %# on service with UUID %# on peripheral with UUID %#",
[self CBUUIDToString: UUIDCharacteristic],
[self CBUUIDToString:UUIDService],
p.identifier.UUIDString);
return;
}
NSString *message = #"AB";
NSData *dataMsg= [message dataUsingEncoding:NSUTF8StringEncoding];
[p writeValue:dataMsg forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
What could be the problem?
Try to this. swift 3
This code for enable notify for CBCharacteristic(0x12 for notify property).
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for newChar: CBCharacteristic in service.characteristics!{
peripheral.readValue(for: newChar)
if newChar.properties.rawValue == 0x12{
peripheral.setNotifyValue(true, for: newChar)
}
}
}

IOS Write to BLE Characteristic using Slider

Really struggling with writing back to a BLE Peripheral. Please help...
I'm connected and have read the characteristics available and wish to write back from a IBAction Slider:
-(IBAction)SrControlIndex:(UISegmentedControl *)sender
{
switch (_SRControl.selectedSegmentIndex)
{
case 0:
[self writeModeCharacteristic:Status_UUID data:[#"00" dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"First Sel");
break;
case 1:
NSLog(#"Second Sel");
break;
default:
break;
}
}
And calls the following write:
-(void)writeModeCharacteristic:(CBCharacteristic *)ModeCharacteristic data:(NSData*)data
{
[ModeCharacteristic.service.peripheral writeValue:data forCharacteristic:ModeCharacteristic type:CBCharacteristicWriteWithResponse];
}
What am I missing?
Thanks to Larme for the answer:
#property (nonatomic, strong) CBCharacteristic statusCharacteristic; When you discovers it: _statusCharacteristic = statusCharacteristicJustDiscovered; Then you can reuse it in your method.
This worked great...
An option is to keep the CBPeripheral object. After detecting services and characteristics you can run something like this:
for (CBService *service in self.peripheral.services) {
if ([service.UUID isEqual:[CBUUID UUIDWithString:#"YOUR-SERVICE-UUID"]]) {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"YOUR-CHARACTERISTIC-UUID"]]) {
[self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
}
}

Resources