The documentation for CBMutableDescriptor:initWithType:value: says to pass a "128-bit UUID that identifies the characteristic" for the type parameter. It then goes on to say you should only use one of CBUUIDCharacteristicUserDescriptionString or CBUUIDCharacteristicFormatString for the type parameter. Finally, there is no method to add a descriptor to a mutable characteristic.
It appears that the parameter is doing two mutually exclusive things. On the one hand, it is being used to tell the O/S which characteristic the descriptor applies to, and on the other, it is being used to set the type of descriptor. The second makes more sense, but then how do you add the descriptor to the characteristic?
Whether you pass the UUID for the characteristic or CBUUIDCharacteristicUserDescriptionString, iOS crashes with
Assertion failure in -[CBMutableDescriptor initWithType:value:], /SourceCache/CoreBluetooth_Sim/CoreBluetooth-59.3/CBDescriptor.m:25
What's the correct way to create the CBMutableDescriptor and add it to a CBMutableCharacteristic?
You're correct about the docs. But just to be clear for everyone, here is a citation found in CBDescriptor.h:
...Only the Characteristic User Description and Characteristic
Presentation Format descriptors are currently supported. The
Characteristic Extended Properties and Client Characteristic
Configuration descriptors will be created automatically upon
publication of the parent service, depending on the properties of the
characteristic itself.
So in other words, unless you are setting those descriptors, the system will block you (thus why you got the assertion failure).
So say you want to use the Characteristic User Description descriptor, you would do:
CBUUID *yourCharUUID = [CBUUID UUIDWithString:#"c07c5050-15a0-11e3-8ffd-0800200c9a66"];//whatever UUID your using
CBMutableCharacteristic *yourCharacteristic = [[CBMutableCharacteristic alloc]initWithType:yourCharUUID properties:CBCharacteristicPropertyWriteWithoutResponse value:nil permissions:perms];
CBUUID *userDescriptionUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];//or set it to the actual UUID->2901
CBMutableDescriptor *yourDescriptor = [[CBMutableDescriptor alloc]initWithType:userDescriptionUUID value:#"myDescriptorValue"];
yourCharacteristic.descriptors = #[yourDescriptor];
Let me know if you have any questions.
Related
I want to check which Bluetooth Devices my iPhone is connected to. In order to do that, I use CBCentralManager.retrieveConnectedPeripherals() like this:
let connectedPerphs = centralManager.retrieveConnectedPeripherals(withServices: []);
My problem is that even if my iPhone is connected to a BluetoothDongle (it explicitly says "connected" in the settings), the list that is returned by retriveConnectedPeripherals() is always empty. Am I using the method in a wrong way or can it not be used to detect a bluetooth connection such as the connection to to my dongle? If the latter is the case, how can I detect that connection?
Let me clear, centralManager.retrieveConnectedPeripherals always return empty or nil value, If you are not passing any value into serviceUUIDs
retrieveConnectedPeripherals(withServices:)
Returns a list of the peripherals (containing any of the specified
services) currently connected to the system.
serviceUUIDs:
A list of service UUIDs (represented by CBUUID objects).
Update:
Unfortunately this the long way to do it. You can create Array of CBUUID statically then you can pass it to the method. Please refer below code.
let aryUUID = ["1800","18811"]
var aryCBUUIDS = [CBUUID]()
for uuid in aryUUID{
let uuid = CBUUID(string: "1800")
aryCBUUIDS.append(uuid)
}
let connectedPerphs = centralManager.retrieveConnectedPeripherals(withServices: aryCBUUIDS)
List of available services
First, this works only with BLE devices, thus if your dongle is using a common BT you will not get it from here, but probably using EAAccessoryManager var connectedAccessories: [EAAccessory] method, but as far as I know your app must comply to MFI.
That is why is asking which service your devices are exposing as a filter.
I connect BLE with CoreBluetooth and paired.
Now when I back to my app screen, I want to make sure that BLE already paired with iOS device.
If I store value in defaults and remove app, this case will not work to fetch device.
If user remove paired bluetooth peripheral from Setting -> Bluetooth -> list of devices this case also not work to identify.
NSArray *ary = [self.bleMgr retrieveConnectedPeripheralsWithServices:#[[CBUUID UUIDWithString:#"180A"]]];
NSUUID *nsUUID = [[NSUUID alloc] initWithUUIDString:identifier];
NSArray *temp = [self.bleMgr retrievePeripheralsWithIdentifiers:#[nsUUID]];
Above both code lines not giving robust result.
How to get that paired BLE device in app?
The first time you discover a peripheral, the system generates an identifier (a UUID, represented by an NSUUID object) to identify the peripheral.
You can then store this identifier (using, for instance, the resources of the NSUserDefaults class), and later use it to try to reconnect to the peripheral using the retrievePeripheralsWithIdentifiers: method of the CBCentralManager class.
The following describes one way to use this method to reconnect to a peripheral you’ve previously connected to.
knownPeripherals = [myCentralManager retrievePeripheralsWithIdentifiers:savedIdentifiers];
The NSUUID class is not toll-free bridged with CoreFoundation’s
CFUUID. Use UUID strings to convert between CFUUIDRef and NSUUID, if
needed. Two NSUUID objects are not guaranteed to be comparable by
pointer value (as CFUUID is); use isEqual(_:) to compare two NSUUID
instances.
Refer this: https://developer.apple.com/documentation/foundation/nsuuid
Refer last two sections: https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/BestPracticesForInteractingWithARemotePeripheralDevice/BestPracticesForInteractingWithARemotePeripheralDevice.html#//apple_ref/doc/uid/TP40013257-CH6-SW1
I'm writing my code using Xamarin. I'm developing my IOS app which allows me to read the BLE devices, Services, characteristic value and activation of the notification.
My BLE beacon have one custom Services that contain two custom characteristics and both have the notification implemented using CCCD.
My ble devices work correctly I test it with BLE scanner app and it working well without any problem.
I can read value and I can active the notification for both characteristic. See picture here.
The app that I wrote using xamarin work correctly (reading services, characteristic value....) the only problem that didn't work is the activation of the notification. Here's a portion of the code :
public UUID Charac_UUID0 = UUID.FromString("0000beef-1212-efde-1523-785fef13d123");
public UUID Charac_UUID = UUID.FromString("0000b1e0-1212-efde-1523-785fef13d123") ;
public UUID Descr_UUID = UUID.FromString("00002902-1212-efde-1523-785fef13d123");
protected BluetoothGattCharacteristic _charac;
....
....
this._charac = App.Current.State.SelectedService.GetCharacteristic(Charac_UUID0);
BluetoothLEManager.Current.ConnectedDevices[App.Current.State.SelectedDevice].SetCharacteristicNotification(_charac, true);
BluetoothGattDescriptor descriptor = _charac.GetDescriptor(Descr_UUID0);
descriptor.SetValue(BluetoothGattDescriptor.EnableNotificationValue.ToArray());
BluetoothLEManager.Current.ConnectedDevices[App.Current.State.SelectedDevice].WriteDescriptor(descriptor);
The code always give me an error at descriptor.SetValue and it indicate me that the descriptor is NULL meaning that _charac.GetDescriptor didn't return any value.
I suspect the Descriptor UUID value (Descr_UUID) is not correct. I don't know excatly how can I determine the Descr_UUID but I saw many example in the internet of people replacing the custom UUID of the caracteristic by 2902 which give me in my case a 128 descriptor UUID equal to 00002902-1212-efde-1523-785fef13d123.
But there is a problem here. The descriptor UUID for both characteristic will be the same because the base UUID is the same for both characteristic?
Any solution?
3 years later im sure you figured it out but, 2902 is the 16 bit value for a Client Characteristic Configuration Descriptor, and I believe it should also be inflated using the "BASE UUID" not just whatever your characteristic descriptor uses. See this: answer
This will convert your 16 (or 32) bit uuids to full 128 bit uuids using the BASE UUID:
public static UUID ConvertUuid(uint uuid)
{
const long msbMask = 0x0000000000001000;
const ulong lsb = 0x800000805f9b34fb;
var msb = msbMask | ((ulong)(uuid & uint.MaxValue) << 32);
return new UUID((long)msb, unchecked((long)lsb));
}
The IotfDevice Class in Quarks has a constructor with an optionsFile. What is the format of this file and what are the necessary values?
The options file has five values: org, type, id, auth-method, auth-token. This is an example of mine with the values obfuscated.
[device]
org = 4pj4r4
type = rustQuark
id = rustIIa
auth-method = token
auth-token = 8jLWEY(P4SVJl5oi!V
When you register a device the set of values are issued, details and examples can be found here.
A little context.
Internet of Things Foundation (IoTf) is an infrastructure for application's to communicate with devices. IoTf requires that you register the application and all
devices that send data to the application.
Why would you use IoTf? Getting the communication setup between devices is
a pain: firewalls, retry, failover, QoS, debugging, monitoring. This is facility especially nice if you're doing a proof of concept.
For a better description of IoTf look here.
I am developing an open source block based wrapper for Core Bluetooth.
Currently I am developing the Peripheral Manager part.
I have it broadcasting and I can see the service and its characteristic. When looking at other services I can see they have been given meaningful names such as Battery and Current time.
When I try and provide a meaningful name for any service or characteristic I create it returns the error String Characteristic name does not represent a valid UUID.
My code so far is
[_peripheralManager startAdvertising:#{CBAdvertisementDataLocalNameKey : #"Peripheral name",
CBAdvertisementDataServiceUUIDsKey : [CBUUID UUIDWithNSUUID:[NSUUID UUID]]}];
CBMutableCharacteristic *transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:#"Characteristic name"]
properties:CBCharacteristicPropertyRead
value:[#"Test value" dataUsingEncoding:NSUTF8StringEncoding]
permissions:CBAttributePermissionsReadable];
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:#"Service name"]
primary:YES];
[transferService setCharacteristics:#[transferCharacteristic]];
[_peripheralManager addService:transferService];
If I replace any initWithType arguments with [CBUUID UUIDWithNSUUID:[NSUUID UUID]] it works but is not nice to show to the user.
Is there a way to set the UUID of the CBService or CBCharacteristic to a meaningful string?
It is also worth noting that the value of the characteristic is null when viewed, bonus points for seeing why this is.
Thanks
The Bluetooth organisation has accepted some services and their characteristics as standards. These can be referred to by a 4 hex digit assigned number when using the Core Bluetooth library - See the CBUUID documentation
User defined UUIDs are not recognised by the Core Bluetooth library as they are not assigned numbers, so you can only display them with their full UUID.
The value of the characteristic will be null if you haven't issued a read or received a notify.
Use the terminal command uuidgen to create a user defined UUID.
As Paul mentioned, the value will be null until you read the value of the characteristic. It seems like a lot of work but after you discover a peripheral you connect to it (don't forget to retain the peripheral and set the peripheral's delegate property), then discover its services, then discover the characteristics for the services, then you may read the value of the characteristics like so:
[peripheral readValueForCharacteristic:characteristic]; //where peripheral is the connected CBPeripheral
In the CBCentralManagerDelegate method peripheral:didUpdateValueForCharacteristic:error: the value can be read:
NSLog(#"%#",[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding]);
Based on the code you included, it is not necessary to include the advertisement data service UUID key in your options dictionary . If you would like to specify a data service UUID that is specific to your app, start advertisementing like this:
[_peripheralManager startAdvertising:#{CBAdvertisementDataLocalNameKey : #"Peripheral name",
CBAdvertisementDataServiceUUIDsKey : #[[CBUUID UUIDWithString:#"generated-string-using-uuidgen"]]}];
You can then set your CBCentralManager to scan for that specific data service uuid like this:
[_centralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:#"generated-string-using-uuidgen"]] options:nil];