Android Things on Raspberry Pi3 - UART and Bluetooth Coexistence -

B!
スポンサーリンク

目次

Development environment for this article

  • Android Things: 1.02
  • Android Studio: 3.1.3
  • Kotlin: 1.2.41
  • buildToolsVersion@27.0.3
  • compileSdkVersion:27
  • minSdkVersion: 26
  • targetSdkVersion:27

Overview

I wanted to control external devices using UART and BLE(Bluetooth), respectively, but both UART/BLE do not work well even if UART0 and BLE are started normally.

When the status of BluetoothAdapter.isEnable was dumped to the debug console, it was false, and BLE could not be started.

The state remains false when bluetoothadapter.enable() is run.

I did various researches on Stack Overflow etc. and found the Raspberry Pi3 function matrix page of Android Things official.

If you have Android Things on the Raspberry Pi3, it seems that enabling or disabling peripheral features may affect the use of other features.

Raspberry Pi3 Feature Matrix - Android Things Official Site -

Failure to use UART0/Bluetooth simultaneously

If you try to use UART0 or BLE at the same time, each of the following failures occurs:

  • UART output is forced to change to 460800bps (guess from time per bit of waveform)
  • BLE is disabled (still disabled when enable is performed)

The waveform measured 1 bit time is as follows.

The Raspberri Pi3's UART function internally has two UART features of UART0 and MINIUART (pin is the same BCM14/BCM15), and you must use MINIUART instead of UART0 when used with BLE.

UART0 and MINIUART have different performance, but at 115200bps, I was able to communicate without any problems.

When I measured the waveform, the clock accuracy seemed to be slightly different, but it was acceptable.

UART Settings Precautions

In general, there are precautions when using UART with raspberry Pi3.

Voltage is 3.3V

The UART voltage of Raspberry Pi3 is 3.3V.

When communicating with 5V devices, use voltage conversion.

Removing debug console settings

By default, UART0 is assigned for the debug console.

When using UART for communication with external devices, it is necessary to change the code of CMDLINE.TXT on the OS written on the SD card.

Insert the SD card into your PC to open CMDLINE.TXT in a text editor and remove the "console=serial0,115200".

dwc_otg.lpm_enable=0 console=serial0,115200 ro rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait earlyprintk init=/init androidboot.hardware=rpi3 androidboot.selinux=permissive loglevel=3
dwc_otg.lpm_enable=0 ro rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait earlyprintk init=/init androidboot.hardware=rpi3 androidboot.selinux=loglevel=3

UART0 and MINIUART output waveforms

It was measured with an oscilloscope.

Communication speed[bps] 115200
Size[bit] 8
Parity Without
Number of stop bits 1

UART0 TxD Waveform

Approx. 972us when transmitted 9 bytes.

1 bit time.Communication speed error is probably measurement error.

Approx. 8.68us/bit.(115207.37bps/bit)

MINIUART TxD Waveform

Approx. 983us.

It is about 11us longer than UART0, but it has no effect on communication.

By the way, I measured 1bit time, but the image is omitted because it was the same as UART0 in the range of 2us/div.

Source code

Paste the source code that confirmed the operation when UART and BLE coexist.

The language of logic part is Kotlin.

AndroidManifest.xml

com.google.android.things.permission.USE_PERIPHERAL_IO to use UART, android.permission.BLUETOOTH and  android.permission.BLUETOOTH_ADMIN and android.permission.ACCESS_FINE_LOCATION to use Bluetooth requires permission.

<?xml version="1.0" encoding="utf-8"?>https://gourmet-technology-crypto.jp/wp-admin/post.php?post=369&action=edit#
<manifest package="jp.cryptocat.shareuartandble"
          xmlns:android="http://schemas.android.com/apk/res/android">

	<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />
	<uses-permission android:name="android.permission.BLUETOOTH"/>
	<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

	<application>
		<uses-library android:name="com.google.android.things"/>

		<activity android:name=".MainActivity">
			<intent-filter>
				<action android:name="android.intent.action.MAIN"/>

				<category android:name="android.intent.category.LAUNCHER"/>
			</intent-filter>
			<intent-filter>
				<action android:name="android.intent.action.MAIN"/>

				<category android:name="android.intent.category.DEFAULT"/>
			</intent-filter>
		</activity>
	</application>

</manifest>

MainActivity.kt

UART and BLE logic are written together in MainActivity.

package jp.cryptocat.shareuartandble

import android.app.Activity
import android.bluetooth.*
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.util.Log
import com.google.android.things.pio.PeripheralManager
import com.google.android.things.pio.UartDevice
import com.google.android.things.pio.UartDeviceCallback
import java.util.*

private val TAG = MainActivity::class.java.simpleName

class MainActivity : Activity() {
    // UART
    private var mPeripheralManager : PeripheralManager = PeripheralManager.getInstance()
    private var mUartDevice : UartDevice? = null

    //Bluetooth
    private var mBluetoothManager : BluetoothManager? = null
    private var mBluetoothAdapter: BluetoothAdapter? = null
    private var bleGatt: BluetoothGatt? = null
    private var bleIsEnabled = false
    private var bleService : BluetoothGattService? = null
    private var bleCharacteristic : BluetoothGattCharacteristic? = null
    private val bleServiceUuid : String = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    private val bleCharacteristicUuid : String = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    var mBluetoothGattDescriptor : BluetoothGattDescriptor? = null
    private var bleConnectionChangedCallback : (() -> Unit)? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        openUart()
        openBle()
    }

    private fun openUart(){
        try {
            if (mUartDevice == null) {
                // Output UART name that can be used with Raspberry Pi 3 to console.
                val device = mPeripheralManager.uartDeviceList
                device.forEach { Log.i("UART", it.toString()) }

                // open MINIUART.
                mUartDevice = mPeripheralManager.openUartDevice("MINIUART")
                Log.i("UART", "open:" + mUartDevice?.name)
            }

            mUartDevice?.setBaudrate(115200)
            mUartDevice?.setDataSize(8)
            mUartDevice?.setParity(UartDevice.PARITY_NONE)
            mUartDevice?.setStopBits(1)
            mUartDevice?.registerUartDeviceCallback(onReceiveUart)
        }
        catch (e : Exception){
            Log.e("UART","UART Open Error:" + e.toString())
        }
    }


    // UART receive event
    private var onReceiveUart = object : UartDeviceCallback {
        override fun onUartDeviceDataAvailable(uart : UartDevice?) : Boolean {
            Log.i("UART", "Received")
            try {
                var data = byteArrayOf(0x53, 0x45)
                mUartDevice?.write(data, data.size)
            } catch (e: Exception) {

            }
            return true
        }
        override fun onUartDeviceError(uart : UartDevice?, error : Int){
            Log.w("UART", "onUartErrorEvent:" + uart.toString() + error)
        }
    }

    private fun openBle(){
        mBluetoothManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        mBluetoothAdapter = mBluetoothManager?.adapter

        // Register event when Bluetooth status changes
        val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
        registerReceiver(bluetoothBroadcastReceiver, filter)

        // Enable when Bluetooth is disabled
        if (mBluetoothAdapter?.isEnabled == false) {
            mBluetoothAdapter?.enable()
        }
        else {
            // start scan.
            mBluetoothAdapter?.bluetoothLeScanner?.startScan(bleScanCallback)
        }
        Log.i("BLE", "mBluetoothAdapter?.isEnabled=" + mBluetoothAdapter?.isEnabled.toString())

    }

    private val bluetoothBroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
            Log.i("BLE", "mBluetoothAdapter?.isEnabled=" + mBluetoothAdapter?.isEnabled.toString())

            when (state) {
                BluetoothAdapter.STATE_ON -> {
                    // start scan .
                    mBluetoothAdapter?.bluetoothLeScanner?.startScan(bleScanCallback)
                }
                BluetoothAdapter.STATE_OFF -> {
                    mBluetoothAdapter?.enable()
                }
            }
        }
    }

    private val bleScanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult?) {
            super.onScanResult(callbackType, result)
            Log.d("BLE","onScanResult")

            // Check that the scanned device supports BLE.
            if ((result?.device?.type != null) && (result?.device?.type!! >= 2)) {
                Log.i("BLE", "type=" + result?.device?.type.toString() + " ... 0:unknown, 1:classic, 2:ble, 3:dual")

                if (result?.device?.name != null) {
                    Log.i("BLE", "name=" + result?.device?.name)
                    // Connect if the device found during scanning is the intended name, and specify the BluetoothGattCallback to be called after the connection in the third argument.
                    val regex = Regex(result?.device?.name!!)
                    if (regex.containsMatchIn("BLE_Device") == true) {
                        result?.device?.connectGatt(applicationContext, false, gattCallback)
                    }
                }
            }
        }

        private val gattCallback = object : BluetoothGattCallback() {
            override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
                super.onConnectionStateChange(gatt, status, newState)
                // Execute when the connection status changes.
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    Log.d("BLE", "onConnectionStateChange STATE_CONNECTED")
                    if (gatt?.device?.name != null) {
                        Log.i("BLE", "Success! Connect to " + gatt?.device?.name!!)
                    }

                    // If the connection is successful, search for the service.
                    gatt?.discoverServices()
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    Log.d("BLE", "onConnectionStateChange STATE_DISCONNECTED")
                    // 接続が切れたらGATTを空にする.
                    bleGatt?.close()
                    bleIsEnabled = false

                    bleConnectionChangedCallback?.invoke()
                }
            }

            // characteristic change event.
            override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
                super.onCharacteristicChanged(gatt, characteristic)
                if (gatt != null) {
                    Log.i("BLE", "onCharacteristicChanged " + "gatt=" + gatt?.toString())
                }
            }

            // characteristic write event.
            override fun onCharacteristicWrite(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
                super.onCharacteristicWrite(gatt, characteristic, status)
                Log.i("BLE", "onCharacteristicWrite: status=" + status.toString())

            }

            // characteristic read event.
            override fun onCharacteristicRead(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
                super.onCharacteristicRead(gatt, characteristic, status)
                Log.i("BLE", "onCharacteristicRead: status=" + status.toString())

                if (characteristic != null) {
                    bleCharacteristic = characteristic
                }
            }

            override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
                super.onServicesDiscovered(gatt, status)
                Log.d("BLE", "service discover state = $status")
                // Execute when Service is found.
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    // Check if the UUIDs are the same.
                    bleService = gatt?.getService(UUID.fromString(bleServiceUuid))
                    if (bleService != null) {
                        // 指Confirm that it is Characteristic with the specified UUID.
                        bleCharacteristic = bleService?.getCharacteristic(UUID.fromString(bleCharacteristicUuid))
                        if (bleCharacteristic != null) {
                            // Update BluetoothGatt if UUID of Service, Characteristic is correct.
                            bleGatt = gatt

                            // Start data transmission when connection is completed.
                            bleIsEnabled = true
                            Log.i("BLE", "GATT Update")

                            // Change notification setting of characteristic.
                            bleGatt?.setCharacteristicNotification(bleCharacteristic, true)
                            mBluetoothGattDescriptor = bleCharacteristic!!.descriptors[0]
                            mBluetoothGattDescriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                            bleGatt?.writeDescriptor(mBluetoothGattDescriptor)

//                        // end scan.
//                        Log.d(TAG, "call stop scan")
//                        mBluetoothManager.adapter.bluetoothLeScanner.stopScan(bleScanCallback)
                        }
                    }
                }
            }
        }
    }



    override fun onDestroy() {
        super.onDestroy()
    }

}
スポンサーリンク
最新の記事はこちらから