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)
}
}
}
Related
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
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
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.
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];
}
}
}
}
So I'm working with two apps. One acts as the central, the other is the peripheral. There could be more than one devices that act as the peripheral. When the Central launches, it begins scanning for peripherals that are running a service I call UUIDService. This service has two characteristics: vendorUUID (read) and totalAmount (write).
When the Peripheral launches, it begins advertising UUIDService with it's -[UIDevice identifierForVendor] string as the value for vedorUUID and nil for totalAmount.
On Central, I have a singleton called BluetoothManager which holds a mutable array called connectedUsers to keep track of peripherals that are connected to the central. This is an array of BTUser objects which has two properties: uuid (NSString) and periph (CBPeripheral).
When Central discovers a peripheral, it creates the user object and fills in the reference to periph:
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(#"Discovered periph advertising UUID Service");
BTUser* user = [[BTUser alloc] init];
user.periph = peripheral;
[_centralManager connectPeripheral:user.periph options:nil];
[_connectedUsers addObject:user];
NSLog(#"User periph: %#", user.periph.identifier);
}
This block of code works. _connectedUsers now contains one BTUser with a periph. Then it connects:
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(#"Connected periph: %#", peripheral.name);
BTUser* thisUser;
for (BTUser* user in _connectedUsers)
{
if ([user.periph.identifier isEqual: peripheral.identifier])
{
thisUser = user;
break;
}
}
thisUser.periph.delegate = self;
[thisUser.periph discoverServices:#[_uuidServiceUUID]];
}
I've confirmed this works, and the peripheral goes on to discover the service and and related characteristics. The issue I'm running into is when I go to use the reference to the peripheral to write to totalAmount.
-(void)initializePaymentWithUser:(BTUser*)user forAmount:(CGFloat)amount
{
_isListeningForUsers = NO;
[self stopScanningForUsers];
NSLog(#"disconnecting from all other users");
for (BTUser* u in _connectedUsers)
{
if (![u isEqual:user])
{
[u disconnectFromCentral:_centralManager];
}
}
CBService* uuidService = user.periph.services[0];
for (CBCharacteristic* cha in uuidService.characteristics)
{
if ([cha.UUID isEqual:_uuidServiceTotalAmountUUID])
{
NSLog(#"Writing total amount %.2f", amount);
NSData* data = [[NSString stringWithFormat:#"%.2f", amount] dataUsingEncoding:NSUTF8StringEncoding];
[user.periph writeValue:data
forCharacteristic:cha
type:CBCharacteristicWriteWithResponse];
break;
}
}
}
It makes it to the block that writes the value, and user.periph still has the same -[CBPeripheral identifier] as it did when it originally connected. But I'm getting the error:
CoreBluetooth[WARNING] <CBPeripheral: 0x14ed28c0 identifier = B4CB35CC-2C78-7543-B083-E0B373EF66D0, Name = "iPhone", state = connected> is not a valid peripheral
And the peripheral never calls -peripheral:didWriteValueForCharacteristic:error.
For completion to this questions, this is the block of code from the Peripheral responsible for advertising the UUIDService:
-(void)advertiseVendorUUID:(NSString*)uuid
paymentInitiatedBlock:(DOBluetoothPeripheralBlock)completion
{
_uuidService = [[CBMutableService alloc] initWithType: _uuidServiceUUID
primary:YES];
NSLog(#"Adding vendor id to characteristic: %#", uuid);
NSData* vendorData = [uuid dataUsingEncoding:NSUTF8StringEncoding];
_uuidServiceVendorUUIDChar = [[CBMutableCharacteristic alloc] initWithType:_uuidServiceVendorUUIDUUID
properties:CBCharacteristicPropertyRead
value:vendorData
permissions:CBAttributePermissionsReadable];
_uuidServiceTotalAmountChar = [[CBMutableCharacteristic alloc] initWithType:_uuidServiceTotalAmountUUID
properties:CBCharacteristicPropertyWrite
value:nil
permissions:CBAttributePermissionsWriteable];
_uuidService.characteristics = #[_uuidServiceVendorUUIDChar, _uuidServiceTotalAmountChar];
[_peripheralManager addService:_uuidService];
[_peripheralManager startAdvertising:#{CBAdvertisementDataServiceUUIDsKey: #[_uuidService.UUID],
CBAdvertisementDataLocalNameKey: #"UUID"}];
}