Raspberry Pi用Androidアプリ
前回のIoTチュートリアルでは、Raspberry Piのセットアップ方法と、Back4App APIを使ってParse Serverに接続し、オブジェクトをサーバーに保存したり、クエリやライブクエリを実行する方法について説明しました。
ここでは、Raspberry側で行ったことをアプリ側で再現する方法を説明します。このチュートリアルでは、先に設定したIoTデバイスと対話するためのAndroidアプリについて説明します。Parse サーバー側では、オブジェクトの書き込み、クエリーとライブクエリーの実行など、Raspberryと同じタスクを行います。これらの機能は、IoTアプリケーションを開発する予定がない場合でも役立つ可能性があります!
Parseは、IoTアプリケーションを作成するための素晴らしいフレームワークであることが証明されています。2020年には、GraphQL APIプロトコルが追加され、さらに優れたデータ取得方法が提供されます。
私たちは、あなたが望むアプリケーションを開発するための第一歩としてコードを提供します。
Contents
前提条件
唯一の前提条件は、私たちのAndroid QuickStartチュートリアルを完了することです。そのためには、以下のリンクをクリックしてください。
https://www.back4app.com/docs/pages/android/how-to-build-an-android-app-on-back4app
このIoTシリーズのチュートリアルのセクション3で必要となるLiveQueryのチュートリアルです。事前に完了する必要はありません。
https://docs.back4app.com/docs/android/live-query/
チュートリアルを通して不明な点があれば、以下のリンクにあるAndroid用の公式Parseガイドに従って解決してください。
http://docs.parseplatform.org/android/guide/
Section 1: アプリの作成とBack4Appとの接続の基本
このプロジェクトでは、クラス名を “MainActivity.java “とします。基本的なコードは以下の通りです。
public class MainActivityextends AppCompatActivity { @オーバーライドする 。
オーバーライド protected void onCreate(Bundle savedInstanceState) { 次のようにします。 super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
// アプリにツールバーを挿入します。このバーの詳細については、menu_main_activity.xmlファイルを参照してください。 ツールバー toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); }
オーバーライド public boolean onCreateOptionsMenu(Menu menu) { // メニューを展開します。 // メニューが存在する場合は、アクションバーに項目を追加します。 getMenuInflater().inflate(R.menu_main_activity,menu); trueを返します; }
オーバーライド public boolean onOptionsItemSelected(MenuItem item) { // アクションバーアイテムのクリックをここで処理します。 // アクションバーのアイテムクリックを処理します。アクションバーは アクションバーは、AndroidManagementで親アクティビティを指定している限り、 // 自動的にHome/Upボタンのクリックを処理します。 // AndroidManifest.xmlで親アクティビティを指定する限り。 int id = item.getItemId();
// 検査なし SimplifiableIfStatement if (id == R.id.action_settings) { //親アクティビティを指定する。 trueを返す; }
return super.onOptionsItemSelected(item); } }
以下のインポートを追加する必要があるかもしれない:
以下のインポートを追加する必要があるかもしれない; import android.support.design.widget.FloatingActionButton;import android.support.design.widget.FloatingActionButton.Import import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity;import android.support.v7.app.AppCompatActivity import android.support.v7.widget.Toolbar.Toolbar;import android.view.View; import android.view.View import android.view.ビュー;import android.view.メニュー import android.view.Menu.メニュー import android.view.MenuItem; import android.widget.EditText; import android.widget.Button; import android.widget.ToggleButton.インポート する; android.view.View.OnClickListenerをインポート します;
レイアウトXMLファイルに、以下のコードを追加してください:
<?xml version="1.0" encoding="utf-8"? <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/myCoordinatorLayout" tools:context="com.example.guilherme.myiotapplication.MainActivity">
<includeレイアウト="@layout/content_main_activity" />を追加します。
<android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>を参照してください。
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout
アプリをBack4Appに接続するには、クラスのonCreate()関数内に以下のコードを追加してください。
// Parseサーバーの初期化 Parse.initialize(新しい Parse.Configuration.Builder(this)) .applicationId("YOUR_APP_ID")// コア設定の "Features" から .clientKey("YOUR_CLIENT_KEY")//コア設定より "機能" .server("https://parseapi.back4app.com/") .build() );
アプリにインターネット・アクセスを許可するには、QuickStartのステップバイステップの指示に従うことを忘れないでください。
セクション2:Parseでのオブジェクトの保存と取得、およびアプリでの表示
まず、以下のインポートを追加します。
importcom.parse.FindCallback; インポート com.parse.ParseCallback import com.parse.ParseException; import com.parse.ParseObject; import com.parse.ParseQuery; import com.parse.SaveCallback; import org.json.JSONException; import org.json.JSONObject; インポート java.util.List;
ここでは、”CommandGPIO1 “クラスのオブジェクトをボタンクリックで保存します。すべてのParse Objectには、objectId、 createdAt 、updatedAtの3つのデフォルト属性があります。このクラスにはさらにcontentと destinationという2つの属性が追加されます。contentの値は “on “か “off “のどちらかで、LEDに送信するステータスを表します。destinationには、すべてのオブジェクトが “command “を含むが、LEDごとに異なる文字列を定義することもできる。
例えば
つのボタンを作成し、それぞれに異なる値を書き込む。ボタンを作成するには、レイアウトXMLファイルに以下のコードを追加します:
<ボタン android:id="@+id/buttonSendOn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|start" android:layout_marginBottom="0dp" android:layout_marginTop="120dp" android:layout_marginEnd="0dp" android:layout_marginRight="0dp" android:layout_marginLeft="16dp" android:layout_marginStart="16dp" android:text="Send ON"/>
クラス・ファイルでは、ボタンの作成は次のように書くだけでよい:
Button buttonOn = (Button) findViewById(R.id.buttonSendOn);
Androidプログラミング初心者の方は、”findViewById “引数に “buttonSendOn “が含まれていることに注意してください。
このボタンをクリックすると、Parse Serverにオブジェクトが保存されるようにします。そのためには、以下のコードを追加します:
buttonOn.setOnClickListener(new OnClickListener(){) @Override public void onClick(final View view) { // 新しいオブジェクトを作成し、代入する。
// 新しいオブジェクトを作成し、適切な属性を割り当てます。 ParseObject command =new ParseObject("CommandGPIO1");
command.put("content","on"); command.put("destination","command"); command.saveInBackground(新しい SaveCallback(){) オーバーライド パブリック void done(ParseException e){ Snackbar.make(view,"Sent ON to Output", Snackbar.LENGTH_LONG ) .setAction("Action",null).show(); } }); });
なお、onClickコールバック関数の中に、実行したい関数を追加することができます。ここでは、”CommandGPIO1 “クラスのオブジェクトを作成し、contentを“on”、destinationを“command “に設定している。
このクラスは、Parseダッシュボード上で事前に定義する必要はありません!後で新しい属性を追加したり、クラス名を変更したりする場合は、コードを修正するだけで、ダッシュボード上で変更が自動的に更新されます。
この時点で、アプリをテストし、オブジェクトが作成されていることを確認しましょう!
SaveCallbackはスナックバーを表示します。スナックバーは、上図のように画面の下部に表示される軽量なフィードバックです。
このコードをコピーして、オブジェクトを “off “で書き込むボタンを作成してください。レイアウトXMLの以下の行を変更してください
android:id="@+id/buttonSendOff" android:layout_gravity="top|center" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" android:text="Send OFF"/>
新しいボタンを作成し、内容が書き込まれる行を変更する:
Button buttonOff = (Button) findViewById(R.id.buttonSendOff);
command.put("content","off");
Parse Server上で正常に動作するようになったので、アプリのユーザーに恒久的なフィードバックを提供したいと思います。そのために、以下のようにEditText要素を使用します。
この要素をレイアウトXMLに定義します:
<EditText android:id="@+id/status1Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|start" android:maxLines ="2" android:ems="10" android:singleLine="false" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
クラスのonCreate()スコープ内に、以下のコードを追加する:
// アプリからコマンドを受信する出力ピンのステータスを表示するためのテキスト要素を定義します。 // コマンドは、このコードで後述するボタンによって送信されます。 final EditText textOutPin = (EditText) findViewById(R.id.status1Text); textOutPin.setFocusable(false); textOutPin.setClickable(true); textOutPin.setText("出力ピンのステータスをロード中...");
最初の行でオブジェクトを作成しています。2行目は、ユーザーが編集できないようにしています。3行目は、コピー&ペーストができるようにクリック可能にしています。4行目は初期テキストを設定している。
Parseで最後に保存した「CommandGPIO1」オブジェクトの内容フィールドをこのEditTextオブジェクトに書き込みたい。コード内のロジックを使ってこれを行うこともできますが、ここでは実際にParseQueryを実行してParseからオブジェクトを取得することにします。
以下のコードでは、ParseQuery の宣言、パラメータの設定、結果の表示を行っています。
ParseQuery<ParseObject> queryOut = ParseQuery.getQuery("CommandGPIO1"); queryOut.whereEqualTo("destination","command"); queryOut.addDescendingOrder("createdAt"); queryOut.setLimit(1);
queryOut.findInBackground(new FindCallback<ParseObject>() {) オーバーライド public void done(List<ParseObject> objects, ParseException e) { if (e == null){. if (e ==null){ textOutPin.setText("Output is " + objects.get(0).getString("content")); } else{ textOutPin.setText("エラーです:" + e.getMessage()); } } });
最初の行はクエリーを作成します。2行目は、宛先フィールドに “command “が含まれるオブジェクトのみを選択する制約を追加しています。3行目は、新しいものからオブジェクトを取り出す。4行目は、結果を1つに制限し、最新のオブジェクトを取得することを保証しています。
5行目はクエリーを実行し、終了したらコールバック関数を呼び出す。ここでは、取得したオブジェクトの内容を、先に定義したEditTextオブジェクトに書き込んでいます。
EditTextオブジェクトを宣言した直後にこのコードを追加することで、アプリを開いたときにクエリが実行され、Parseで新しいオブジェクトを保存するときのSaveCallbackでも、新しいオブジェクトを作成するときに自動的にテキストが更新されるようにします。
この時点で、アプリは以下の画面キャプチャのように動作するはずです。
最後に、ユーザーがいつでも上記のクエリを実行できるように、更新ボタンを追加します。これは別のスタイルのボタン、つまりフローティング・アクション・ボタンで行います。
以下のコードをレイアウトXMLファイルに追加してください:
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|end" android:layout_marginBottom="0dp" android:layout_marginTop="120dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" app:srcCompat="@android:drawable/ic_popup_sync" />
ここで、クラスのonCreate()スコープ内に、以下のコードを追加する:
// 要求されたときに出力状態を取得するための更新ボタン。 // 最初のクエリと同じクエリがここで実行されます。 FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(新しい View.OnClickListener(){) @Override public void onClick(final View view){ @override ParseQuery<ParseObject> queryOut = ParseQuery.getQuery("CommandGPIO1"); queryOut.whereEqualTo("destination","command"); queryOut.addDescendingOrder("createdAt"); queryOut.setLimit(1); queryOut.findInBackground(new FindCallback<ParseObject>() {) オーバーライド public void done(List<ParseObject> objects, ParseException e) { if (e == null){. if (e ==null){ textOutPin.setText("Output is " + objects.get(0).getString("content")); } else{ textOutPin.setText("エラーです:" + e.getMessage()); } Snackbar.make(view,"Updated status of Output", Snackbar.LENGTH_LONG ).setAction("Action",null).show(); } }); } });
この時点で、オブジェクトをParse Serverに保存し、そこから情報を取得してアプリに表示することができます!アプリは下図のように動作しているはずです:
セクション 3: ライブクエリを使用したリアルタイムイベントのリスニングとアプリへの表示
このセクションでは、Parse Dashboardの “InputGPIO “クラスをリアルタイムで監視し、オブジェクトの “内容 “をユーザーに表示します。
まず、レイアウトXMLで新しいEditTextを定義します:
<EditText android:id="@+id/status2Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center|start" android:layout_marginBottom="0dp" android:maxLines ="2" android:ems="10" android:singleLine="false" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
onCreate()のスコープで、新しいEditTextオブジェクトを作成し、その直後に初期化クエリー(まだライブではない)を実行する。
final EditText textInPin = (EditText) findViewById(R.id.status2Text); textInPin.setFocusable(false); textInPin.setClickable(true); textInPin.setText("Loading status of input pin...");
// 最後に保存されたピンのステータスを取得するための初期(非ライブ)クエリ ParseQuery<ParseObject> queryIn = ParseQuery.getQuery("InputGPIO"); queryIn.whereEqualTo("type","interrupt"); queryIn.addDescendingOrder("createdAt"); queryIn.setLimit(1);
queryIn.findInBackground(new FindCallback<ParseObject>() {) オーバーライド public void done(List<ParseObject> objects, ParseException e) { もし(e == null)なら if (e ==null){ textInPin.setText("Input is " + objects.get(0).getString("content")); } else{ textInPin.setText("エラーです:" + e.getMessage()); } } });
InputGPIO “の属性はcontentと typeであり、後者は “CommandGPIO “クラスの宛先の役割を果たすことに注意してください。
この時点で、あなたのアプリは下図のようになっているはずです。
次に、Live Queryを実装します。ここでは、先ほど紹介したチュートリアルが必要です。
https://docs.back4app.com/docs/android/live-query/
Live Queryチュートリアルのステップ1に従い、どのクラスでLive Queryを有効にするかを選択し、サブドメイン名を定義してください。InputGPIO “クラスを作成し(まだ存在しない場合)、この機能を有効にする必要があるかもしれません。チュートリアルのステップ2に従い、Android上でLive Queryクライアントをセットアップします。
以下のインポートを忘れずに追加してください:
インポートtgio.parselivequery.BaseQuery; import tgio.parselivequery.LiveQueryClient; インポート tgio.parselivequery.LiveQueryEvent; import tgio.parselivequery.Subscription; import tgio.parselivequery.interfaces.OnListener.LiveQueryクライアント
以下のコードを追加して、LiveQueryを初期化し、パラメータを定義します。
// LiveQueryの初期化 LiveQueryClient.init("wss:YOUR_SUBDOMAIN_NAME.back4app.io","YOUR_APP_ID",true ); LiveQueryClient.connect();
// LiveQueryの属性を定義する Subscription subIn =new BaseQuery.Builder("InputGPIO") .where("type","interrupt") .addField("content") .build() .subscribe();
サブスクリプションを定義する際に、type属性が “interrupt “である要素のみを取り出し、フィールドの内容を取得します。
次に、サブスクリプションで定義されたオブジェクトがParse Serverで生成されるたびに応答するために、以下のコードを追加します。
// LiveQueryのCREATEイベントをリッスンし始め、その内容を取得し、書く。 subIn.on(LiveQueryEvent.CREATE,new OnListener() { // LiveQuery CREATEイベントをリッスンし始め、その内容を取得し、書き込む。 @Override public void on(JSONObject object) { 以下のようにします。 try { final String subInContent = (String) ((JSONObject) object.get("object")).get("content"); runOnUiThread(new Runnable() {) @Override public void run() { {. textInPin.setText("Input is " + subInContent); Snackbar.make(findViewById(R.id.myCoordinatorLayout),"Input pin was changed to " + subInContent.toUpperCase(), Snackbar.LENGTH_LONG ).setAction("Action",null).show(); } }); }catch (JSONException e){ e.printStackTrace() e.printStackTrace(); } } });
オブジェクトのコンテンツ・フィールドは、新しいEditText要素に表示されます。
ここまでで、IoTデバイスから送信された入力を表示するには十分です。InputGPIO」クラスのオブジェクトを作成し、Parse Serverに保存します。
以下のコードをレイアウトXMLファイルに追加します:
<ToggleButton android:id="@+id/toggleTest" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center|end" android:layout_marginBottom="0dp" android:layout_marginTop="100dp" android:layout_marginEnd="0dp" android:layout_marginRight="0dp" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" android:textOn = "入力オン" android:textOff = "入力オフ"/>。
MainActivityクラスのonCreate()関数のスコープに、以下のコードを追加する。
// トグル・ボタンは、ハードウェアが作成するオブジェクトをエミュレートするためにここにあります。 ToggleButton toggleTest = (ToggleButton) findViewById(R.id.toggleTest); toggleTest.setOnClickListener(new OnClickListener(){) @Override public void onClick(final View ビュー) { 次のようにします。 ParseObject command =new ParseObject("InputGPIO"); if(gpioStatusTest.equals("off")) gpioStatusTest ="on"; else gpioStatusTest ="off";
command.put("type","interrupt"); command.put("content","From Toggle: " +gpioStatusTest); command.saveInBackground(新しい SaveCallback(){) オーバーライド パブリック void done(ParseException e){ Snackbar.make(view,"入力ピンを変更しました", Snackbar.LENGTH_LONG ).setAction("Action",null).show(); } }); } });
また、MainActivityスコープでStringを宣言します。
StringgpioStatusTest ="off";
これでアプリは完成です!このチュートリアルの最後には、あなたのアプリは下図のようになるはずです。
IoTアプリケーションの開発を続けたい方は、Raspberry Piのセットアップや、JavaScriptを使ってParse Serverからオブジェクトを取得・保存するなどの具体的なアプリケーションの基礎を学ぶ、ステップ・バイ・ステップのガイドであるIoTシリーズをお読みください。
私たちのコードは以下のリンクから入手できます:
https://github.com/back4app/iot-raspberry-node
参考文献
Raspberry Pi と Parse Server の接続
Android アプリを IoT デバイスと連携させるにはどうすればよいでしょうか?
セクション1:アプリの作成とサーバーサイドとの接続の基本
セクション2:Parse にオブジェクトを保存・取得し、アプリに表示する方法
セクション3:Live Query を使ったリアルタイムイベントの監視とアプリへの表示