Google Home と Android ThingsでLチカする
今回はGoogle Home と Android ThingsでLチカする方法を解説します。
Google Homeとは
Google HomeとはGoogleが開発したスマートスピーカーです。2016年11月にアメリカで発売されましたがしばらく日本では買うことができず、2017年10月にようやく日本でも発売されました。AmazonのスマートスピーカーであるEchoに対抗してか、セールも行われ思わず買ってしまった人も多いのではないでしょうか。 Google Homeはタッチパネルやキーボードなどの入力装置は持たず、基本的に話しかけることで操作します(一応、本体上部を触って音量の操作などができる)。話しかけることで天気や交通情報を教えてくれたり、音楽を再生したりすることができます。
Android Thingsとは
次にAndroid ThingsですがこちらもGoogleが開発したもので、IoT向けのプラットフォームとして発表したOSなどを含むソフトウェア群です。IoT機器向けの開発を行う場合、通常は組込機器向けのシステム設計が必要ですが、Android Thingsがその辺りのことを隠蔽してくれるので組込エンジニア(組込系の会社)以外もIoT機器関連の開発に手が出しやすくなるということがAndroid Thingsを使うメリットの一つではないでしょうか。
Android Thingsはいくつかのデバイスに対応していますが、日本ではRaspberry Pi3(ARMプロセッサを搭載したシングルボードコンピュータ)を購入するのが1番確実です。
今回の登場人物たち
さて、本題に入っていきますがまずは今回の登場人物たちを整理しておきます。
- Google Home
- Android Things with ブレッドボード / LED
- IFFFT
- Firebase Realtime Database
大まかな流れとしては以下の図の通りです。裏方にIFFFTとFirebase Realtime Databaseを使うことでお手軽にGoogle HomeとAndroid Thingsを連携させることができます。
Lチカ、つまりLEDをチカチカさせるということで半田ごてを使った電子工作なイメージを思い浮かべるかもしれませんが、今回ハンダ付けは必要ありません。ブレッドボードという穴の空いた実験用の基盤を使うことでハンダ付けをせずに穴にLEDやジャンパ線などを差し込むだけで完成します。
Android Things単体でLチカする
まずはAndroid Things単体でLチカしてみましょう。ここでは前述したRaspberry Pi3にAndroid Thingsがインストール済みであることを前提とします。インストールの仕方などはこのあたりを参考にしてください。
crossbridge-lab.hatenablog.com
プロジェクトを作成する
Android Things向けアプリのプロジェクトを開発する際も通常のAndroidアプリと同様にAndroid Studioを使います。いつも通り新規プロジェクト作成の画面に進み、1番下のAndroid Thingsにチェックを入れることでAndroid Things向けアプリのプロジェクトを作成することができます。
Activityの種類を選ぶ画面ではEmptyActivityの方を選びます。
Android Things向けに作成した場合に通常のAndroidアプリと異なる点はdependenciesに以下の記述があることです。これによりAndroid Things向けのクラス群を使うことができるようになります。逆にそれ以外はほぼAndroidアプリと同じであり、通常のAndroidアプリ開発を行うときと同じように開発を行うことができます。
compileOnly 'com.google.android.things:androidthings:+'
PeripheralManagerServiceを使ってGPIOを操作する
突然でてきたGPIOという単語ですが、General-purpose input/outputの略で日本語では「汎用入出力」となり、簡単に言うと他のデバイスに入力にも出力にも使える便利なデジタル信号の出入口です。今回は出力として使い、LEDに電力を流すことで点灯させるという使い方をします。入力として使うとセンサーを繋いで温度や明るさなどを得るという使い方ができます。
Raspberry Pi3にはいくつかのGPIOがあります。Raspberry Pi3自体の仕様書を見てもよいですが、GoogleもRaspberry Pi3のピン配置を公開しておりアプリから利用する名前も記載されているのでこちらを見るのが良いでしょう。
GPIOを使う際は PeripheralManagerService
というクラスを使います。gpioList
メソッドでGPIOの一覧が取得できるのでログに出力してみます。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // GPIOのリストを表示 val service = PeripheralManagerService() for (gpio in service.gpioList) { Log.d(TAG, "gpio -> $gpio") } }
次のように利用できるGPIOの名前が出力されます。今回は BCM17(11番ピン)
と BCM27(13番ピン)
を使うことにします。
12-19 13:25:08.257 1819-1819/? D/ContentValues: gpio -> BCM10 12-19 13:25:08.257 1819-1819/? D/ContentValues: gpio -> BCM11 12-19 13:25:08.257 1819-1819/? D/ContentValues: gpio -> BCM12 12-19 13:25:08.257 1819-1819/? D/ContentValues: gpio -> BCM13 〜〜以下省略〜〜
それでは実際にGPIOを利用します。手順は以下の通りです。
- 利用するGPIOの名前を指定して
open
する - GPIOを出力で利用するのか入力で指定するのか指定する。出力の場合は初期値も合わせて指定する。
- 値を設定する
- 最後に忘れずに
close
する
先ほどのコードの後に2つのGPIOの初期設定を行います。PeripheralManagerServiceクラスの openGpio
メソッドに利用するGPIOの名前を指定します。Gpioクラスが取得できるので、setDirection
メソッドで DIRECTION_OUT_INITIALLY_LOW (出力/初期値LOW)
と設定します。onDestroyで忘れずに close
メソッドを呼び出します。これを忘れるとアプリを終了しても他のアプリでこれらのGPIOを操作することができなくなってしまいます。
val RED_LED_NAME = "BCM17" val YELLOW_LED_NAME = "BCM27" var redGPIO: Gpio? = null var yellowGPIO: Gpio? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // GPIOのリストを表示 val service = PeripheralManagerService() for (gpio in service.gpioList) { Log.d(TAG, "gpio -> $gpio") } // GPIOにアクセスできるようにする try { redGPIO = service.openGpio(RED_LED_NAME) yellowGPIO = service.openGpio(YELLOW_LED_NAME) // GPIOの設定を出力、初期値LOWにする redGPIO?.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW) yellowGPIO?.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW) } catch (e: IOException) { Log.e(TAG, "error", e) } } override fun onDestroy() { super.onDestroy() try { redGPIO?.close() yellowGPIO?.close() } catch (e: IOException) { Log.e(TAG, "error", e) } }
これでGPIOを利用する準備が整いました。ゴールはGoogle Homeから操作することですが、まずは画面にSwitchを表示してSwitchをオン/オフしたらLEDが点灯/消灯するようにします。Gpioクラスのvalueにture/falseを設定することで出力のオン/オフとなります。その結果、LEDが点灯/消灯します。これでGPIOの操作は完璧です。(GPIOを入力で使う場合はアクティブローとアクティブハイの設定などが必要になりますがまたの機会に)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) binding.switchRed.setOnCheckedChangeListener{view, isChecked -> if (isChecked) { redGPIO?.value = true } else { redGPIO?.value = false } } binding.switchYellow.setOnCheckedChangeListener{view, isChecked -> if (isChecked) { yellowGPIO?.value = true } else { yellowGPIO?.value = false } }
IFFFTとFirebase(Realtime Databese)を使ってGoogle HomeとAndroid Thingsを連携する
Android Things単体でLチカができるようになったので次はGoogle Homeから操作できるようにします。Google Homeとの連携は今回はIFFFTを使います。 GoogleのDialogflow https://dialogflow.com/ と比較して細かい設定ができないことや、タイムラグが大きいことなど欠点もありますが簡単に使うことができるよう大きなメリットがあります。
Firebaseのプロジェクトを作成してデータベースを準備する
IFFFTからWebhookでデータベースの値を変更し、AndrodThingsではその値の変化を監視します。Firebaseのプロジェクトを作成します。プロジェクトを作成したら直下に red
と yellow
というキーを作っておきます。
本来はよろしくないのですがセキュリティ(ルール)の設定を変更します。IFFFTからWebhookで書き込みできるように認証なしでアクセスできるルールとします。この設定にするとURLを知っていれば自由に読み書きできてしまうので注意してください。実際に何か実用的なものを作って運用する際にはセキュリティを意識しましょう。
{ "rules": { ".read": true, ".write": true } }
これで https://hoge.firebaseio.com/red.json
https://hoge.firebaseio.com/yellow.json
というアドレスでデータを書き込むことができるようになりました。(hogeの部分は各自作成したプロジェクトに置き換えてください)
IFFFTのAppletを作成する
トリガーとなるthisにはGoogle Assistantレシピを設定します。Google Assistantレシピの中の Say a phrase with a text ingredient
を選択し、What do you want to say に 赤色 $
と入力します。これは 赤色
という言葉をトリガーとし、それに続く言葉を変数としてtahtで使うようにすることを意味します。
トリガーを受けて動作するアクションであるthatにはWebhooksを設定します。URLにはFirebaseのURL https://hoge.firebaseio.com/red.json
を入力し、PUTでbodyに "{{TextField}}"
を入力します。
これを黄色用も同じように作成します。さて、これでGoogle Homeに「赤色(黄色) ほげほげ」と話しかけるとほげほげの部分がFirebaseのデータベースに保存されるようになりました。
アプリでRealtime Databaseの値を監視する
最後にアプリでRealtime Databaseの値を監視し、その値によってLEDを点灯/消灯させる処理を実装します。Firebaseの公式ドキュメントの手順通りにbuild.gradleへの追記とjsonファイルを配置してFirebaseを扱えるようにしておきます。
onCreateでGPIOの設定をした後に次のように記述します。キー red
の値が変化した際に、バリューを見てLEDを点けるか消すかの判断をしています。
// Database val database = FirebaseDatabase.getInstance() val redRef = database.getReference("red") redRef.addValueEventListener(object: ValueEventListener { override fun onCancelled(p0: DatabaseError?) { } override fun onDataChange(snapshot: DataSnapshot?) { val value = snapshot?.getValue(String::class.java) Log.d(TAG, " $value") val word = value?.replace(" ", "") when (word) { "オン", "音", "つけて" -> { redGPIO?.value = true binding.switchRed.isChecked = true } "オフ", "消して" -> { redGPIO?.value = false binding.switchRed.isChecked = false } } } })
ポイントはバリューの文字列からスペースを除いている点です。今回「つけて」と発音したときに「つけ て」とスペースが入ってきたためです。
また、「オン」と発音しても「音」と期待通りの文字列が入って来なかったためwhenで分岐させる際の値として「音」も指定しています。音声入力は期待通りの読みで入ってくるとは限らないためできるだけ多くの文字列で判断した方が良いでしょう。
今回はとりあえずLEDを点灯させただけですが、赤外線LEDを使ってエアコンのリモコン代わりにしたり、センサーと組み合わせたりとかアイデア次第で使い方が広がると思います。皆さんも組み込みの開発の深い知識がなくても手軽に始めることができるAndroid Thingsを活用してみては如何でしょうか。
最後にデモしてる動画は置いておきます。