スポンサーリンク

ラズパイによる疑似SDL車載機に、地図を表示してみよう!

SDLおよびSDLアプリ開発を紹介する本連載。第3回は、SDLBOOTCAMPのプロジェクションモードとAndroidを使って、マップ(OpenStreetMap)を表示するアプリの作り方を解説する。


 前回の記事では、Web上で動作するSDL(Smart Device Link)デバイスのシミュレーター「Manticore」を使った、SDLアプリの開発の基本を解説しました。

 「Manticore」でSDLをシミュレートして開発したあとは、実際の車についているナビゲーションディスプレイと同じような環境で、アプリを表示したくなると思います。「SDLBOOTCAMP(SDLブートキャンプ)」をインストールした「Raspberry Pi 3」と、タッチ操作に対応したディスプレイを使えば、SDLアプリをクルマのナビゲーション・ディスプレイに表示した状態を、疑似的に再現できます。

 SDLBOOTCAMPでは、「テンプレートモード」と「プロジェクションモード」のどちらでも表示できます。「テンプレートモード」は、規定されたテンプレートに従ってボタンや画像を配置できます。一方「プロジェクションモード」では、アプリの画面をディスプレイ全体にミラーリング表示したり、マップを表示してオリジナルのナビゲーションを作ったりできます。

 今回は、自由に画面が作れるプロジェクションモードとAndroidを使って、マップ(OpenStreetMap)を表示するアプリを作ります。ドライブが楽しく便利になるアプリ開発しましょう。

 今回説明する内容はこちら。

●SDLBOOTCAMPのセットアップ
●プロジェクションモードでマップを表示
●サンプルコードの解説

事前準備はこちらから

 まず始める前に、必要なものはこちらになります。

Raspberry Pi 3 Model B
Raspberry Pi Touch Display

 他のディスプレイでも動作可能ですが、タッチ動作するディスプレイを購入してください。
●microSDメモリーカード(1GB以上)
●USBケーブル(micro USB Type-B)
●Android端末 バージョン6.0(APIレベル21)以降
Etcher
Android Studio
サンプルコード
OpenStreetMap Japan


 Raspberry Pi 3は、SDLBOOTCAMPの必要機材の項目を参考に準備してください。

 サンプルコードは、Android Studio3.6.3で動作確認しています。SDLのサンプルはJavaで書かれていますが、今回はKotlinを使って開発します。最新のAndroidアプリ開発では、Kotlinが主流になっています。サンプルコードにはJavaファイルも含まれていますが、JavaとKotlinは混在して使用できるため、問題ありません。

 OpenStreetMap(OSM)は、誰でも自由に地図を使えるよう、みんなでオープンデータの地理情報を作るプロジェクトです。プロジェクトには、誰でも自由に参加でき、自由に地図を編集して、自由に利用できます。AndroidでOpenStreetMapを表示するライブラリ(osmdroid)の依存関係については、サンプルコードに含まれています。

 microSDカードは、SDLBOOTCAMPのイメージをバージョンアップしたり設定を変更したりする必要があるため、抜き差しする場合があります。ですので、Rasberry Pi3のケースは、microSDカードが抜き差ししやすいケースを購入することをおすすめします。SDLBOOTCAMPのイメージは、最新のものにバージョンアップしてからご利用ください。

SDLBOOTCAMPをセットアップする

 SDLBOOTCAMPのハードウェア構成例は以下の通りです。Raspberry PiとAndroidはUSBケーブルで接続します。Raspberry Piとディスプレイは、HDMIもしくはDisplay Portで接続します。
 
 ※ディスプレイの接続方法については、そのディスプレイの機種に依存します。場合によってはタッチパネルとUSBケーブルで接続するものもありますので、その場合はそのように接続してください。

 
 下記のフォローチャートに沿って、Raspberry Pi 3を使ったSDLBOOTCAMPの環境を構築します。Raspberry Pi 4では動作しないので、注意してください。


  

 SDLBOOTCAMPの使用中にわからないことが出てきたら、以下のサイトも参考にしてください。詳しい情報が公開されています。

 SDLBOOTCAMP Document


SDLBOOTCAMPのイメージをダウンロードする

 まずはSDLBOOTCAMPのイメージをダウンロードします。こちらのページで、イメージについて確認しましょう。

 
 SDLBOOTCAMPのイメージは、Downlordの下に書かれたリンク(Dropbox)にあります。そこから20190705sdlbootcamp.img.gzをダウンロードしてください。

SDLBOOTCAMPのイメージをmicroSDカードに書き込む

 ダウンロードしたSDLBOOTCAMPのイメージをmicroSDカードに書き込みます。今回は、Etcherを使います。

 初めてイメージを書き込む場合は、先にEtcherをダウンロードしてください(Windows/Mac/Linux版があります)。Mac/Windowsについては操作は同じですが、ここではMac版をメインに説明します。Mac版とWindows版で異なる箇所のみ、Mac版とWindows版の両方を紹介します。

[1] まず、Etcherを起動します。
[Mac版]

 

[Windows版]

 

[2] microSDカードをPCのSDカードスロットに挿入し、[Select Image]ボタンをクリック。ダウンロードした[20190705sdlbootcamp.img.gz]を選択します。
[Mac版]

 

[Windows版]


[3] [Select target]ボタンをクリックし、挿入したmicroSDカードを選択します。


[4] [Flash!]ボタンをクリックします。

 

[Mac版]
管理者のパスワードの入力を要求された場合は、パスワードを入力します

 

[Windows版]
ユーザーアカウント制御の画面が表示された場合は、[はい]をクリックします。


[5] イメージの書き込みが完了するまで待ちます。

 

[6] [Flash Complete!]と表示されれば、イメージの書き込みは完了です。

※ Decompressing完了後に[Cancel] or [Retry]の画面が表示された場合は、[Retry]ボタンをクリックしてください。繰り返し画面が表示される場合は、microSDカードを交換し、再度イメージを書き込んでください。

SDLBOOTCAMPを起動する

 Raspberry Piに、SDLBOOTCAMPのイメージを書き込んだmicroSDカードを挿入します。その後Raspberry Piに電源ケーブルを接続して電源をOnにすると、SDLBOOTCAMPが起動します。

 以下の画面が表示されれば正常にインストールできています。画面が表示されない場合は、Raspberry Piの電源をOffし、各種ケーブルの接続を確認し、再度電源をOnしてください。


 これで「プロジェクションモード」を使う準備が整いました。

プロジェクションモードでマップを表示する

 サンプルコードを使って、下記のフローチャートに沿って、プロジェクションモードでマップを表示します。そして、マップの中心には、現在位置を表す車のピンを表示します。



  

Android Studioでサンプルコードを開く

 ここからサンプルコードを取得します。その後Android Studioを起動し、取得したソースコードのプロジェクト[SDLProjection]を読み込んでください。プロジェクトの読み込みが完了すると、以下のファイルが表示されます。


 プロジェクションモードで表示する処理は、すべてRemoteDisplay.ktに記載されています。今回の説明はすべてこのファイルの中のコードです。

 SDLが公開しているサンプルコードは、SDLライブラリのソースコードを直接指定しています。SDLライブラリは日々開発が進められています。今回のサンプルはプロジェクションモードでマップの表示を体験してもらうことを目的としているため、sdl_androidのバージョンは動作実績のある4.11.1を設定しています。

[build.gradle]

dependencies {
    implementation 'com.smartdevicelink:sdl_android:4.11.1'
}


 今後のSDLライブラリのバージョンアップにより、methodやinterfaceが変更・追加されることがあります。バージョンアップでアプリのソースコードにビルドエラーが発生した場合は、適宜修正してください。

Androidにアプリをインストール

 AndroidとPCをUSBケーブルで接続します。この後でAndroid Stduioからデバッグ版のアプリをインストールするため、Androidは開発者モードに設定してから接続してください。

 [Build Valiant]から[multihighbandwithdebug]を選択します。


 
 [Run App]ボタンをクリックし、Androidへアプリをインストールします。



 PCに接続していたUSBケーブルを外します。このときAndroid側のケーブルは外さないでください。

デバイスにマップを表示

 そして、PCから外したUSBケーブル(Type-A)をRaspberry Pi(USB Type-A)に接続します。以下の写真では、白いUSBケーブルでAndroidとRaspberry Piを接続しています。



 AndroidとRaspberry Piを接続すると、Device Listの画面に切り替わります。



 [Device List]の下のボタンをタップします。少しわかりにくいですが、横に長いダークグレーのボタンです。タップすると、インストールしたアプリ(SDL Display)が表示されます。さらにアプリのボタンをタップルすると、アプリが起動します。



 ディスプレイに、現在位置を中心としたマップが表示されれば成功です。



 ここまででサンプルコードの動作が確認できました。次からはサンプルコードの詳細について説明します。

サンプルコードの解説

 プロジェクションモードを起動する処理は、SdlService.ktに記述されています。プロジェクションモードでの開発においては、プロジェクション画面での操作がメインになるので、ここでは画面のソースコードRemoteDisplay.ktに絞って解説します。
 

プロジェクション画面を構成する

 通常のAndroidと同様に、layoutファイルに記述します。マップにはOpenStreetMapライブラリ(osmdroid)の[org.osmdroid.views.MapView]を指定します。

remote_display.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/baseLayout"
    tools:context=".MainActivity">
 
 
    <org.osmdroid.views.MapView
    android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
 
</LinearLayout>

プロジェクション画面を生成する

 リソースファイルから、マップ(MapView)を取得します。このMapViewは、OpenStreetMapのライブラリorg.osmdroid.views.MapViewであることに注意してください。位置情報を取得するために、ロケーションマネージャを生成します。

RemoteDisplay.kt Line.33

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.remote_display)

    // MapViewを取得
    mapView = findViewById(R.id.mapView)

    // Zoomレベルの設定
    mapView.controller.zoomTo(17.0)

    // ロケーションマネージャ生成
    locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    Configuration.getInstance().load(context.applicationContext, PreferenceManager.getDefaultSharedPreferences(context.applicationContext))

位置情報を取得する

 現在の位置情報が更新されると、onLocationChanged()が呼ばれます。ここでマップの中心位置と車のピンを現在位置に更新します。

RemoteDisplay.kt Line.61

private val locationListener = object : LocationListener {
    override fun onLocationChanged(location: Location?) {
        location?.let {
            // 現在位置のアイコンをオーバーレイ表示
            val geo = GeoPoint(location.latitude, location.longitude)
            setOverlayLocation(geo)
            // 現在位置をマップ中心に設定
            mapView.controller.setCenter(geo)
        }
    }

    override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {

    }

    override fun onProviderEnabled(provider: String?) {

    }

    override fun onProviderDisabled(provider: String?) {

    }
}

マップ上に車のピンを表示する

 マップ上に車のピン(オーバーレイ)を、指定された位置情報で表示します。

RemoteDisplay.kt Line.98

// 現在位置のアイコンをオーバーレイ表示
private fun setOverlayLocation(geo:GeoPoint) {
    overlayLocation?.let {
        itemsLocation.clear()
        mapView.overlays.remove(overlayLocation)
    }

    // 現在位置のアイコン生成
    val icon = resources.getDrawable(R.drawable.car)
    val item = OverlayItem("", "", geo)
    item.setMarker(icon)
    itemsLocation.add(item)

    // オーバーレイ生成
    overlayLocation = ItemizedIconOverlay(
            itemsLocation,
            object : ItemizedIconOverlay.OnItemGestureListener {
                override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
                    return true
                }

                override fun onItemLongPress(index: Int, item: OverlayItem): Boolean {
                    return false
                }
            }, context
    )

    // マップへオーバレイ追加
    mapView.overlays.add(overlayLocation)
}

プロジェクションモードをデバッグする

 一般的なAndroidアプリの開発では、AndroidとPCをUSBケーブルで接続してデバッグします。しかしプロジェクションモードでは、すでにRaspberry PiとAndroidをUSBケーブルで接続しているため、USBケーブルを使えません。

 そこで、AndroidとPCをWi-Fi(TCP/IP)でつないでデバッグします。その際、AndroidとPCは同一ネットワーク上で接続してください。下記にAndroidとPCをWi-Fi(TCP/IP)でつないでデバッグするときの接続図を示します。



 AndroidのWi-Fi設定からIPアドレスを確認します。Macの場合はターミナルを、Windowsの場合はコマンドプロンプトを起動します。ここではターミナル(Mac)で説明しますが、コマンドプロンプト(Windows)でも同じコマンドで操作できます。

 WiFi(TCP/IP)経由のデバッグを有効にします。ポート番号は任意です。ここでは例として50000番を指定します。

$ adb tcpip 50000

 Androidに接続します。192.168.0.5はAndroidのIPアドレスです。

$ adb connect 192.168.0.5:50000

 connected to 192.168.0.5:50000と表示されれば接続完了です。


 Android Studioのデバッグボタンをクリックしてデバッグを開始します。SDLBOOTCAMPに接続したら、[Attach Debugger to Android Process]ボタンをクリック。[Choose Process]画面から[com.machadev.android.sdlprojection]プロセスを選択し、OKボタンをクリックします。



 プロセスがアタッチされ、デバッグできるようになればOKです。一般的なAndroidアプリ開発と同様に、ブレークポイントの設定や変数の参照が可能です。

 Wi-Fi(TCP/IP)経由のデバッグが完了したら、USBケーブル経由のデバッグに戻します。

$ adb usb


[注意] 
 ディスプレイによっては、初期状態でSDLBOOTCAMPと解像度が合っていないものがあります。私が購入したELECROW 7 Inch 1024*600 HDMI LCD Display with Touch Screenは、SDLBOOTCAMPを書き込み後、microSDカード内の[config.txt]に、HDMIの設定が必要でした。ディスプレイ購入後は、マニュアルを一読することをお勧めします。
 

「プロジェクションモード」で新しいナビゲーションのアイデアを

 ここまでで、マップの表示を例にプロジェクションモードを体感してもらいました。今後は、プロジェクションモードを利用したナビゲーション系の開発が多くなると思います。SDLで車の情報を取得してナビゲーションに役立てれば、今よりさらに運転が楽しく安全になるようなものが作れるかもしれません。

 みなさんも、この機会に新しいナビゲーションのアイデアを実現してみましょう。
 

「クルマとスマホをなかよくする SDLアプリコンテスト2020」

主催:SDLアプリコンテスト実行委員会(事務局:角川アスキー総合研究所)
協力:SDLコンソーシアム日本分科会
応募締切:2020年11月4日(水)24:00
募集内容:エミュレーターか開発キット上で開発したSDL対応アプリ(既存アプリのSDL対応、新規開発)
募集対象:年齢、性別、国籍等不問。個人・チームどちらでも応募可
応募方法:プレゼンシートと動作解説動画をWebフォームで応募
審査:審査員が新規性、UX・デザイン、実装の巧みさ等で評価
最終審査会:2020年12月上旬、東京都内で開催予定
グランプリ:賞金50万円+副賞
特別賞(最大5作品):賞金各10万円
公式サイト:http://sdl-contest.com/

元記事