I am able to get a value for BLE battery life with the help of following questions:
Read data from BLE device
Reading a BLE Peripheral Characteristic and checking its value?
But I am not sure if It returns the right value? It returns 18, and I am also not sure about the maximum number to determine the battery life based on percentage. Does it mean 18 hours?
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print("-CBService is: \(service.uuid.description)")
if compare(service.uuid, uuid2: CBUUID(string:BluetoothConstants.TI_KEYFOB_BATT_SERVICE_UUID)) {
print("Battyer Life determination")
for characteristic: CBCharacteristic in service.characteristics! {
if characteristic.uuid == CBUUID(string: BluetoothConstants.TI_KEYFOB_LEVEL_SERVICE_UUID) {
print(characteristic.properties.rawValue)
}
}
}
}
The following line is print for characterstic:
CBCharacteristic: 0x1c00be8a0, UUID = Battery Level, properties =
0x12, value = (null), notifying = NO>
I tried most of the answers here but they are not working for Swift 4.
I'm not sure about Swift 4, but here's how I figured it out using Swift 5. The value in the characteristic.properties is not the battery level. You need to request a read from the device on the Battery Level characteristic (0x2A19):
if characteristic.uuid == CBUUID(string: "0x2A19") {
peripheral.readValue(for: characteristic)
}
Then you'll need to add the didUpdateValueFor as part of your CBPeripheralDelegate as well:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("Battery level: \(characteristic.value![0])")
}
I tested this with an old 3V that was at 2.76V (which is pretty much considered dead) and it gave me a value of 3. Then I put in a new battery and it gave me a value of 100.
func peripheral(
_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?
) {
if characteristic.uuid.uuidString == "2A19" {
print("Battery level: \(characteristic.value![0])")
}
}
func peripheral(
_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor service: CBService,
error: Error?
) {
if let error = error {
print("Error discovering service characteristics: \(error.localizedDescription)")
}
for newChar: CBCharacteristic in service.characteristics! {
peripheral.readValue(for: newChar)
if newChar.properties.rawValue == 0x12 {
peripheral.setNotifyValue(true, for: newChar)
print("characteristicValues",newChar)
}
}
}
Related
We have a peripheral device and I am trying to connect it with iOS device. It was working fine with Swift 3.0 But when I am trying to convert it in Swift 5.0 it did not work. It is working perfectly fine with Android device. So it is not an issue of hardware.
I have successfully connected and also discovered services and characteristics. My characteristics give me notify property only, I set that to true which also triggered the didupdateNotificationStateFor delegate method and it showed the characteristic.isnotifying as true. Which meant my subscription is successful. The only problem I am facing is didUpdateValueForCharacteristic is never been called. My code is as under.
extension HRMViewController: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
print(service)
peripheral.discoverCharacteristics(nil, for: service)
// break
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
print(characteristic)
if characteristic.properties.contains(.read) {
print("\(characteristic.uuid): properties contains .read")
peripheral.readValue(for: characteristic)
}
if transferCharacteristic.properties.contains(.notify) {
print("\(characteristic.uuid): properties contains .notify")
peripheral.setNotifyValue(true, for: characteristic)
}
if characteristic.properties.contains(.write){
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
switch characteristic.uuid {
case aCBUUID:
lblResult.text = "UUID: \(characteristic.uuid) Value = \(String(describing: characteristic.value))"
case bCBUUID:
lblResult2.text = "UUID: \(characteristic.uuid) Value = \(String(describing: characteristic.value))"
case f:
print("CORRECT Characteristic UUID: \(characteristic.uuid)")
print(characteristic.value ?? "no value")
default:
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
print(characteristic.value ?? "no value")
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
print("Error ==",error?.localizedDescription ?? "")
}
print("updateNotification: \(characteristic.isNotifying)")
}
Any help in this regard is highly appreciated.
I know disconnect error mentioned is asked many times on stack but answers accepted are not actual solution to issue
I am trying to connect MI Band 3 with my swift App. It get connected successfully but after some time it get disconnected automatically
Error: Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us." UserInfo={NSLocalizedDescription=The specified device has disconnected from us
My App Requirement: I need to get heart rate and walking distance from Apple Watch, Fitbit and MI Smart Watch
I have tried connecting same MI Band on multiple device result is same. But that specific MI band work perfectly with their own app
--> Connect Device - Kept reference of connected Device
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected With Peripheral: \(peripheral)")
selectedPeripheral=peripheral
self.delegate?.scannedPeripherals(Is: peripheral)
/// Discover Services Provided By Device
selectedPeripheral?.delegate=self
selectedPeripheral?.discoverServices([heartRateServiceCBUUID])
}
---> After Discovering services With heart Rate CBUUID
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
selectedPeripheral=peripheral
for char in characteristics {
print("Characterstics: \(char)")
if char.properties.contains(.read) {
}
if char.properties.contains(.notify) {
/// ------------- Setting Notify to true but not never call required delegates ----------------
peripheral.setNotifyValue(true, for: char)
print("\(char.uuid): properties contains .notify")
peripheral.readValue(for: char)
}
}
}
Calculating BPM from delegate
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
switch characteristic.uuid {
case bodySensorLocationCharacteristicCBUUID:
let bodySensorLocation = bodyLocation(from: characteristic)
print(bodySensorLocation)
default:
/// --------- here characteristic value is nil --------------
let bpm = heartRate(from: characteristic)
print("BPM: \(bpm)")
}
}
In func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
Try to remove peripheral.readValue(for: char) and test again!
I have a problem where I connect to a peripheral and set notify value to true correctly but I can't get the value. I am following the next steps.
1 - I connect to peripheral
2 - I discover services
3 - I discover characteristics for services
4 - I activate notifications for a specific characteristic
override func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if characteristic.uuid == historicCharacteristicCBUUID && characteristic.properties.contains(.notify) {
print("\(characteristic.uuid): properties contains .notify")
if hasWritenOnHistoric == false {
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
5 - I wait to the delegate and then write on the characteristic to tell the peripheral to start sending data
override func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print(characteristic)
if error == nil {
print("TRUE")
if characteristic.uuid == historicCharacteristicCBUUID && characteristic.isNotifying == true {
writeOnHistory(peripheral: peripheral, characteristic: characteristic)
} else {
print("no ha cambiado su estado")
}
}
}
6 - Write on peripheral
func writeOnHistory(peripheral: CBPeripheral, characteristic: CBCharacteristic) {
if characteristic.uuid == historicCharacteristicCBUUID {
hasWritenOnHistoric = true
var bitesArray: [UInt8] = [0x01, 0x05]
for _ in 1...16 {
bitesArray.append(0x00)
}
print(bitesArray.count)
print(bitesArray)
let data = Data(bytes: bitesArray)
peripheral.writeValue(data, for: characteristic, type: .withResponse)
}
}
7 - This delegate is only called once and with empty data [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00](in Android is working fine getting all the data from the peripheral)
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let data = characteristic.value {
print(data)
}
}
Is there something I am missing?
Thanks in advance!
You will not observe if your value is changed unless you call didWriteValueFor.
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == myCharacteristic {
self.bluetoothManager.readValueForCharacteristic(characteristic: myCharacteristic)
}
}
I recently upgraded from Swift 3 to Swift 4 and iOS 10.3.3 to iOS 11.1.
I'm developing an application that uses BLE to communicate bi-directionally. The workflow is as follows:
PERIPHERAL - Advertise Identity
CENTRAL - Receive Identity (process it...)
CENTRAL - Respond to peripheral
PERIPHERAL - Receive response from central
Done
My code was working perfectly before the update but now it's not. At the end of step 4, I execute the following line:
peripheral.writeValue(encryptedData!, for: characteristic, type: .withResponse)
This should call the following delegate method but it doesn't:
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
print("Did Write")
print("Error=\(error?.localizedDescription)")
}
It should also (and was calling) the following delegate method on the PERIPHERAL device but it doesn't:
public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
print("did receive write request")
}
The service and characteristic are set as follows :
let prefs = Preferences()
let strServiceUUID = prefs.GetString(key: Preferences.PREF_IDENTITY_SERVICE_UUID, defaultVal: "")!
let strCharacteristicUUID = prefs.GetString(key: Preferences.PREF_IDENTITY_CHARACTERISTIC_UUID, defaultVal: "")!
print("ServiceUUID=\(strServiceUUID)")
print("CharacteristicUUID=\(strCharacteristicUUID)")
mServiceUUID = CBUUID(string: strServiceUUID)
mCharacterUUID = CBUUID(string: strCharacteristicUUID)
mCBBluetoothServices = CBMutableService(type: mServiceUUID, primary: true)
//lets configure the data we want to advertise for
var characteristics : [CBCharacteristic] = []
//let strData : String = "933911"
//let data = strData.data(using: .utf8)
let cbProperties: CBCharacteristicProperties = [.read, .write, .notify]
let cbPermissions: CBAttributePermissions = [.readable, .writeable]
mIdentityObjectCharacteristic = CBMutableCharacteristic(type: mCharacterUUID,
properties: cbProperties,
value: nil,
permissions: cbPermissions)
characteristics.append(mIdentityObjectCharacteristic)
mCBBluetoothServices.characteristics = characteristics
mCBPeripheralManager.add(mCBBluetoothServices)
I am not sure why upgrading the OS and Swift versions broke your code, however, it looks to me like you may be using the wrong delegate method?
Try using this
func peripheral(CBPeripheral, didWriteValueFor: CBCharacteristic, error: Error?)
instead of this
func peripheral(CBPeripheral, didWriteValueFor: CBDescriptor, error: Error?)
Swift 4
For any kind of update characteristic (ex. read/write characteristic), then the didUpdateValueFor delegate will be called.
So, first check in the following delegate methods.
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateValueForChar", characteristic)
if let error1 = error{
alertMSG(titleString: "Error", subTitleString: "Found error while read characteristic data, Plase try again", buttonTitle: "OK")
print(error1)
}
else{
print("Update Characteristic: ", characteristic)
}
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("Write Characteristic :", characteristic)
}
Swift 5
iOS 13
Some things to check:
Make sure to set the peripheral's delegate to whichever controller is conforming to the CBPeripheralDelegate protocol (this should also be the same controller that needs to implement the peripheral(_:didWriteValueFor:error:) method).
Make sure you are not specifying a .withoutResponse write type.
As mentioned by this other answer, there are two very similar delegate methods that have the signature peripheral(_:didWriteValueFor:error:). Make sure you are implementing the correct one.
When writing to a characteristic:
writeValue(_:for:type:):
func writeValue(_ data: Data,
for characteristic: CBCharacteristic,
type: CBCharacteristicWriteType)
peripheral(_:didWriteValueFor:error:)
func peripheral(_ peripheral: CBPeripheral,
didWriteValueFor characteristic: CBCharacteristic,
error: Error?)
When writing to a descriptor:
writeValue(_:for:)
func writeValue(_ data: Data,
for descriptor: CBDescriptor)
peripheral(_:didWriteValueFor:error:)
func peripheral(_ peripheral: CBPeripheral,
didWriteValueFor descriptor: CBDescriptor,
error: Error?)
It is easy to confuse the 2 sets of write and delegate methods.
Since you are using:
peripheral.writeValue(encryptedData!, for: characteristic, type: .withResponse)
The code for the write and delegate pair should be something like this:
class BluetoothController: CBCentralManagerDelegate, CBPeripheralDelegate {
...
func writeSomething(to characteristic: CBCharacteristic, of peripheral: CBPeripheral) {
let something = "1234"
NSLog("Writing \(something) to \(characteristic.uuid.uuidString)")
peripheral.delegate = self // <===== You may have forgotten this?
peripheral.writeValue(something.data(using: .utf8)!,
for: characteristic,
type: .withResponse)
}
...
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
NSLog("Write error: \(String(describing: error))")
} else {
NSLog("Wrote value to \(characteristic.uuid.uuidString)")
}
}
}
I'm currently working on BLE device with CoreBluetooth. I can find my device via CBCentralManagerDelegate and connect with my device.
When I want to discover the characteristics of a service, I can get the correct uuid, however, the value of characteristic is nil. Any ideas?
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if error != nil {
print("ERROR DISCOVERING CHARACTERISTICS: \(error?.localizedDescription)")
return
}
if let characteristics = service.characteristics {
for characteristic in characteristics {
print("--------------------------------------------")
print("Characteristic UUID: \(characteristic.uuid)")
print("Characteristic isNotifying: \(characteristic.isNotifying)")
print("Characteristic properties: \(characteristic.properties)")
print("Characteristic descriptors: \(characteristic.descriptors)")
print("Characteristic value: \(characteristic.value)")
}
}
}
-------------------------------------------------------------------
Characteristic UUID: FA01
Characteristic isNotifying: false
Characteristic properties: CBCharacteristicProperties(rawValue: 26)
Characteristic descriptors: nil
Characteristic value: nil
Another question about properties, according to Bluetooth SIG
Why nRFConnect shows up read, write, notify. But it indeed gets the right value of the characteristic.
read value of characteristics. swift 4
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for newChar: CBCharacteristic in service.characteristics!{
peripheral.readValue(for: newChar)
}