How to write a value to characteristc for BLE Device in ios swift
Asked Answered
N

1

5

I'm working on iOS BLE app to charge a mobile phone. I did everything correct up to discover characteristics. First Scanning peripheral and connected to it. Discover the services(FFB0) and characteristics(FFB1, FFB2) which having notify and write without response properties.

I found the Client Characteristic Configuration Descriptor. I want to send the command to PCB to unlock for charging I want write value to FFB2 characteristics but peripheral doesn't respond. Here is my code.

I have searched everything related to this but I didn't find any solution If anybody gives solution to this problem it will be helpful to me.

This is Document Provided by client:

There is a BLE4.0 Bluetooth module on each of the PCB, each Bluetooth module has a solo 12 bytes length address code, The PCB data channel, Service UUID is OxFFBO, including two eigenvalues, it is OxFFBI and OxFFB2, The length of communication data is 1--20 bytes. OxFFB1 is the channel for APP data download.OxFBB2 is the channel for Bluetooth upload the data.

Power-up into idle mode, when in idle mode, the two lights will flash.it will send an ID code(a byte)to the phone, the APP in the phone will get the ID code(a byte). ID code can be set by phone settings. it means sending the corresponding unlock instruction by ID code.

The unlock instruction is 0x55,0xe1,(ID0+1),time,Following is the explain of unlock instruction:

ID0 is a ID code,"time" is the user unlock time(5-120 min), please pay attention to it is binary-to-hexadecimal conversion, time==05 means 5 min, time==6 means 6 min, time ==A means 10 min ,and so on. If the ID0 of the PCB is 0xff, unlock the pcb 60min is:0x55,0xe1,0x00,0x3c; If the ID0 of the PCB is 0x05, unlock the pcb 10 min is :0x55 , 0xe1 ,0x06 , 0x0a

It will start open output and timing when the MCU on the PCB receives the unlock instruction. The system goes into idle mode again when timing time is out. In idle mode, long press the key goes into shut down sleeping time mode.

The baud rate of uart is set at 9,600. The inquiry command that APP send to MCU is 0x55 0x01 MCU should reply to the APP's information format: 0x55, 0x02, bat_level, xH, XL (xH XL represents the current voltage value of the battery, xH - represents the high 8 bits, and XL--- represents the low 8 bits)

import CoreBluetooth

let batteryServiceCBUUID = CBUUID(string: "FFB0")
let batteryServiceRequestCBUUID = CBUUID(string: "FFB1")
let batteryServiceRequestCBUUID2 = CBUUID(string: "FFB2")

 class ChargingViewController: UIViewController , CBPeripheralDelegate 
 ,CBCentralManagerDelegate {    

  override func viewDidLoad() {
    super.viewDidLoad()
 centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)  
 }
 func centralManagerDidUpdateState(_ central: CBCentralManager) {

    switch central.state {
    case .unknown:
        print("central.state is .unknown")
    case .resetting:
        print("central.state is .resetting")
    case .unsupported:
        print("central.state is .unsupported")
    case .unauthorised:
        print("central.state is .unauthorised")
    case .poweredOff:
        print("central.state is .poweredOff")
    case .poweredOn:


        centralManager.scanForPeripherals(withServices: [batteryServiceCBUUID])

        print("central.state is .poweredOn")
    @unknown default:
        fatalError()
    }

    print("state: \(central.state)")

 }
 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, 
 advertisementData: [String : Any], rssi RSSI: NSNumber) {

    print(peripheral)
    batteryServicePeripheral = peripheral
    batteryServicePeripheral.delegate = self
    centralManager.stopScan()
    centralManager.connect(batteryServicePeripheral)

 }
  func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {

    print("Connected=======>\(String(describing: batteryServicePeripheral))")
    batteryServicePeripheral.delegate = self
    batteryServicePeripheral.discoverServices([batteryServiceCBUUID])

}

func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, 
 error: Error?) {

    print("Fail To Connect=======>\(String(describing: error))")
}


func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: 
CBPeripheral, error: Error?) {

    if error == nil {

        print("Disconnected========>\(peripheral)")
    }else {

        print("Disconnected========>\(String(describing: error))")
    }
 }
// MARK: -  CBPeripheral Delegate Methods

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {

    guard let services = peripheral.services else { return }

    for service in services {

        print("SPAKA:PERIPHERALSERVICES============>\(service)")
        peripheral.discoverCharacteristics(nil, for: service)

    }

}

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

    if let characteristics = service.characteristics {
        //else { return }

        for characteristic in characteristics {

            print(characteristic)


            if characteristic.uuid == batteryServiceCBUUID {

               peripheral.setNotifyValue(true, for: characteristic)

            }

            if characteristic.uuid == batteryServiceRequestCBUUID2 {

                batteryCharacteristics = characteristic


                let str1 = "55e100"
                let data = String(format: "%@%@",str1,hexTimeForChar)

                guard let valueString = data.data(using: String.Encoding.utf8)  else 
    {return}



                peripheral.writeValue(valueString, for: characteristic , type: 
        CBCharacteristicWriteType.withoutResponse)
                print("Value String===>\(valueString.debugDescription)")
                peripheral.setNotifyValue(true, for: characteristic)

            }

        }
    }
    peripheral.discoverDescriptors(for: batteryCharacteristics)
}


func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {

    if error == nil {
        print("Message sent=======>\(String(describing: characteristic.value))")
    }else{

        print("Message Not sent=======>\(String(describing: error))")
    }




}

func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: 
CBCharacteristic, error: Error?) {

    if error == nil {
        print("SPAKA : IS NOTIFYING UPDATED STATE ======>\(characteristic.isNotifying)")
        print("SPAKA : UPDATED DESCRIPTION ======>\(String(describing: 
 characteristic.description))")


    }else{
        print("SPAKA : ERRORUPDATEDNOTIFICATION\(String(describing: error))")
    }


}


func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

    print("SPAKA: UPDATED VALUE RECEIVED========>\(String(describing: characteristic.value))")
    print("characteristic UUID: \(characteristic.uuid), value: \(characteristic.value)")

    guard let str = characteristic.value else { return  }

    if let string = String(bytes: str, encoding: .utf8) {
        print("SPAKA==========>:::\(string)")
    } else {
        print("not a valid UTF-8 sequence")
    }



}

func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {

    guard let desc = batteryCharacteristics.descriptors else { return }


    for des in desc {

        print("BEGIN:SPAKA DESCRIPTOR========>\(des)")
        discoveredDescriptor = des

        print("Descriptor Present Value and uuid: \(des.uuid), value: \(String(describing: des.value))")
        peripheral.readValue(for: discoveredDescriptor)


    }


}

func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
    if let error = error {
        print("Failed… error: \(error)")
        return
    }

    print("Descriptor Write Value uuid: \(descriptor.uuid), value: \(String(describing: descriptor.value))")
}

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {

    if let error = error {
        print("Failed… error: \(error)")
        return
    }

    print("Descriptor Updated Value uuid: \(descriptor.uuid), value: \(String(describing: descriptor.value))")

}
}
Noumenon answered 18/9, 2019 at 4:34 Comment(8)
Have you tired with different BLE device that can respond to your command, if it is then there must be an issue with the BLE device.Tiffie
Thank you @Kampai once i will check with another Ble deviceNoumenon
@Kampai the device is working fine in android. Now im write a value to FFB1 char which having .Notify and .writewithoutresponse properties and its not respondingNoumenon
central.state is .poweredOn state: CBManagerState <CBPeripheral: 0x282651c20, identifier = 956A3BB4-817B-AD68-AFA1-49AD75A07C41, name = songshudiandian, state = disconnected> Connected=======>Optional(<CBPeripheral: 0x282651c20, identifier = 956A3BB4-817B-AD68-AFA1-49AD75A07C41, name = songshudiandian, state = connected>)Noumenon
SPAKA:PERIPHERALSERVICES============><CBService: 0x2802a8140, isPrimary = YES, UUID = FFB0> <CBCharacteristic: 0x283378000, UUID = FFB1, properties = 0x14, value = (null), notifying = NO> Value String===>8 bytes <CBCharacteristic: 0x283378120, UUID = FFB2, properties = 0x14, value = (null), notifying = NO> SPAKA : IS NOTIFYING UPDATED STATE ======>true SPAKA : UPDATED DESCRIPTION ======><CBCharacteristic: 0x283378000, UUID = FFB1, properties = 0x14, value = (null), notifying = YES> SPAKA: UPDATED VALUE RECEIVED========>nilNoumenon
characteristic UUID: FFB1, value: nil UPDATED STATE true <CBCharacteristic: 0x283378120, UUID = FFB2, properties = 0x14, value = (null), notifying = YES> <CBDescriptor: 0x2819dcc30, UUID = Client Characteristic Configuration, value = (null)> Descriptor Present Value : Client Characteristic Configuration, value: nil Descriptor Updated Value: Client Characteristic Configuration, value: Optional(1)Noumenon
These are the my console response.Noumenon
Please help me anybody. idn't know how to solve itNoumenon
S
7

The information about the PDB is somewhat difficult to decipher but contains good information. It sounds as if the PCBs contain a Bluetooth-to-UART module. One characteristic is for sending data to the PCB, one characteristic is for receiving data from the PCB.

I still don't understand the commands that need to be sent to the PCB. If you have access to the Android code, then you can find the answers there.

Below is the minimal code that could potentially:

let batteryServiceUUID = CBUUID(string: "FFB0")
let rxCharacteristicUUID = CBUUID(string: "FFB1")
let txCharacteristicUUID = CBUUID(string: "FFB2")

var centralManager: CBCentralManager!
var batteryPeripheral: CBPeripheral? = nil
var batteryService: CBService? = nil
var txCharacteristic: CBCharacteristic? = nil
var rxCharacteristic: CBCharacteristic? = nil

func centralManagerDidUpdateState(_ central: CBCentralManager) {
    if central.state == .poweredOn {
        centralManager.scanForPeripherals(withServices: [batteryServiceUUID])
    }
}

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    batteryPeripheral = peripheral
    batteryPeripheral!.delegate = self
    centralManager.stopScan()
    centralManager.connect(batteryPeripheral!)
}

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    batteryPeripheral!.discoverServices([batteryServiceUUID])
}

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    peripheral.discoverCharacteristics(nil, for: peripheral.services![0])
}

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    for characteristic in service.characteristics! {
        if characteristic.uuid == rxCharacteristicUUID {
            rxCharacteristic = characteristic
            peripheral.setNotifyValue(true, for: rxCharacteristic!)
        } else if (characteristic.uuid == txCharacteristicUUID) {
            txCharacteristic = characteristic
        }
    }
    sendInitialCommand()
}

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    if let value = characteristic.value {
        print("Data received")
        print(value as NSData)
    }
}

func sendInitialCommand() {
    let cmdBytes: [UInt8] = [0x55, 0xe1, 0x00, 0x0a]
    let cmd = Data(cmdBytes)
    batteryPeripheral!.writeValue(cmd, for: txCharacteristic!, type: .withoutResponse)
}
Soucy answered 18/9, 2019 at 7:3 Comment(15)
let cmd = Data(bytes: cmdBytes) in this command they show errorNoumenon
It shows error like:Incorrect argument label in call (have 'bytes:', expected 'dictionary:')Replace 'bytes' with 'dictionary' . Please help meNoumenon
<CBCharacteristic: 0x282910840, UUID = FFB1, properties = 0x14, value = (null), notifying = YES> Im getting null value for FFB1 CharNoumenon
I've updated my code and fixed the most obvious bugs. It should at least compile now. Regarding the null value. Where do you get that? In peripheral:didUpdateValueFor: it would be surprising. In all other methods it's okay. The value will be null until the device sends data for the first time.Soucy
Ok @Soucy but let cmd = Data(cmdBytes) there is an error and how to get data from device. I mean Ble device sends data to our app??. Even im not getting peripheral:didUpdateValueFor: call back alsoNoumenon
What version of Swift are you using? It affects the Data(...) initialization. And for reading data, you probably have to send the command 0x55, 0x01 first (as described in your question). After that peripheral:didUpdateValueFor:. Do you have access to the Android code? If so, it's all solved there. This is not iOS specific.Soucy
Im using swift5 version . I don't have access android code im new to this companyNoumenon
If you're using Swift 5, let cmd = Data(cmdBytes) will compile. I've verified it. And I strongly recommend you ask for access to the Android code and/or talk to the Android developers about the device. Otherwise you'll waste a lot of time for things that have already been solved. Your biggest challenge is not the iOS code. It's understanding how the devices works, what protocol it uses and how it is mapped to GATT services and characteristics. Obviously, it's not the typical BLE devices with characteristics that can be read at all times. Instead it's a custom protocol on top of GATT.Soucy
Thank you for your suggestion just now i talk with android developer he saying he find peripheral and discover the services, characteristics and for this char he discover descriptor(Client Characteristics Configuration) write a value of {0x01, 0x00} to this descriptor and set notify value to characteristic.After that he write a value (Binary Array) to that char..And the function you just mentioned in above code really not working Its showing "Missing argument label 'dictionary:' in call" error im saying truth only try to understand codo. Im thankful to you for valuable informationNoumenon
func sendInitialCommand() { let cmdBytes: [UInt8] = [0x55, 0xe1, 0x00, 0x0a] let cmd = Data(cmdBytes) batteryPeripheral!.writeValue(cmd, for: txCharacteristic!, type: .withoutResponse) }Noumenon
I can't help you with Data(...) because I cannot reproduce it. And discovering the descriptor and settings its value to { 0x01, 0x00 } is most not needed as it is the same as setNotification:for:. Also see android.googlesource.com/platform/frameworks/base/+/master/core/….Soucy
hi @Soucy today im changing xcode version now im getting Data(..) func but didUpdatevalue for: characteristics didn't get call back the value for txcharacteristic is nil. How can i solve it. Can anybody give suggessions??.Noumenon
From the above description it seems that you first need to send a command to wake up the device (which should be visible from the LEDs). Then you can send a second command to query data. This command will cause a notification on the RX characteristic, which will trigger didUpdateValue:for:. The Android guys should have experience with it. And I'm assuming sendInitialCommand is called.Soucy
Thank you. Just i implemented the demo for Ble its working fine but as you mentioned in above line ididn't get call back of didupdatevalue: For:.just i receive call back of didupdateNotification state:for:Noumenon
I am not able to discover the custom characteristic under Heart Rate service? Can anybody suggest possible reasons?Gerigerianna

© 2022 - 2024 — McMap. All rights reserved.