電子工作や製品のプロトタイピング (例『地球規模で遠隔操作できるブルドーザー』) で利用される Raspberry Pi 3 について、 Android Things アプリケーションを開発できます。
本ページでは、簡単な例として LED を点灯させるアプリケーションを扱います。より実用的なアプリケーションを開発する際には GitHub (Android Things) のサンプルコードを参考にします。
関連する公式ドキュメント
- Raspberry Pi 3 のセットアップ、コンソールの使用方法
- 制御プログラムの開発
- APK ファイルの作成
Raspberry Pi 3 で Android を動かして Wi-Fi 接続
Raspberry Pi 3 に Android Things イメージを書き込み、Wi-Fi 設定を行う方法は環境によって異なります。公式ドキュメントにすべての場合について記載がありますが、特にここでは以下の場合を想定した例を記載します。
- macOS から書き込む。
- モバイル Wi-Fi ルータのためイーサネットケーブルが利用できない。
- HDMI ディスプレイが手元にない。
必要なものは以下のとおりです。
- 本体
- Raspberry Pi 3
- Raspberry Pi 3 電源用 USB ケーブル
- 書き込み先
- microSD カード (8 GB 以上)
- microSD カードリーダー/ライター
- 通信ケーブル
- USB to TTL Serial Cable (3.3v、イーサネットケーブルが利用できる場合は不要です)
Android Things イメージのダウンロード
Android Things Console に Google アカウントでログインして「CREATE A PRODUCT」ボタンをクリックします。
情報を入力します。SOM (System on Module) type は Raspberry Pi 3 を選択します。また、OEM パーティションには Bundle とよばれるアプリケーション関連ファイル一式が格納されます。
登録した Product ページ内の「CREATE BUILD CONFIGURATION」をクリックすると、ページ内下部の Build configuration list に一行追加されます。
追加された行内の Download build をクリックすると zip ファイルのダウンロードが開始されます。少々時間がかかるため、待ちます。
SD カードへの書き込み
解凍
ダウンロードした zip ファイルを解凍します。unzip コマンドではエラーが出るため、The Unarchiver (Mac OS) を利用します。約 250 MB の zip ファイル myproduct_Raspberry Pi 3_0_OIR1.170720.015_userdebug_build.zip
が解凍されて 4 GB 程の iot_rpi3.img
になります。
dd コマンド
8 GB 以上の microSD カードを Mac に認識させた後、デバイスの識別番号を確認します。以下の例では disk3
が microSD カードです。
$ diskutil list
/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *121.3 GB disk0
1: EFI EFI 209.7 MB disk0s1
2: Apple_CoreStorage 120.5 GB disk0s2
3: Apple_Boot Recovery HD 650.0 MB disk0s3
/dev/disk1
#: TYPE NAME SIZE IDENTIFIER
0: Apple_HFS Macintosh HD *120.1 GB disk1
Logical Volume on disk0s2
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Unencrypted
/dev/disk2
#: TYPE NAME SIZE IDENTIFIER
0: Apple_partition_scheme *20.0 MB disk2
1: Apple_partition_map 32.3 KB disk2s1
2: Apple_HFS Flash Player 20.0 MB disk2s2
/dev/disk3
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme *7.9 GB disk3
1: Windows_FAT_16 RECOVERY 2.2 GB disk3s1
2: Linux 33.6 MB disk3s5
3: Windows_FAT_32 boot 66.1 MB disk3s6
4: Linux 5.6 GB disk3s7
アンマウントします。
$ diskutil unmountDisk /dev/disk3
Unmount of all volumes on disk3 was successful
以下のコマンドで img を書き込みます。時間がかかるため気長に待ちます。
$ sudo dd bs=1m if=iot_rpi3.img of=/dev/rdisk3 conv=sync
4352+0 records in
4352+0 records out
4563402752 bytes transferred in 452.560177 secs (10083527 bytes/sec)
書き込みが終わると Mac からは認識できないためポップアップが表示されますが、そのまま取り出して Raspberry Pi にセットします。
シリアルデバッグコンソールによる Raspberry Pi への接続
今回、モバイル Wi-Fi ルータを利用しており、有線 LAN で Raspberry Pi に接続できない場合を想定しているため、初期の Wi-Fi パスワード設定等を別の方法で行う必要があります。ここでは、公式ドキュメントにも記載のあるシリアルデバッグコンソールを利用します。接続のためのケーブルは Amazon 等で安価に入手できます。電気街が近場にある方は実際の店舗で購入するのもよさそうです。その際、TTL 電圧レベルは 5V ではなく 3.3V のものを選択することに注意します。
デバイスドライバのインストール
OS によって USB to TTL Serial Cable のデバイスドライバをインストールする必要があります。上記 Amazon リンクのケーブルは PL2303 です。Adafruit の商品ページにドライバのインストール方法が記載されています。例えば、macOS について以下のように記載されています。
- こちらのページ にアクセスしてドライバをダウンロードする。
- ドライバには
Prolific Chipset
とSiLabs CP210X Drivers
の二種類あり、どちらか分からない場合は両方インストールしておけば問題ない。 Prolific Chipset
については macOS のバージョンによってダウンロードすべきドライバのバージョンが異なるため注意する。
ただし、これらのドライバは OS のバージョンによってはシステムのクラッシュにつながります。特に Prolific Chipset
のドライバは不具合報告がなされており、以下のコマンドでドライバを削除できます。
保存場所の確認
$ kextfind -b com.prolific.driver.PL2303
/Library/Extensions/ProlificUsbSerial.kext
適当なディレクトリに移動
$ sudo mv /Library/Extensions/ProlificUsbSerial.kext ~/Desktop/
PC 再起動
$ sudo reboot
Raspberry Pi と接続するピン
以下のページの画像と見比べて作業するなどし、接続するピンを間違えて Raspberry Pi を壊さないように注意します。
黒 → GND ピン、白 → TXD ピン、緑 → RXD ピンにそれぞれ接続します。赤 (5V) は接続しません。
シリアル通信
デバイスドライバのインストールに成功していれば、USB ケーブルを PC に接続すると以下のコマンドで識別子を確認できます。以下の例では /dev/tty.usbserial
と表示されています。
$ ls -l /dev/tty.*
crw-rw-rw- 1 root wheel 18, 0 8 24 02:17 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw- 1 root wheel 18, 2 8 24 02:17 /dev/tty.Bluetooth-Modem
crw-rw-rw- 1 root wheel 18, 4 8 24 02:31 /dev/tty.usbserial
macOS の場合、screen を利用して以下のコマンドで接続できます。何も表示されない場合はエンターキーを押してみます。
screen /dev/tty.usbserial 115200
以下のように表示されれば接続成功です。
rpi3:/ $
別ターミナルから以下のコマンドで pid を確認して終了できます。環境によってはドライバが合わず、ここで正常にデバイスを解放できない可能性があります。別のドライバをインストールするか、後述の Wi-Fi 設定を行ってしまい、シリアル通信は以降利用しないようにして回避します。
screen -ls
screen -S 1983 -X quit
Wi-Fi 認証情報の設定
以下のコマンドで Wi-Fi 認証情報を設定します。特殊な記号がパスワードに含まれる場合は passphrase64 を利用します。
rpi3:/ $ am startservice -n com.google.wifisetup/.WifiSetupService -a WifiSetupService.Connect -e ssid 利用するSSID名 -e passphrase パスワード
Starting service: Intent { act=WifiSetupService.Connect cmp=com.google.wifisetup/.WifiSetupService (has extras) }
ログに Successfully connected to
と記載されていることを確認します。途中で NTP に接続できたため時刻が現在時刻に変更されていることが確認できます。
rpi3:/ $ logcat -d | grep Wifi | tail
01-01 00:43:52.083 876 876 V WifiWatcher: SSID changed: "SSID_NAME"
01-01 00:43:52.083 876 893 I WifiConfigurator: Successfully connected to SSID_NAME
01-01 00:43:52.089 309 378 E WifiStateMachine: Did not find remoteAddress {192.168.179.1} in /proc/net/arp
01-01 00:43:52.112 309 378 E WifiVendorHal: getSupportedFeatureSet(l.856) failed {.code = ERROR_NOT_AVAILABLE, .description = }
01-01 00:43:52.122 309 378 D WifiNetworkAgent: NetworkAgent: Received signal strength thresholds: []
01-01 00:43:52.125 309 378 E WifiVendorHal: stopRssiMonitoring(l.2115) failed {.code = ERROR_NOT_AVAILABLE, .description = }
01-01 00:43:52.924 309 378 D WifiStateMachine: NETWORK_STATUS_UNWANTED_VALIDATION_FAILED
08-23 17:47:13.903 309 378 W AlarmManager: Unrecognized alarm listener com.android.server.wifi.WifiConfigStore$1@ad923b1
08-23 17:47:13.965 309 378 D WifiConfigStore: Writing to stores completed in 61 ms.
08-23 17:47:14.647 309 540 D WificondControl: Scan result ready event
時刻が同期されていることを確認します。
rpi3:/ $ date
Wed Aug 23 17:49:07 GMT 2017
シリアル通信中は ping コマンドを実行するために root 権限が必要です。8.8.8.8
は Google Public DNS です。
rpi3:/ $ su
rpi3:/ # ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=55 time=197 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=55 time=121 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=55 time=120 ms
ip コマンド等で IP アドレスを確認しておきます。以下の例では 192.168.179.7
がポケット Wi-Fi ネットワーク内の IP です。
rpi3:/ $ ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
link/sit 0.0.0.0 brd 0.0.0.0
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether b8:27:eb:79:1e:23 brd ff:ff:ff:ff:ff:ff
inet 192.168.179.7/24 brd 192.168.179.255 scope global wlan0
valid_lft forever preferred_lft forever
inet6 fe80::ba27:ebff:fe79:1e23/64 scope link
valid_lft forever preferred_lft forever
4: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether b8:27:eb:2c:4b:76 brd ff:ff:ff:ff:ff:ff
Wi-Fi 設定をリセットするためには以下のコマンドを実行します。
am startservice -n com.google.wifisetup/.WifiSetupService -a WifiSetupService.Reset
adb コマンドによる接続
Wi-Fi で無線接続できるようになれば、以降はシリアルデバイスコンソールを利用せずに adb コマンドを利用して Raspberry Pi に接続できます。
Android Studio で SDK と同封されてインストールされる adb (Android Debug Bridge) コマンドを利用すると Android 端末に shell で接続できます。「Preferences → Appearance & Behavior → System Settings → Android SDK → Android SDK Location」で SDK の場所を確認して PATH を通しておきます。
$ /Users/username/Library/Android/sdk/platform-tools/adb connect 192.168.179.7
connected to 192.168.179.7:5555
$ /Users/username/Library/Android/sdk/platform-tools/adb devices
List of devices attached
192.168.179.7:5555 device
$ /Users/username/Library/Android/sdk/platform-tools/adb shell
Android Studio で LED 点灯アプリケーションを開発
SDK tools および SDK のアップデート
Android Studio をインストールします。その後、インストールされている「SDK tools」と「SDK Platforms」を SDK Manager でアップデートします。
空プロジェクトを作成
新規プロジェクトを作成します。
アプリケーション名を入力します。
2017/08/19 現在のところ Android Things 専用の項目はなく、Phone and Tablet を選択します。
Empty Activity を選択します。
Activity Name は MainActivity
のままでも問題ありませんが、ここでは Android Things のドキュメントで散見される HomeActivity
に変更しています。また、UI を持たないアプリケーション開発を想定しているため Layout File 生成のチェックは外します。更に、モバイルアプリケーション開発で必要になる後方互換性は不要であり Homeactivity は AppCompatActivity ではなく Activity を直接継承すればよいため二つ目のチェックも外しておきます。
Android Things アプリ化するための設定を追加
2017/08/19 現在のところ Android Things 専用の項目はなく、Phone and Tablet で生成された内容を流用しているため、Android Things アプリ化するために以下の箇所を手動で設定する必要があります。
app/build.gradle
アプリケーションのルートディレクトリに存在する build.gradle ではなく app ディレクトリ直下の build.gradle です。Bintray / androidthings-supportlibrary で確認できる最新のバージョンを指定して追記します。外部 JAR に依存しているだけでありコンパイル時に APK ファイルに含める必要はないため compile
ではなく provided
で設定します。
apply plugin: 'com.android.application'
android {
...
}
dependencies {
....
provided 'com.google.android.things:androidthings:0.5-devpreview' ←追記
}
app/src/main/AndroidManifest.xml
build.gradle で provided
設定した外部 JAR を実行時に参照するための classpath 設定を uses-library
で行います。また、Android Things デバイスブート時に起動される Activity であることをインテントフィルタで設定しています。
...
<manifest ...>
<application ...>
<uses-library android:name="com.google.android.things" /> ←追記
<activity android:name=".HomeActivity">
<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>
GPIO 制御処理の記述
汎用の入出力ピンである GPIO ピンを制御して LED を一定間隔で点灯させます。
app/src/main/java/com/example/mycompany/myapp/HomeActivity.java
package com.example.mycompany.myapp;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import com.google.android.things.pio.Gpio;
import com.google.android.things.pio.PeripheralManagerService;
import java.io.IOException;
public class HomeActivity extends Activity {
// ログ出力で使用するタグ名
private static final String TAG = "HomeActivity";
// LED の点滅間隔
private static final int INTERVAL_BETWEEN_BLINKS_MS = 1000;
// LED 用の出力ピン識別子
// https://developer.android.com/things/hardware/raspberrypi-io.html
private static final String LED_PIN_NAME = "BCM6";
// main スレッドとは別のスレッド、およびそのスレッドで順番に実行するためのキューを有します。
// https://developer.android.com/reference/android/os/Handler.html
private Handler mHandler = new Handler();
// GPIO を表現するオブジェクト
private Gpio mLedGpio;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 周辺機器との接続を管理するオブジェクト
PeripheralManagerService service = new PeripheralManagerService();
try {
// LED 点灯用の GPIO ピンをオープン
mLedGpio = service.openGpio(LED_PIN_NAME);
// 出力ピンとして設定、電圧の初期状態は 0V
mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
// LED 点灯処理を別スレッド実行用のキューに積みます。
mHandler.post(mBlinkRunnable);
} catch (IOException e) {
Log.e(TAG, "Error on PeripheralIO API", e);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 終了時は別スレッド実行用のキューを空にします。
mHandler.removeCallbacks(mBlinkRunnable);
// GPIO ピンを閉じます。
if (mLedGpio != null) {
try {
mLedGpio.close();
} catch (IOException e) {
Log.e(TAG, "Error on PeripheralIO API", e);
}
}
}
// 別スレッドで実行する LED 点灯処理です。
private Runnable mBlinkRunnable = new Runnable() {
@Override
public void run() {
// 何らかの原因で GPIO が閉じられている場合はそのまま処理を終えます。
if (mLedGpio == null) {
return;
}
try {
// 「点灯」と「消灯」をトグルします。
mLedGpio.setValue(!mLedGpio.getValue());
Log.d(TAG, mLedGpio.getValue() ? "ON" : "OFF");
// 一定時間経過してから実行する設定で再びキューに積みます。
mHandler.postDelayed(mBlinkRunnable, INTERVAL_BETWEEN_BLINKS_MS);
} catch (IOException e) {
Log.e(TAG, "Error on PeripheralIO API", e);
}
}
};
}
アプリケーションの配信 (OTA アップデート)
Wi-Fi 接続の設定を行う際に Raspberry Pi 3 に書き込んだイメージは Factory Image とよばれます。Factory Image は Bundle と Android Things OS をそれぞれコンソール上で選択して「CREATE BUILD CONFIGURATION」することで設定が生成されます。先程は空の Bundle を利用したことになります。
Android Things におけるアプリケーションは Bundle ファイルとしてまとめられます。新規 Bundle の配信は OTA (over-the-air) にインターネット経由で行うことができます。LED 点滅アプリケーションを Bundle 化してコンソールにアップロードして配信方法を設定できます。
ただし、Android Things 内の update_engine
が更新を確認する周期が 2017/08/20 現在のところ 5 時間とされており時間がかかるため、本ページではアップロードした Bundle と Android Things OS で新規に Factory Image を生成して、再度 SD カードに書き込むことで動作確認することにします。
Bundle ファイルについて
Bundle 内に最低限必要なファイルは IOT_LAUNCHER
が AndroidManifest.xml
で設定された Activity を含んだ APK ファイルのみです。これを zip 化すれば動作確認に必要な Bundle ファイルが得られます。
APK ファイルの作成
APK ファイルのビルド種別について、既定ではデバッグビルドとリリースビルドの二つがあります。また、ビルド方法には Android Studio で行う方法とコマンドで行う方法があります。ここでは簡単のため、以下のコマンドでデバッグビルドを行います。
cd ~/AndroidStudioProjects/MyApp/
./gradlew assembleDebug
生成された apk ファイルを確認します。
$ find . -name *.apk
./app/build/outputs/apk/app-debug.apk
適当なディレクトリにコピーして zip 化します。
mkdir ~/Desktop/myapp-20170820
cp app/build/outputs/apk/app-debug.apk ~/Desktop/myapp-20170820/
cd ~/Desktop/
zip -r myapp-20170820 myapp-20170820
これをコンソールからアップロードして、再度 Factory Image を作成して SD カードに書き込みます。ちなみに、2017/08/20 現在コンソールが対応しておらず、作業で使用していた Firefox ではアップロードできなかったため Google Chrome でアップロードしました。
Raspberry Pi をブレッドボードで LED と接続して動作確認
ソースコードで LED 用の GPIO を LED_PIN_NAME = "BCM6"
と設定したため、以下のようにブレッドボード上で適当な抵抗と LED を直列につなぎます。抵抗や LED に限らず、電子工作でよく使用するパーツや工具は手元に揃えておくと便利です。
Ground ピン --- 180 Ω 抵抗 --- LED --- BCM6 ピン
接続するピンを間違えて Raspberry Pi を壊さないように注意します。以下のページの画像と見比べて作業するとよさそうです。
電源を入れてしばらく待つとアプリが起動して LED が点滅し始めます。
Android Studio サンプルプロジェクトの導入 (補足)
Google Developers Japan / Android Things Developer Preview 5 に記載のとおり、Android Studio の File → New → Import Samples から "Things" で検索することで、サンプルプロジェクトをダウンロードして試すことができます。
関連記事
- Spring Security フォームログインのサンプルコードSpring フレームワークによる Web アプリケーション開発で、ログイン処理を実装する際は Spring Security が便利です。ここでは特に Spring Boot で Web アプリケーションを開発する場合を対象とし、フォームによる ID/Password ログインを行うためのサンプルコードをまとめます。 公式ドキュメント [Spring Security チュートリアル](http...
- Java配列の宣言方法 (C/C++との違い)Javaの配列 Javaの配列宣言方法はC/C++と似ているようで若干異なる。 初期化しない場合 C/C++の int array[10]; はJavaでは int array[] = new int[10]; となる。同様にC/C++の int array[3][3]; はJavaでは int array[][] = new int[3][3]; となる。 初期化
- PlantUML による UML 図の描き方PlantUML はテキスト形式で表現されたシーケンス図やクラス図といった UML (Unified Modeling Language) 図の情報から画像を生成するためのツールです。簡単な使い方をまとめます。 インストール方法の選択 Atom や Eclipse のプラグインをインストールしてエディタから利用する方法、JAR をダウンロードして Java コマンドで実行する方法、Redmine ...
- Akka HTTP サンプルコード (Scala)Akka アクターを用いて実装された汎用 HTTP フレームワークです。Spray の後継です。コアモジュールである akka-http-core は 2016/2/17 に experimental が外れました。akka-http などのいくつかのサブモジュールは 2016/3/1 現在 experimental のままですが、基本的な
- Kestrel の使用例Kestrel は Message Queue (MQ) の実装のひとつです。一般に MQ はアプリケーション間やプロセス間、スレッド間で非同期に通信するために用いられます。メッセージの送信側は MQ に書き込めば受信側の応答を待たずに次の処理に非同期に進むことができます。Kestrel はわずか 2500 行程の Scala で実装されており JVM で動作します。MQ 自体はメモリ上に存在する...