Android ThingsでPeripheral Driver Libraryを使って、サーボを動かす


せっかくなので短めの内容で、Advent Calendarを埋めていきたいと思います。
今回はAndroid ThingsでPeripheral Driver Libraryを使って、サーボを動かしてみます。

## Peripheral Driver Library

Peripheral Driver Libraryは一般的によく使われるセンサーや、電子部品を、
アプリケーションで使いやすくするためのライブラリ群です。

Peripheral Driver Libraryは
サーボなどのよく使う電子部品を扱いやすいようにラップしたものと、
GPS、Button等をAndroid Frameworkに組み込んで利用するUser Driverが含まれています。

通常、一部のセンサーは、センサーメーカーが、アプリケーションから扱いやすいようにライブラリなどを提供しています。
ただ現状ではメーカーがAndroidThings向けにこういったライブラリを提供していることはなく、
このPeripheral Driver Libraryから探すか、自分で作るかしかありません。

現状Peripheral Driver Libraryはgithubにまとまっています。自分でライブラリを作ったり、
User-Driverを作る場合は参考にしてみてください。

https://github.com/androidthings/contrib-drivers


## pwmservoを利用してサーボを動かす

とりあえずコードは以下にあります。
https://github.com/soundTricker/SampleServo

### 配線

以下の感じです。

![servo.png](https://qiita-image-store.s3.amazonaws.com/0/2965/e86a8946-57a0-7906-5aa2-533bc88af704.png "servo.png")


### build.gradle

Servoを扱うためには、`com.google.android.things.contrib:driver-pwmservo`を利用します。
以下のように`build.gradle`で指定します。

```build.gradle
implementation 'com.google.android.things.contrib:driver-pwmservo:0.3'
```

### AndroidManifest.xml

特に追加のPermissionはありません。
AndroidThings用にuses-libraryとintent-filterを追加します。

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.github.soundtricker.androidthings.sampleservo">

    <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.IOT_LAUNCHER" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>

</manifest>
```

### MainActivity#onCreate

onCreateでServoクラスを初期化します。`setAngleRange`で最大最小の角度を設定。
`setPulseDurationRange`でpwmの周波数の最大値と最小値を設定します。
周波数の最大値、最小値はサーボごとで異なるので、datasheetを御覧ください。
安いところで買うとついてない時もありますが....

またUI Threadを邪魔しないようにHandlerThreadを使います。UIがないならHandlerだけでもいいです。多分。

```kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        try {
            mServo = Servo(PIN_PWM)
            mServo!!.setAngleRange(0.0, 180.0)
            mServo!!.setPulseDurationRange(MIN_PULSE_MS, MAX_PULSE_MS)
            mServo!!.setEnabled(true)
        } catch (e: Exception) {
            Log.e(TAG, "Error creating Servo", e)
            return
        }
        mHandlerThread = HandlerThread("servo")
        mHandlerThread.start()
        mHandler = Handler(mHandlerThread.looper)
        currentAngle.text = "0.0"
        angleText.setText("0.0")
        speedText.setText("5.0")
        setAngle(0.0)
    }

```

### MainActivity#setAngle

角度の設定を行います。
通常PWMサーボだと速度が設定できないので適当な角度ごとにインクリメントして、スピード調整をします。
`mServo!!.angle = targetAngle`だけでサーボを動かせるので楽ですね。
もしServoライブラリを使っていない場合は自分でDutyの計算をする必要があります。


```kotlin
private fun setAngle(angle: Double) {
        mAngle = angle
        mHandler.post(mRunnable)
    }


    private val mRunnable = Runnable {
        if (mServo == null) {
            return@Runnable
        }
        try {
            while (mAngle != mServo!!.angle) {

                var targetAngle: Double
                if (mAngle > mServo!!.angle) {
                    targetAngle = mServo!!.angle + mSpeed
                    targetAngle = Math.min(targetAngle, mAngle)
                } else {
                    targetAngle = mServo!!.angle - mSpeed
                    targetAngle = Math.max(targetAngle, mAngle)
                }

                targetAngle = Math.min(targetAngle, mServo!!.maximumAngle)
                targetAngle = Math.max(targetAngle, mServo!!.minimumAngle)

                mServo!!.angle = targetAngle
                runOnUiThread {
                    currentAngle.text = targetAngle.toString()
                }
                Thread.sleep(1)
            }
        } catch (e: IOException) {
            Log.e(TAG, "Error setting Servo angle")
        }

    }
```

## まとめ

とりあえず今回はサーボを動かしてみました。
まだまだPeripheral Driver Libraryは数が少ないのです。自分が使おうとしているセンサーのライブラリがないことが多々あるので、Peripheral Driver Libraryの実装を参考にしながら作ってみるといいと思います。