Search code examples
swiftbluetooth-lowenergycore-bluetoothios-bluetooth

I'm trying to receive indicate via BLE connection, but I can't receive any data


I am creating an app for BLE connection for iOS. I can connect to the peripheral from central (iPhone6: iOS12.9) and send commands with I am able to send commands with writevalue.

https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518949-setnotifyvalue

In the above setNotifyValue, there is a description that seems to be accepted by indicate. The following methods of didUpdateValueFor do not return.

    /// When changing the characteristic
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518708-peripheral

If you know how to implement receiving data in indicate, please let me know.

The sample code is shown below. I'm still working on it, so there may be some garbage code, sorry.

//  ViewController.swift

import UIKit
import CoreBluetooth
import os

class ViewController: UIViewController {

    /// https://qiita.com/eKushida/items/def628e0eff6c106d467

    var serviceUUID : CBUUID!
    var characteristicUUID : CBUUID!
    var responseCharacteristicUUID : CBUUID!

    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!
    
    var writeCharacteristic: CBCharacteristic!
    var responsCharacteristic: CBCharacteristic!

    var data = Data()
        
    @IBOutlet weak var dispLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
        dispLabel.text = "Startup"
    }
    /// Initialize the central manager and UUID
    private func setup() {
        // Create an object representing the UUID.
        self.serviceUUID = CBUUID(string: "XXXXX0000-XXXX-XXXX-XXXX-XXXXXXXXXX")
        self.characteristicUUID = CBUUID(string: "XXXX2001-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
        self.responseCharacteristicUUID = CBUUID(string: "XXXX2000-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
     }

    /// Pairing process
    @IBAction func scan(_ sender: UIButton) {
        print("Pairing process")
        dispLabel.text = "Pairing process pressed"
        
        self.centralManager = CBCentralManager(delegate: self, queue: nil)
    }
    
    /// Communication connection
    @IBAction func connect(_ sender: UIButton) {
        print("Communication connection")
        
        /// https://qiita.com/MashMorgan/items/32500f158cb08d565786
        /// https://knkomko.hatenablog.com/entry/2019/07/16/013443
        let message = "**COMMAND**"
        let command = message + "\r"
        let writeData = Data(command.utf8)
        print("writeData:" + String(data: writeData, encoding: .utf8)!)
        
        peripheral.writeValue(writeData, for: writeCharacteristic, type: CBCharacteristicWriteType.withResponse)
    }
    

}

//MARK : - CBCentralManagerDelegate
extension ViewController: CBCentralManagerDelegate {
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {

        switch central.state {

        //wait for power on and scan
        case CBManagerState.poweredOn:
            let services: [CBUUID] = [serviceUUID] ///serviceUUID
            centralManager.scanForPeripherals(withServices: nil, options: nil)
            // centralManager.scanForPeripherals(withServices: services, options: nil)
            print("isScanning:" + String(centralManager.isScanning))
        default:
            break
        }
    }
    
    /// Called when a peripheral is discovered
    func centralManager(_ central: CBCentralManager,
                        didDiscover peripheral: CBPeripheral,
                        advertisementData: [String : Any],
                        rssi RSSI: NSNumber) {

        self.peripheral = peripheral
        
        print("peripheral.name:" + String(peripheral.name ? "") + " peripheral.id:" + peripheral.identifier.uuidString)
        
        if "XXXXXXX" == peripheral.name {
            //start connection
            self.centralManager.connect(self.peripheral, options: nil)
            //peripheral is found, stop scanning
            centralManager.stopScan()
        }
    }
    
    /// called when connected
    func centralManager(_ central: CBCentralManager,
                        didConnect peripheral: CBPeripheral) {
        print("Connection successful serviceUUID:" + serviceUUID.uuidString)
        peripheral.delegate = self
        peripheral.discoverServices([serviceUUID])
        dispLabel.text = "Peripheral connection successful"
    }
    
    /// Called when the connection fails
    func centralManager(_ central: CBCentralManager,
                        didFailToConnect peripheral: CBPeripheral,
                        error: Error?) {
        print("Connection failed")
    }
    
    /// When disconnected
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        print("Disconnection: \(String(describing: error))")
    }
}

//MARK : - CBPeripheralDelegate
extension ViewController: CBPeripheralDelegate {

    /// Called when the characteristic is found
    func peripheral(_ peripheral: CBPeripheral,
                    DidDiscoverCharacteristicsFor service: CBService,
                    error: Error?) {

        if error ! = nil {
            print(error.debugDescription)
            return
        }
        
        guard let serviceCharacteristics = service.characteristics else {
           // error handling
           return
       }
        // Processing by characteristic
        for characreristic in serviceCharacteristics {
            if characreristic.uuid == characteristicUUID
            {
            // keep the characteristic for writing data
                self.writeCharacteristic = characreristic
                print("Write characreristic / UUID:" + characreristic.uuid.uuidString)
                print("Write characreristic / properties: \(self.writeCharacteristic.properties)")
                continue
            }
            if characreristic.uuid == responseCharacteristicUUID {
                peripheral.setNotifyValue(true, for: characreristic)
                self.responsesCharacteristic = characreristic
                print("Responses characreristic / UUID:" + characreristic.uuid.uuidString)
                print("Responses characreristic / properties: \(self.responsesCharacteristic.properties)")
                continue
            }
            print("Other characreristic / UUID:" + characreristic.uuid.uuidString)
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverIncludedServicesFor: CBService, error: Error?){
        print("peripheral didDiscoverIncludedServicesFor")
    }

    /// When writing data to the characteristic (called when sending a command)
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        print("peripheral didWriteValueFor")
        guard error == nil else {
            print("Error when writing characteristic data: \(String(describing: error))")
            // failure handling
            return
        }
        print(characteristic.value)
        
    }
    
    func peripheral(peripheral: CBPeripheral,
        didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic,
        error: NSError?)
    {
        print("peripheral didUpdateNotificationStateForCharacteristic")
        if let error = error {
            print("Notify state update failed.... .error: \(error)")
        } else {
            print("Notify state update succeeded! isNotifying: \(characteristic.isNotifying)")
        }
    }
    
    func peripheral(peripheral: CBPeripheral,
        didUpdateValueForCharacteristic characteristic: CBCharacteristic,
        error: NSError?)
    {
        print("peripheral didUpdateValueForCharacteristic")
        if let error = error {
            print("Data update notification error: \(error)")
            return
        }
        print("Data update! value: \(characteristic.value)")
    }
    
    /// When changing the characteristic
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        print("peripheral didUpdateValueFor")
        guard error == nil else {
            print("Error getting/changing characteristic value: \(String(describing: error))")
            // failure handling
            return
        }
        guard let data = characteristic.value else {
            print("characteristic.value")
            // failure process
            return
        }
        // data will be passed to us
        print(data)
    }
}

Solution

  • I have example iOS projects (Central & Peripheral) which send/receive indications: https://github.com/alexanderlavrushko/BLEProof-collection

    setNotifyValue here is called similarly as you do, it should be fine.

    I suggest to check the way how the characteristic is created and updated on the Peripheral side, iOS example link.

    Also there is a great iOS application LightBlue which can simulate a BLE device, see this guide:

    • Central - the topic "Subscribing to Characteristics" might be useful
    • Peripheral - "Adding a New Virtual Peripheral", but use Blank device and configure services/characteristics you need