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)
}
}
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.
There is lock that opens via bluetooth. Before working with the lock, you need to send a set of bytes, in response to which the device must return the key. Here is the instruction for compiling a set of bytes:
https://www.dropbox.com/s/z0nsoccz8or4kkf/Omni_OGB1_LOCK_Permenant_connection_smart_lock_Air_Interface_Protocol_V01_03_1%20%281%29.pdf?dl=0
Based on the data from the table of values required to obtain the key, an array of such values was obtained:
FE 43 11 22 19 8A 60 65 7E 5C 46 41 8B F7 4D
Subscribe to the notification and write this set. But after not getting any notification when reading all the uuid get empty arrays
Questions:
1) do I make a byte array to send Correctly?
2) do I subscribe to the changes Correctly?
3) do we read the values Correctly to get answers or a key from the device?
4) How can I get an answer from the lock and pull out the key?
5) How to communicate with this device?
Class code:
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager: CBCentralManager!
var lock: CBPeripheral!
var characteristicNot: CBCharacteristic!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
// Ищем устройство
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let name = peripheral.name {
if name == "LOCK" {
lock = peripheral
lock.delegate = self
manager.stopScan()
manager.connect(lock, options: nil)
}
}
}
// Произошло подключение
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
// Ищем сервисы
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let servicePeripheral = peripheral.services as [CBService]? {
for service in servicePeripheral {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
// Находим сервис
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let characterArray = service.characteristics as [CBCharacteristic]? {
for characteristic in characterArray {
if characteristic.uuid.uuidString == "00002902-0000-1000-8000-00805F9B34FB" {
print("00002902-0000-1000-8000-00805f9b34fb")
}
if characteristic.uuid.uuidString == "0783B03E-8535-B5A0-7140-A304D2495CB8" {
characteristicNot = characteristic
if characteristic.properties.contains(.read) {
print("\(characteristic.uuid): properties contains .read")
}
if characteristic.properties.contains(.notify) {
print("\(characteristic.uuid): properties contains .notify")
}
peripheral.discoverDescriptors(for: characteristic)
peripheral.setNotifyValue(true, for: characteristic)
print("-------------------------")
}
if characteristic.uuid.uuidString == "0783B03E-8535-B5A0-7140-A304D2495CBA" {
if characteristic.properties.contains(.read) {
print("\(characteristic.uuid): properties contains .read")
}
if characteristic.properties.contains(.writeWithoutResponse) {
print("\(characteristic.uuid): properties contains .writeWithoutResponse")
}
let data:[UInt8] = [0xFE, 0x43, 0x11, 0x22, 0x19, 0x8A, 0x60, 0x65, 0x7E, 0x5C, 0x46, 0x41, 0x8B, 0xF7, 0x4D]
let writeData = Data(bytes: data)
peripheral.writeValue(writeData, for: characteristic, type: CBCharacteristicWriteType.withoutResponse)
peripheral.readValue(for: characteristic)
peripheral.readValue(for: characteristicNot)
peripheral.setNotifyValue(true, for: characteristicNot)
print("-------------------------")
}
if characteristic.uuid.uuidString == "0783B03E-8535-B5A0-7140-A304D2495CB9" {
if characteristic.properties.contains(.read) {
print("\(characteristic.uuid): properties contains .read")
}
if characteristic.properties.contains(.notify) {
print("\(characteristic.uuid): properties contains .notify")
}
if characteristic.properties.contains(.writeWithoutResponse) {
print("\(characteristic.uuid): properties contains .writeWithoutResponse")
}
print("-------------------------")
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
if let descriptorsArray = characteristic.descriptors as [CBDescriptor]? {
for descriptor in descriptorsArray {
print(descriptor)
}
}
}
// Читаем значения сервиса
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateValueFor characteristic", characteristic.uuid.uuidString)
print("didUpdateValueFor characteristic value", characteristic.value)
print("didUpdateValueFor characteristic error", error)
if let characteristicData = characteristic.value {
let byteArray = [UInt8](characteristicData)
//let firstBitValue = byteArray[0] & 0x01
print(byteArray)
}
print("-------------------------")
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateNotificationStateFor characteristic", characteristic.uuid.uuidString)
print("didUpdateNotificationStateFor characteristic value", characteristic.value)
print("didUpdateNotificationStateFor characteristic error", error)
print("didUpdateNotificationStateFor characteristic descriptors", characteristic.descriptors)
print("-------------------------")
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("didWriteValueFor characteristic", characteristic.uuid.uuidString)
print("didWriteValueFor characteristic", characteristic.value)
print("didWriteValueFor characteristic", error?.localizedDescription)
print("-------------------------")
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
print("didWriteValueFor descriptor")
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {
print("didUpdateValueFor descriptor")
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var consoleLog = ""
switch central.state {
case .poweredOff:
consoleLog = "BLE is powered off"
case .poweredOn:
consoleLog = "BLE is poweredOn"
manager.scanForPeripherals(withServices: nil, options: nil)
case .resetting:
consoleLog = "BLE is resetting"
case .unauthorized:
consoleLog = "BLE is unauthorized"
case .unknown:
consoleLog = "BLE is unknown"
case .unsupported:
consoleLog = "BLE is unsupported"
default:
consoleLog = "default"
}
}
}
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 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)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for c in service.characteristics!{
print("---Characteristic found with UUID: \(c.uuid) \n")
let uuid = CBUUID(string: "2A19")//Battery Level
if c.uuid == uuid{
//peripheral.setNotifyValue(true, for: c)//Battery Level
peripheral.readValue(for: c)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
/* Battery Level */
if (characteristic.uuid == CBUUID(string: "2A19")) && (characteristic.value != nil){
let value = characteristic.value
let valueUint8 = [UInt8](value!)
print("\(valueUint8)")
print("\(valueUint8[0])")
let batteryLevel: Int32 = Int32(bitPattern: UInt32(valueUint8[0]))
print("\(batteryLevel)")
}
}
I can read Battery Level value, but how to update Battery Level when the value changed?
Why peripheral.setNotifyValue(true, for: c) can't update?