swiftswiftuibluetooth-lowenergycore-bluetoothobd-ii

How to iterate through OBD-II write/read requests with CoreBluetooth in Swift


I am trying to continually iterate through my write/read requests to get live data from the OBD-II bus on my vehicle. I am using CoreBluetooth in Swift to connect and communicate with my OBD-II BLE adapter.

My goal is to constantly update the live data for 4 different sensors while the user is in the LiveDataView() and then stop asking if they leave the view, or the app is set to background/is closed.

I first tried to write requests without response using the built in function below. This is supposed to notify the manager when the adapter is ready to receive write requests.

func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
    for item in commands {
        self.write(value: item, characteristic: self.obdSensorCharacteristic!)
        self.read(characteristic: self.obdSensorCharacteristic!)
        print("Sent item \(String(data:item, encoding:.utf8)!) to \(String(describing: obdSensorCharacteristic))\n in the new peripheralIsReady()!")
        
    }
}

This did not work. It just bombarded the adapter with write requests.

Then I tried a timer. I noticed that as I repetitively ask for PID "0105" (Engine Coolant Temperature), everything seemed to work in the debug console.. but it wasn't actually updating my engine coolant temp in the LiveDataView().

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    for characteristic in service.characteristics ?? [] {
        if (String(describing: characteristic.uuid)) == "BEF8D6C9-9C21-4C9E-B632-BD58C1009F9F" {
            obdSensorCharacteristic = characteristic
            peripheral.setNotifyValue(true, for: characteristic)
            print("Found \(characteristic), waiting on values:")
            print("Characteristic Value: \(String(describing: characteristic.value))")
            
            if stopData {
                Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
                    self.requestData()
                    
                    if self.stopData == false {
                        timer.invalidate()
                    }
                })
            }
        }
    }
}

func requestData() {
    if ((obdSensorPeripheral?.canSendWriteWithoutResponse) != nil && sendData) {

        if sendData {
            self.write(value: commands[0], characteristic: self.obdSensorCharacteristic!)
            self.read(characteristic: self.obdSensorCharacteristic!)
            
        }
    }
}

Is there a better way to continually write/read information from the OBD-II BLE device in CoreBluetooth than using a timer? Is there a better way to control write/reads to the peripheral characteristic?


Solution

  • The CoreBluetooth delegates provide you with everything you need to do it in an asynchronous fashion.

    Assuming you are interfacing with a generic serial adapter (such as an ELM327 clone), on top of these delegates you will want to implement an serial command queue that deals with outputting a command and delivering the result asynchronously to your caller.