On iOS7.1.1, the following BLE operation succeeds - it assumes I have a BLE connection setup etc...
[[self peripheral]writeValue:dataToWrite forCharacteristic:nextCharacteristic type:CBCharacteristicWriteWithResponse];
But If I switch the "type" to CBCharacteristicWriteWithoutResponse, I get the following warning and the peripheral does not receive the command :(
[[self peripheral]writeValue:dataToWrite forCharacteristic:nextCharacteristic type:CBCharacteristicWriteWithoutResponse];
Error:
CoreBluetooth[WARNING] Characteristic <CBCharacteristic: 0x178081f90 UUID = 249C2001-00D7-4D91-AC75-22D57AE2FFB8, Value = (null), Properties = 0x28, Notifying = YES, Broadcasting = NO> does not specify the "Write Without Response" property - ignoring response-less write**
Any clues appreciated!
When a BLE peripheral advertises characteristics the advertisement includes the properties of those characteristics. These include what operations are supported on that characteristic - read, notify, write without response and write with response.
In this case it seems that the characteristic supports write with response but not write without response, so when you attempt a write without response you get the warning and the write operations doesn't complete
Related
I need to get the list of paired bluetooth devices(iOS Devices) as same as the list in 'Bluetooth' section in iOS settings as shown in below picture.
Is it possible?
Have you seen any apps doing this type of functionality?
I have tried the following:
link1, link2, link3, link4, link5, link6
But nothing helped me clearly to get the exact list. I hope there should be a way to achieve this. Please help me by sharing your experience.
Thank you.
It's not possible to retrieve list of paired peripherals from iOS. Neither it's possible to check if specific peripheral is paired.
Retrieving peripheral which is paired
There are two cases which you need to consider:
Peripheral may be already connected in the system (iOS connects automatically with some peripherals in order to for example display battery level). In this case peripheral won't be broadcasting and detection using scanForPeripherals won't work.
Peripheral is paired, but disconnected. In this case retrieveConnectedPeripherals(withServices:) won't work.
Therefore to retrieve your peripheral you need to combine both things. First you need to check if it's in peripherals returned from retrieveConnectedPeripherals(withServices:). If not you should scanForPeripherals.
If you want to retrieve peripheral which is out of range, you can try to use retrievePeripherals(withIdentifiers:), however it may return also not paired devices and it relies on peripheral's UUID which you have to save after pairing.
Detecting if peripheral is paired
There is one way to detect if the specific peripheral is paired. You need to try to read from protected characteristic (which requires encryption - bonding). If you receive expected data, it means that user accepted pairing request. Otherwise you will receive empty response or none.
References
You can read more about Bluetooth in my articles:
Original answer
Swift – Bluetooth Low Energy communication using Flow Controllers
How to communicate with Bluetooth Low Energy devices on iOS
Bluetooth Low Energy – background mode on iOS
You can use ShowBluetoothAccessoryPicker if you are using the Classic Bluetooh into an MFi device.
https://developer.apple.com/documentation/externalaccessory/eaaccessorymanager/1613913-showbluetoothaccessorypicker
The code below is a C# but you can easily convert it into the IOS (object c your swift)
EAAccessoryManager.SharedAccessoryManager.ShowBluetoothAccessoryPicker(null, completion: ((Foundation.NSError error) => {
Console.WriteLine("My callback");
if (error != null && (uint)error.Code != (uint)EABluetoothAccessoryPickerError.AlreadyConnected)
{
Console.WriteLine(String.Format("Error code: {0} Desc: {1}", error.Code, error.DebugDescription));
Console.WriteLine("Failed? " + EABluetoothAccessoryPickerError.Failed.ToString());
Console.WriteLine("Failed? " + Convert.ToInt64(EABluetoothAccessoryPickerError.Failed));
}
}));
You need to find the service UUID in which you are interested, in my case it works perfectly,
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[self.centralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:SERVICE_UUID]]
options:options];
and when it will find any device which advertise same service UUID, then it will appear in the screen which you have pointed above.
handle didDiscoverperipherel in this way:
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
_discoveredPeripheral = peripheral;
if(![self.mRemoteDevices containsObject:_discoveredPeripheral])
{
NSArray *peripherels = [self.centralManager retrievePeripheralsWithIdentifiers:#[_discoveredPeripheral.identifier]];
[self.mRemoteDevices addObject:[peripherels objectAtIndex:0]];
}
}
I have to access heart rate from Fitbit Surge Device , its give four services are with -
UUID - 180a (work fine for Device Information)
UUID - 180f (work fine for Battery)
UUID - 558dfa00 4fa84105 9f024eaa 93e62980
UUID - adabfb00 6e7d4601 bda2bffa a68956ba
I have to set [peripheral setNotifyValue:YES forCharacteristic:c], with [CBUUID UUIDWithString:#"ADABFB01-6E7D-4601-BDA2-BFFAA68956BA"] didUpdateNotification: are <CBCharacteristic: 0x17dc6bb0, UUID = ADABFB01-6E7D-4601-BDA2-BFFAA68956BA, properties = 0x12, value = (null), notifying = YES>.
But value is nil.
Please help me or any suggestion .
I know it's been a few months since you asked, but in case you're still wondering (or anyone else is), you're probably receiving a nil because Fitbit does not expose its data over Bluetooth. There's a discussion stating this on the Fitbit developers forum here: https://community.fitbit.com/t5/Web-API/Where-is-the-Bluetooth-SDK-Can-my-app-communicate-directly-with-the-Fitbit/td-p/324384
If you scroll through the posts, you'll see that Fitbit still does not have plans to expose data over Bluetooth, because they believe it will adversely affect the battery.
I have two programs, one for Mac and one for iOS. When I connect to the iOS device from the Mac, it finds the services I want, and the characteristics I want. After finding the characteristic, I use peripheral.setNotifyValue(true, forCharacteristic: characteristic), but the peripheralManager:central:didSubscribeToCharacteristic: method isn't being called on the iOS side. When I check if characteristic.isNotifying it is false. From what I understand when I set the notify value to true, it should be notifying, and whenever i change the value is updates. Why is it not updating? Thanks in advance.
Here is the code that sets up the characteristic in question -
self.characteristic = CBMutableCharacteristic(type: UUID_CHARACTERISTIC, properties: CBCharacteristicProperties.Read, value: self.dataToSend, permissions: CBAttributePermissions.Readable)
theService = CBMutableService(type: UUID_SERVICE, primary: true)
theService.characteristics = [characteristic]
self.peripheralManager.addService(theService)
If you want your characteristic to support notify operations then you need to set this on its properties -
self.characteristic = CBMutableCharacteristic(type: UUID_CHARACTERISTIC, properties: CBCharacteristicProperties.Read|CBCharacteristicProperties.Notifiy, value: self.dataToSend, permissions: CBAttributePermissions.Readable);
An attempt to set notification on a characteristic that doesn't state support for notification is ignored by Core Bluetooth.
Also, be aware that by setting a value when the characteristic is created, you will not be able to change the value of this characteristic in the future - therefore notification is somewhat pointless. If you want to be able to change the value you must specify nil for value when you create the characteristic.
From the CBMutableCharacteristic documentation -
Discussion
If you specify a value for the characteristic, the value is
cached and its properties and permissions are set to
CBCharacteristicPropertyRead and CBAttributePermissionsReadable,
respectively. Therefore, if you need the value of a characteristic to
be writeable, or if you expect the value to change during the lifetime
of the published service to which the characteristic belongs, you must
specify the value to be nil. So doing ensures that the value is
treated dynamically and requested by the peripheral manager whenever
the peripheral manager receives a read or write request from a
central. When the peripheral manager receives a read or write request
from a central, it calls the peripheralManager:didReceiveReadRequest:
or the peripheralManager:didReceiveWriteRequests: methods of its
delegate object, respectively.
After establishing a connection with a TI BLE Keyfob and retrieving the descriptors for a characteristic, I attempt the call
[peripheral writeValue: nsData forDescriptor: descriptor];
The various objects look reasonable in the debugger. nsData has a single byte of 1, peripheral is a valid looking CBPeripheral object returned from iOS, and descriptor is a valid looking CBDescriptor returned by iOS.
My app crashes with the error
* Assertion failure in -[CBConcretePeripheral writeValue:forDescriptor:],
/SourceCache/CoreBluetooth/CoreBluetooth-59.3/CBConcretePeripheral.m:358
Any thoughts on why this is happening or how to debug it?
Note from the documentation of writeValue:forDescriptor
Client characteristic configuration descriptors cannot be written
using this method, and should instead use setNotifyValue:forCharacteristic:
So if you are trying to write the descriptor of the connected peripheral, then it will fail.
For writing the characteristic, use the - writeValue:forCharacteristic:type: method.
My code is based on Apple's the CoreBluetooth sample code named "TemperatureSenor".
I find a phenomena that if I set peripheral to repeat sending message, then call peripheral:setNotifyValue:YES forCharacteristic: , at last peripheral:didUpdateValueForCharacteristic: is called.
If I call peripheral:setNotifyValue:YES forCharacteristic: to listen messages from peripheral, then set peripheral to send message to central, the central will not call peripheral:didUpdateValueForCharacteristic:.
What's the reason?
Maybe you should be sure that whether your characteristic that you use to send message has the notify property which is decided by peripheral.If your characteristic doesn't has notify property but you still call "peripheral:setNotifyValue:YES forCharacteristic:",you will receive unknown error 2.
If your characteristic has notify property and you call "peripheral:setNotifyValue:YES forCharacteristic:",the central will call "peripheral:didUpdateValueForCharacteristic:"