Kotlin OBDII Bluetooth communication: Channel is unrecoverably broken and will be disposed

63 Views Asked by At

Im having some issues with retrieving data from my OBDII Adapter. The issue is divided into 2 parts, which maybe are related to each other. I want to mention at the beginning that the issue should not be related to the Adapter since it worked a few weeks ago. It gave me correct values and did only crash a few times at the beginning. Since i did some changes it started to not work at all.

Issue 1 The first issue is that i get weird data from my Adapter. Im retrieving Vehicle Speed and Engine RPM with the Kotlin OBD API from Elton Viana.

Issue 2 The second issue is that after starting to retrieve data my app crashes after a few seconds with the error message: channel 'a7785b2 com.anonymous.ecodrive/com.anonymous.ecodrive.EcoLiveActivity (server)' ~ Channel is unrecoverably broken and will be disposed! I cant find much information about this issue. Some mention it could be memory leaks, but i can't find the issue after many days of analysis and testing.

UPDATE I solved the first issue. I learned that the Adapter is always resetting itself after taking it off the car. I made the initialization part of the connecting process, so it always has the correct settings. The second issue is partially still there. I found out that the reason for the first app crash is, that the first respond of my Adapter always includes something like "BUS INIT:". This message cant be handled by the OBD API so the App crashes. So after putting the Adapter in the first crash is expected. Generally the second time connecting everything works, but sometimes i need a few more tries, which i cant understand. Also rarely the app crashes during driving, so after im getting correct data for a while. I guess its somehow related to memory leaks again, but im not sure. Still the app is now working much better and quite usable as a prototype. I hope this information will help others in their work.

This is the way im connecting to my Adapter in MainActivity through the method ConnectToDevice():

    private fun pairedDeviceList(){
        m_pairedDevices = m_bluetoothAdapter.bondedDevices
        val list: ArrayList<BluetoothDevice> = ArrayList()
        if(m_pairedDevices.isNotEmpty()){
            for (device: BluetoothDevice in m_pairedDevices){
                list.add(device)
                Log.i("device", "" + device)
            }
        }
        else {
            Toast.makeText(activity, "keine gekoppelten Geräte gefunden", Toast.LENGTH_SHORT).show()
            return
        }

        val deviceNames = list.map { it.name }.toTypedArray()

        activity?.let {
            AlertDialog.Builder(it)
                .setTitle("Gekoppelte Geräte")
                .setItems(deviceNames) { dialog, which ->
                    val selectedDevice: BluetoothDevice = list[which]
                    m_address = selectedDevice.address
                    val name: String = selectedDevice.name
                    BluetoothHandling.ConnectToDevice(requireActivity(),requireActivity(), selectedDevice).execute()
                    dialog.dismiss()
                }
                .show()
        }
    }

The ConnectToDevice class in BluetoothHandling.kt looks like the following:

class ConnectToDevice(private val activity: Activity, c: Context, device: BluetoothDevice) : AsyncTask<Void, Void, String>(){
        private var connectSuccess: Boolean = true
        private val context: Context
        private val dev = device
        private lateinit var m_progress: ProgressDialog
        init {
            this.context = c
        }

        override fun onPreExecute() {
            super.onPreExecute()
            m_progress = ProgressDialog.show(context, "", "Connection will be established...")
        }

        @SuppressLint("MissingPermission")
        override fun doInBackground(vararg  p0: Void?): String? {
            try {
                if (m_bluetoothSocket == null || !m_isConnected){
                    m_bluetoothSocket = dev.createRfcommSocketToServiceRecord(randomUUID)
                    m_bluetoothSocket!!.connect()
                    inputStream = m_bluetoothSocket!!.inputStream
                    outputStream = m_bluetoothSocket!!.outputStream
                    obdConnection = ObdDeviceConnection(inputStream, outputStream)
                }
            }
            catch (e: IOException){
                Log.w("data","Did not work!")
                connectSuccess = false
                e.printStackTrace()
            }
            return null
        }

        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            m_progress.dismiss()
            if(!connectSuccess){
                Log.i("data", "CONNECTION FAILED")
                Toast.makeText(context,"Connection Failed", Toast.LENGTH_SHORT).show()
            }
            else {
                m_isConnected = true
                Log.i("data", "CONNECTION SUCCESS")
                val intent = Intent(context, EcoLiveActivity::class.java)
                context.startActivity(intent)
                activity.finish()
            }
        }
    }

After that the EcoLiveActivity starts, where the User should starts the repeatRequests() Function by pressing the startStopBtn. The relevant Code looks like this:

   private fun repeatRequests() {
        if (ecoLiveActive.value == true && m_isConnected) {
            CoroutineScope(Dispatchers.IO).launch {
                if (m_bluetoothSocket != null) {
                    requestVehicleSpeed()
                    requestEngineRPM()
                }
            }
                handler.postDelayed({ repeatRequests() }, 500L)
        }
    }

        private suspend fun requestVehicleSpeed() {
        try {
            val response = obdConnection.run(SpeedCommand())
            val output = response.value
            Log.i("data", "Speed: $output")
            currentSpeed = output.toInt()
            viewModel.updateCurrentSpeed(currentSpeed)
        } catch (e: IOException) {
            Log.w("data", "Requesting vehicle speed didn't work!")
            e.printStackTrace()
        }
    }

    private suspend fun requestEngineRPM() {
        try {
            val response = obdConnection.run(RPMCommand())
            val output = response.value
            currentRPM = output.toInt()
            viewModel.updateCurrentRPM(currentRPM)
            Log.i("data", "RPM: $output")
            withContext(Dispatchers.Main) {
                rpmBar.speedTo(currentRPM.toFloat())
            }
        } catch (e: IOException) {
            Log.w("data", "Requesting engine speed didn't work!")
            e.printStackTrace()
        }
    }

The Logging output has some weird values for RPM and Vehicle Speed as shown following: Error Log

0

There are 0 best solutions below