androidandroid-studioandroid-thingsandroid-threading

Problem in Uart callback in Android Things Uart


I am using Odroid N2+ board for transmitting and receiving data from UART and turning GPIO ON and OFF. The GPIO part is working alright. When I put the code for transmitting only, there was no problem. But when I put code to receive , the app crashes.

The logcat error I get says

2021-04-14 15:50:47.623 5127-5127/com.example.androidthings E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.androidthings, PID: 5127
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.androidthings/com.example.androidthings.MainActivity}: java.io.IOException: Uart Callback feature is not implemented
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2951)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6718)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
 Caused by: java.io.IOException: Uart Callback feature is not implemented
    at com.google.android.things.pio.impl.UartImpl.registerUartDeviceCallback(UartImpl.java:261)
    at com.example.androidthings.MainActivity.onCreate(MainActivity.kt:85)
    at android.app.Activity.performCreate(Activity.java:7144)
    at android.app.Activity.performCreate(Activity.java:7135)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2931)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:193) 
    at android.app.ActivityThread.main(ActivityThread.java:6718) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

I looked at Uart callback implementation, Handler implementation and HandlerThread implementation and they all look fine but I can't see the source of the problem.

Is there some implementation or somethings that I am missing here ? It looks like UartDeviceCallback is not recognized.

My implementation is as follows:

class MainActivity : AppCompatActivity() 
{
    val TAG = "PeripActivity"
    private lateinit var bind: ActivityMainBinding
    private lateinit var gpio: Gpio
    private lateinit var gpioList: List<String>
    private lateinit var uart: UartDevice
    lateinit var inputThread: HandlerThread
    lateinit var handler: Handler
    private lateinit var pManager: PeripheralManager
    val maxCount = 128

    private val uartDeviceCallback: UartDeviceCallback = object: UartDeviceCallback {
        override fun onUartDeviceDataAvailable(uart: UartDevice): Boolean {
            // Loop until there is no more data in the RX buffer.
            transferUartData()
            // Continue listening for more interrupts
            return true
        }

        override fun onUartDeviceError(uart: UartDevice?, error: Int) {
            Log.w(TAG, "$uart: Error event $error")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        bind = ActivityMainBinding.inflate(layoutInflater)
        setContentView(bind.root)

        pManager = PeripheralManager.getInstance()
        gpioList = pManager.gpioList
        gpio = pManager.openGpio(gpioList[0]).apply {
            setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW)
        }

        var uartlist = pManager.uartDeviceList
        for(item in uartlist)
            Log.d(TAG, "Uarts: $item\n")

        inputThread = HandlerThread("InputThread")
        inputThread.start()
        handler = Handler(inputThread.looper)

        uart = openUart(uartlist[0], 115200)
        uart.registerUartDeviceCallback(handler, uartDeviceCallback)
        // Read any initially buffered data
        handler.post { transferUartData() }

        bind.gpioSwitch.setOnCheckedChangeListener { _, isChecked ->
            if (isChecked) {
                Toast.makeText(this, "GPIO HIGH", Toast.LENGTH_SHORT).show()
                gpio.value = true
            }
            else {
                Toast.makeText(this, "GPIO LOW", Toast.LENGTH_SHORT).show()
                gpio.value = false
            }
        }
        bind.btnUart.setOnClickListener {
            writeUartData("Odroid says hello\n");
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "Loopback Destroyed")

        // Terminate the worker thread
        inputThread.quitSafely()

        // Attempt to close the UART device
        uart.unregisterUartDeviceCallback(uartDeviceCallback)
        uart.close()
    }

    private fun writeUartData(str: String){
        val bytes = str.toByteArray()
        val count = uart.write(bytes, bytes.size)
        Log.d(TAG, "Wrote $count bytes to peripheral")
        Toast.makeText(this, "message send : $str", Toast.LENGTH_SHORT).show()
    }
    private fun openUart(name: String, baudRate: Int): UartDevice {
        return pManager.openUartDevice(name).apply {
            // Configure the UART
            setBaudrate(baudRate)
            setDataSize(8)
            setParity(UartDevice.PARITY_NONE)
            setStopBits(1)
        }
    }
    private fun transferUartData() {
        // Loop until there is no more data in the RX buffer.
        val buffer = ByteArray(maxCount)
        do {
            val read = uart.read(buffer, buffer.size)
            if (read > 0) {
                Log.w(TAG, "received data:\n${buffer}")
            }
        } while (read > 0)
    }
}

Upon looking at the implementation of UartDeviceCallback, the window open with message on top. enter image description here

I chose Download Sources but nothing happened. I am missing any files/libraries over here ?


Solution

  • I solved the problem using Runnable Interface to read the Uart data.

    private lateinit var handler: Handler
    private lateinit var rThread: Thread
    private lateinit var eRunnable: ExampleRunnable
    
    inner class ExampleRunnable (pUart: UartDevice): Runnable{
        val maxlen = 128
        var inneruart = pUart
        val TAG = "Uart_Activity"
    
        @Volatile var threadRunning = true;
    
        override fun run(){
            Log.d(TAG, "Uart receive start")
            while(threadRunning){
                readUart()
            }
        }
    
        private fun readUart() {
            // Loop until there is no more data in the RX buffer.
            val buffer = ByteArray(maxlen)
            do {
                val read = inneruart.read(buffer, buffer.size)
                if (read > 0) {
                    val sub = buffer.copyOfRange(0,read)
                    var readString = String(sub)
                    Log.w("Uart_Activity", "received data bytes ($read):\n${readString}")
                    handler.post(object : Runnable{
                        override fun run() {
                            var cat:String = bind.peripheralMsg.text.toString() + readString
                            bind.peripheralMsg.text = cat
                        }
    
                    })
                }
            } while (read > 0)
        }
    
        fun setRunning(state: Boolean){
            threadRunning = state
        }
    }
    

    In the above code,

    bind.peripheralMsg.text = cat
    

    display received data in UI.

    In the main program, in onCreate function, I did as follows:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Other stuffs
    
        handler = Handler(Looper.getMainLooper())
        eRunnable = ExampleRunnable(uart)
        rThread = Thread(eRunnable)
        rThread.start()
    
        // Other stuffs
    
    }
    
    override fun onStop() {
        super.onStop()
        eRunnable.setRunning(false)
    }