Android
電子工作
RaspberryPi
AndroidThings

User Driverを使って、電子部品とAndroid Frameworkをつなぐ

AndroidThingsでは、デバイスごとにつけているセンサーやGPS、周辺機器が異なるため、一部のAndroid Frameworkで利用できる機能がデフォルトでは使えなくなっています。

例えばGPSはRaspberry Piには乗っていないため、GPSに関連する機能はデフォルトでは使えません。
ただし、電子部品として追加する分にはGPS等をつけることは可能です。

この時、追加したGPSや温度センサー等をAndroid Frameworkで利用できるようにするのがUser Driverです。
※ドキュメントだと、User-Space Drivers、User Driversと若干表記ゆれがある気がします.

https://developer.android.com/things/sdk/drivers/index.html

User Driverのメリット

主にUser Driverのメリットには以下があります。

  • 移植性
  • 再利用性
  • 統合

色々メリットはありますが、単純に「様々な周辺機器ラップしてAndroid Framework上から触れたほうが、既存のコードやライブラリも使えるし、移植性も高いでしょ」という感じでイメージすればいいと思います。

User Driverの種類

主に3種類あります。

  • GPS
    • GPSです。AndroidのLocation Serviceと統合できます。
  • HID - Human Interface Devices
    • ユーザの入力を扱うDriverです。AndroidのKey EventsやMotion Events(Touchとか)を統合することが出来ます。
    • 例えば物理ボタンとKey Eventsを紐付けたり出来ます。
  • Sensor
    • センサーです。 AndroidのSensorManagerと統合できます。
    • PIOで追加した様々なセンサーをAndroid Framework上(SensorManager)経由で扱う事ができるようになります。

User Driverを試してみる

とりあえず一番簡単なボタンのUser Driverを試してみます。めんどくさいので公式のsample-buttonをそのまま使います。

ハードウェア構成

以下です。

userdriver.png

コード

一応kotlinにして、いらないところやコメントを追加してあります。

MainActivity.kt
package com.github.soundtricker.androidthings.samleuserdriver

import android.app.Activity
import android.os.Bundle
import java.io.IOException
import android.util.Log
import android.view.KeyEvent
import com.google.android.things.contrib.driver.button.Button
import com.google.android.things.contrib.driver.button.ButtonInputDriver
import com.google.android.things.pio.Gpio
import com.google.android.things.pio.PeripheralManagerService


class MainActivity : Activity() {

    companion object {
        private val TAG = MainActivity::class.java.simpleName
        private val GPIO_BUTTON_PIN_NAME= "BCM21"
        private val GPIO_LED_PIN_NAME = "BCM6"
    }

    private var mButtonInputDriver: ButtonInputDriver? = null
    private var mLedGpio: Gpio? = null

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

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

    private fun setupButton() {
        try {
            mButtonInputDriver = ButtonInputDriver(GPIO_BUTTON_PIN_NAME,
                    Button.LogicState.PRESSED_WHEN_LOW,
                    KeyEvent.KEYCODE_SPACE
            )
            mButtonInputDriver!!.register()
        } catch (e: IOException) {
            Log.e(TAG, "failed initialize button driver", e)
            return
        }
    }
    private fun setupLed() {
        try {
            val pioSerivce = PeripheralManagerService()
            mLedGpio = pioSerivce.openGpio(GPIO_LED_PIN_NAME)
        } catch (e: IOException) {
            Log.e(TAG, "failed initialize led", e)
            return
        }
    }


    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn on the LED
            setLedValue(true)
            return true
        }

        return super.onKeyDown(keyCode, event)
    }

    override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn off the LED
            setLedValue(false)
            return true
        }

        return super.onKeyUp(keyCode, event)
    }

    private fun setLedValue(value: Boolean) {
        try {
            mLedGpio!!.value = value
        } catch (e: IOException) {
            Log.e(TAG, "Error updating GPIO value", e)
        }

    }

    private fun destroyButton() {
        Log.i(TAG, "Closing button")
        mButtonInputDriver?.unregister()
        try {
            mButtonInputDriver?.close()
        } catch (e: IOException) {
            Log.e(TAG, "Error closing Button driver", e)
        } finally {
            mButtonInputDriver = null
        }

    }
    private fun destroyLed() {
        try {
            mLedGpio!!.close()
        } catch (e: IOException) {
            Log.e(TAG, "Error closing LED", e)
        } finally {
            mLedGpio = null
        }
    }}


上の

mButtonInputDriver = ButtonInputDriver(GPIO_BUTTON_PIN_NAME,
                    Button.LogicState.PRESSED_WHEN_LOW,
                    KeyEvent.KEYCODE_SPACE
            )
            mButtonInputDriver!!.register()

部分がAndroid FrameworkにUser Driverを登録している部分です。
Android FrameworkとしてUser Driverを登録することで以下のように物理Buttonの押し離しをKey Eventとして、扱う事ができるようになります。

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn on the LED
            setLedValue(true)
            return true
        }

        return super.onKeyDown(keyCode, event)
    }

    override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn off the LED
            setLedValue(false)
            return true
        }

        return super.onKeyUp(keyCode, event)
    }

なお、上記コードを動かすためには、AndroidManifest.xmlに以下を追加する必要があります。

AndroidManifest.xml
<uses-permission android:name="com.google.android.things.permission.MANAGE_INPUT_DRIVERS" />

まとめ

User Driverによって、Androidらくしセンサーなどを扱えるのはAndroid Thingsのメリットだと思います。
多分